This is part of the issue. In ocaml there is no distinction between statements and expressions. Statements are expressions in whose value you are not interested. This and the associated syntax can be an initial speed bump for those who come from a brace-structured language (C, C#, Java and so forth) or an indentation/whitespace structured language (Python).
The structuring of those languages is well suited to code which may perform side effects. However ocaml expression syntax relies much more on operators and operator precedence. The results are intuitive most of the time in relation to the composition of expressions (ocaml is a functional language after all) but less so when functions are evaluated for their side effects: that is, where the value to which a function evaluates is ignored or that value is unit. Take this:
let x = "Hello world"
print_endline x
At first sight it looks OK it but it won’t compile. Furthermore, before you even get to compiling it, a decent editor is going to try to format it as something like:
let x = "Hello world"
print_endline x
The proximate cause is that, because of the precedence of function application, this is in fact:
let x = ("Hello world" print_endline x)
namely function application of a string which is clearly impossible.
;
is for sequencing side effects, so you might try:
let x = "Hello world" ;
print_endline x
but that won’t work because “Hello world”, and so ‘x’, doesn’t evaluate to unit.
Instead let
is the operator to express the intended meaning.
let x = "Hello world"
let _ = print_endline x
Or if ‘x’ is not intended to be a top level variable:
let x = "Hello world" in
print_endline x
The ;
operator used with side-effectual functions can have other gotchas until you get used to it. For example this might look OK:
if true then
do_this () ;
do_that ()
else
do_the_other () ;
but it won’t compile. This is because the code is in fact this:
(if true then do_this () ) ;
(do_that () else do_the_other () )
meaning that because of the sequencing operator the conditional expression ends with ‘do_this ()’ and ‘else’ is left dangling. What the programmer really meant was:
if true then (do_this () ; do_that () )
else do_the_other () ;
Furthermore, this will be fine because let has even lower precedence than the semi-colon operator:
if true then
let x = 5 in
do_this x ;
do_that ()
else
do_the_other () ;
yet_more_stateful_computation ()
But this will never execute yet_more_stateful_computation, because despite appearances its application is part of the let
expression within the else
block:
if true then
do_this ()
else
let x = 5 in
do_that x ;
yet_more_stateful_computation ()
What the programmer really meant was:
(if true then
do_this ()
else
let x = 5 in
do_that x) ;
yet_more_stateful_computation ()
In short, particularly when dealing with side effects, as others have said there is no substitute for learning the rules on operator precedence and associativity. In the end it will become second nature.
On ;;
, as others have also said, don’t use it except at the REPL