Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Immutable variables in OCaml

Tags:

ocaml

I'm learning OCaml, and I'm a bit confused with the immutability of variables. According to the book I'm reading, variables are immutable. So far so good, but why on Earth can I do this:

let foo = 42
let foo = 4242

What am I missing??

like image 953
cfischer Avatar asked Dec 06 '22 18:12

cfischer


2 Answers

I think the best way to explain is with an example. Consider this code (executed in the OCaml REPL):

# let foo = 42;;
val foo : int = 42

# let return_foo () = foo;;
val return_foo : unit -> int = <fun>

# let foo = 24;;
val foo : int = 24

# return_foo ();;
- : int = 42

The above code does the following:

  1. Binds 42 to the name foo.
  2. Creates a function return_foo () that returns the value bound to foo.
  3. Binds 24 to the name foo (which hides the previous binding of foo).
  4. Calls the return_foo () function, which returns 42.

Compare this with the behaviour of a mutable value (created using ref in OCaml):

# let foo = ref 42;;
val foo : int ref = {contents = 42}

# let return_foo () = !foo;;
val return_foo : unit -> int = <fun>

# foo := 24;;
- : unit = ()

# return_foo ();;
- : int = 24

which:

  1. Creates a mutable reference containing 42 and binds it to the name foo.
  2. Creates a function return_foo () that returns the value stored in the reference bound to foo.
  3. Stores 24 in the reference bound to foo.
  4. Calls the return_foo () function, which returns 24.
like image 71
Leo White Avatar answered Dec 26 '22 11:12

Leo White


The name foo is first bound to an immutable value 42 and later it is rebound to another immutable value 4242. You can even bind the same name to variables of different types. In OCaml we are talking not about mutability of a variable, but about a mutability of a value. For example, if you bind foo to an array of values, this would be the same name, but bound to a mutable data, so that the value of a variable can change in time. Finally, each new binding just hides the previous one, so the original foo is still bound to 42, but it is invisible and will garbage collected.

Maybe a little example will clarify the idea:

let example () = 
  let foo = 42     in (* 1 *)
  let foo = 4242   in (* 2 *)
  let foo = [|42|] in (* 3 *)
  foo.(0) <- 56       (* 4 *)

It might be easier to have the following mental model:

                  (*1*)  +--------+
                  +----> |   42   |
+------------+    |      +--------+
|            +----+
|    foo     +----+      +--------+
|            |    +----> |  4242  |
+---------+--+    (*2*)  +--------+
          |
          |       (*3*)  +--------+
          +------------> |[| 42 |]|
                  (*4*)  +--------+

On lines 1 and 2 we just bind a variable foo to two different values. On line 3 we bind it to an array that contains one element. On line 4, we change the value, and foo is still bound to the same value, but the value contains different datum.

I hope I didn't confuse you even more ;)

like image 37
ivg Avatar answered Dec 26 '22 11:12

ivg