10
votes

There are codes like

let () = print_string "something" in
fn

in some OCaml codes.

What does this mean? Is there special meaning on "()"? or Is it same meaning as

print_string "something";
fn
2
You'll probably want to use let _ = ... exclusively however as it will work for most if not all expressions. - Jeff Mercado
let _ = ..., or fragile pattern matching, is absolutely not recommended. It's pretty much equivalent to giving a middle finger to the type system. - nlucaroni
I agree with nlucaroni. if you want let _ = ... in ..., then you probably want ignore (...); ... - newacct

2 Answers

14
votes

There's nothing special about () in this let expression, it's just a pattern. All let expressions look like let pattern = expression in other-expression. Here the pattern will always match, because print_string returns unit, and () is the only value of that type. In this way, it's just another way of combining two expressions into one when the first one is really more of a statement (returns unit).

So you're right, the construct has pretty much the same meaning as using the ; operator. The only real difference is in the precedence. If, for example, you write

if x < 3 then
    print_string "something";
    f x

you would find that f x is always called. The precedence of ; is too low to pull the second expression under the control of the if. That's the reason many people (including me) get into the habit of using let () = expression. If you write the above as

if x < 3 then
    let () = print_string "something"
    in f x

the f x is only called when x is less than 3, which is usually what I want. In essence, the precedence of let is much higher than ;.

Of course there are may other ways to get this effect, but the nice thing about using let is that you don't have to add anything later on in the code (like a closing parenthesis or an end). If you're adding the print_string as a debugging statement, this is a handy way of keeping the changes local to the one spot.

3
votes

Jeffrey's answer is absolutely correct, but one more point:

if you write

fx "something";
fn

And you messed up the result type of fx "something" the compiler will emit a Warning, that might get lost during compilation. On the other hand if you write:

let () = fx "something" in
fn

The compiler will type check that the result of fx "something" can be matched against (), i.e. that it is really of type unit. Thus if you messed up an error is produced, which is usually more secure.

There also is the possibility to write

let _ = fx "something" in
fn

which will only get the precedence effect that Jeffrey mentioned, but not do any type checking, since _ can be matched against values of any type.