1. Function calls
Calls to functions are written differently, without any brackets or commas.
In C you would write:
printf ("hello %s\n", name);
but in OCaml the same function call would be written like this:
printf "hello %s\n" name;
What's the difference? In functional languages you don't put brackets around the arguments, and you just put spaces between the function name and the arguments.
Why is it confusing? It doesn't look like a function call (unless you're used to this style). Functions are very common in functional programming, not surprising really, and they are often given short names, so you'll see plenty of code like this:
f (g a b) c
Just take it one step at a time and remember that
g a bis a function call (in C it would be written as
g (a, b)), and that
f (...) cis a function call with two parameters (in C it would be written as
f (g (a, b), c).)
Why is it done like this? This syntax is better because it's shorter. Functions and function calls are very common in functional programming languages, so we need to use the shortest possible syntax, which is this one.
2. Bindings are not variables
let x = foo
xis not a variable. It's just a name which refers to
foo, and there is no way to change its value. The technical term is a let-binding.
You can create another name
xwith a different value, but that doesn't change the original
let x = foo in
let x = bar in
One consequence of this is that the following code doesn't do what you think it does:
let quit = false in
while not quit do
let line = read_line () in
if line = "q" then let quit = true in ();
In fact this loop never exits. Why? Because the inner
quitis just a different label from the outer one. In this case you would get a compiler warning because the inner
quitlabel is never used.
What's the difference? Let bindings make labels, not variables.
Why is it confusing? Variables do exist in some functional languages, particularly ones based on ML like OCaml, but they aren't used very much. Most code you look at will use only let bindings, and you shouldn't confuse those with variables.
Why is it done like this? This encourages the use of immutable data structures, which is a giant topic in itself. In brief, immutable data structures make programming errors less likely because they remove the "who owns that data" problem that imperative languages have. (It's fair to say that immutable data structures also have disadvantages, which is why OCaml lets you drop down to mutable data when you need it).
3. Function types use lots of '->' (arrows)
To write the type of a function, you use an arrow notation. The parameters and the return type are separated by arrows like this:
val average : float -> float -> float
This means that there is a function called
averagewhich takes two parameters, both floating point numbers, and returns a floating pointer number.
Here are some more examples:
val print_string : string -> unit
(Takes one parameter, a string, and returns nothing --
val int_of_string : string -> int
(Takes one parameter, a string, and returns an int).
val open_out_gen : open_flag list -> int -> string -> out_channel
(Takes three parameters: a list of flags, an integer and a string. Returns an output channel).
What's the difference? This syntax is common in functional programming, and almost completely unknown outside of it.
Why is it confusing? The parameters and the return type aren't separated from each other.
Why is it done like this? The reason is to do with a mathematical concept called currying. The practical reason is that functional languages let you generate new functions by partially applying some arguments. Thus:
add : int -> int -> int
(add 42) : int -> int
(add 42 2) : int
The first is the general adding function. The second is a partially applied function which adds 42 to any int. The third is the number 44.