let func acceptor word = acceptor word
let func acceptor = acceptor
Why is it possible to simplify the first line to the second line? Also, how would you call the second version…with 1 or 2 arguments?
let func acceptor word = acceptor word
let func acceptor = acceptor
Why is it possible to simplify the first line to the second line? Also, how would you call the second version…with 1 or 2 arguments?
\x.\y. (x y)
can be rewritten to \x.x
via eta-contraction, viz. the eta rule is
'M == \x.(M x)` when x does not appear free in M.
[Uh, been so long, hope I got that right.]
The first line:
let func acceptor word = acceptor word
The type of this function is (making assumptions about the types here to make it simpler):
val func : (int -> string) -> int -> string
The second line:
let func acceptor = acceptor
The type of this function is:
val func : (int -> string) -> int -> string
So the types are the same.
In the first line, the definition of func
takes the acceptor
and its parameter word
and applies them. In the second line func
takes acceptor
and returns it, but what does it return? It returns a function that takes another thing as input, so you have to apply that parameter to execute acceptor. You can tell both are equivalent since their types are the same.
Stylistically, there is a question of which of those you should prefer. For me, it depends on the situation. Sometimes I think the first case is clearer and sometimes I think the latter is clearer.
Not mentioning the name of arguments is called point-free style, and is regularly used in Haskell. The main reason for this is that this language has a typeclass-based type system allowing to handle values through abstract concepts (e.g. functors, monads) very frequently, and defines a lot of combinators and operators that can be used to combine functions with those concepts. One handles functions all the time and applies them to values sometimes. The whole Haskell ecosystem is built on top of this way of thinking.
All these tools could be implemented in OCaml, but it is less idiomatic. Instead, OCaml is based on a module system where things are more concrete in their expression, and giving a name to all the arguments of a function is more common. We even have a feature for named parameters. Having used both languages, I would say that OCaml is simpler thanks to this way of thinking.
The rule of thumb I use is the following: if the binding you are writing is meant to return a non-function value, you should name all the parameters for code clarity; if its essential meaning is to build functions, then removing the name of the last parameter can make it clearer.
Let me give an example: say you have an array, and you want a function making a getter to the nth element of the array. How would you write it?
let make_getter arr n () = arr.(n)
let make_getter arr n = fun () -> arr.(n)
Even though the usual OCaml style is the first version, I personally find the second one more readable, because the name make_getter
makes it clear we return a function. If the same binding were called array_get
, then the first style would be ok.
Please note that this is only my opinion, you can take it or leave it, and I completely understand that people might not agree with this way of seeing things.
Cheers
As simply as possible, without any “make” “'get” wording, just evocating the notion of “nth” on an array:
# let nth a n = a.(n)
val nth : 'a array -> int -> 'a = <fun>
or:
# let nth a = ( fun x -> a.(n) )
val nth : 'a array -> int -> 'a = <fun>
or
let nth = ( fun n -> ( fun x -> n.(x) ) )
and even more functional with less parens:
let nth = fun n -> ( fun x -> n.(x) )
let nth = fun n -> fun x -> n.(x)
...
val nth : 'a array -> int -> 'a = <fun>
(* this way, that sticks to the signature *)