let () = Random.self_init();;
let _ = Random.self_init ();;
│- : unit = ()
It seems "let ()" returns nothing ?
Sincerely!
let is block-scoped. var is function scoped. let does not allow to redeclare variables. var allows to redeclare variables.
I understood the difference between let and let! with a very simple example. Let me read the doc sentence first, then show the output hands on. ... let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked.
let can be updated but not re-declared. This is because both instances are treated as different variables since they have different scopes. This fact makes let a better choice than var . When using let , you don't have to bother if you have used a name for a variable before as a variable exists only within its scope.
let
is the keyword used to define new variables, like in the following construct:
let pattern = expr
For instance
let a = 2
assigns the value 2
to the name a
. (Note this is not a way to assign a value to an already existing variable, but this is another topic).
But the pattern to the left of the =
sign can be more than just a name. For instance
let (a,b) = (42,"foo")
defines both a
and b
, to be respectively 42
and "foo"
.
Of course, the types on both sides must match.
Which is the case here: both sides are of type int * string
.
The expressions to the right of the =
sign can also be elaborated, for instance
let foo =
let temp = String.make 10 'a' in
temp.[2] <- 'b';
temp
defines foo
as the string "aabaaaaaaa"
. (As a side note, it also ensures that temp
is local to this code snippet).
Now, let's use both: on the left, a pattern matching values of type unit
, and on the right, an expression of type unit
:
let () = Printf.printf "Hello world!\n"
Which explains the let () =
construct.
Now, about the let _
, one simply needs to know that _
can be used in a pattern as a wildcard: it matches values of any type and does not bind any name. For instance
let (a,_) = (42,"foo")
defines a
as 42
, and discards the value "foo"
. _
means "I know there is something here and I explicitly say I will not use it, so I don't name it". Here _
was used to match values of type string
, but it can match value of any type, like int * string
:
let _ = (42,"foo")
which does not define any variable and is not very useful. Such constructs are useful when the right hand side has side effects, like this:
let _ = Printf.printf "Hello world!\n"
which explains the second part of the question.
Practical purposes
Both are used and it's rather a matter of taste whether to use one or the other.
let () =
is slightly safer as it has the compiler check that the right hand side is of type unit
.
A value of any other type than unit is often a bug.
let _ =
is slightly shorter (I've seen this argument). (Note that with an editor that automatically closes parenthesizes, the number of keystrokes is the same ;-)
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