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?
Function in ocaml is a expression. That means, a function is a value that represents the function. This is also called anonymous function, lambda. (* syntax for a function expression *) fun n -> n + 1;; (* applying a function to a value *) (fun n -> n + 1) 2;; (* ⇒ 3 *)
The way -> is defined, a function always takes one argument and returns only one element. A function with multiple parameters can be translated into a sequence of unary functions.
The tilde introduces the feature known as labelled argument. OCaml standard library has module List where functions are declared without labelled arguments and module ListLabels which uses them.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With