7

It is typical in OCaml or F# to have successive let bindings in the form:

let a1 = ...
let a2 = ...
let a3 = ...
let f1 = ...
let f2 = ...
let f3 = ... 
f3 a1 a2 a3

In many cases some of these let bindings (e.g. f1 and f2 in the example above) are only used as building blocks of the expression or function immediately following them and not referenced again afterwards. In other cases some values are indeed used at the end of the "chain" (e.g. a1, a2 and a3 in the example above). Is there any syntactic idiom to make these differences in scope explicit?

Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331

2 Answers2

11

On can use this to make clear that temp is used only in the definition of a1:

let a1 =
  let temp = 42 in
  temp + 2 in
let a2 = ...

The scope of temp is indeed restricted to the definition of a1.

Another template is reusing the same name to hide its previous use, thus also making it clear that the previous use is temporary:

let result = input_string inchan in
let result = parse result in
let result = eval result in
result

Reusing the same name is debatable, though.

Of course one always has comments and empty lines:

let a1 = ...
let a2 = ...
let a3 = ...

(*We now define f3:*)
let f1 = ...
let f2 = ...
let f3 = ...

f3 a1 a2 a3

Edit: as pointed out by fmr, I'm also fond of the pipe operator. It's not defined by default in OCaml, use

let (|>) x f = f x;;

Then you can write something like

input_string inchan |> parse |> eval |> print
jrouquie
  • 4,315
  • 4
  • 27
  • 43
  • 2
    And, to expand on this slightly, if `temp` is used to compute multiple values, you can bind those values simultaneously as a tuple: `let a1, a2, a3 = let temp = ... in temp + 2, temp - 2, temp * 2` – Daniel Aug 08 '12 at 21:28
  • 2
    Also, depending on the complexity of the expressions on the RHS, you might consider the "piping" idiom (e |> e |> e |> e) – fmr Aug 09 '12 at 00:24
7

In addition to jrouquie's answer, you can avoid giving names to intermediate values by judicious use of function composition and other combinators. I especially like the following three provided by Batteries:

# let ( |> ) x f = f x;;
val ( |> ) : 'a -> ('a -> 'b) -> 'b = <fun>
# let ( |- ) f g x = g (f x);;
val ( |- ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun>
# let flip f x y = f y x;;
val flip : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c = <fun>

A small example using |> is

# [1;2;3]
  |> List.map string_of_int
  |> String.concat "; "
  |> Printf.sprintf "[%s]";;
- : string = "[1; 2; 3]"

You'll end up needing |- and flip in more realistic examples. This is known as point-free or tacit programming.

Community
  • 1
  • 1
Ashish Agarwal
  • 3,000
  • 16
  • 18