Reputation: 1009
I am a ocaml noob so apologies for the stupid question. My google skills have failed me!
I am trying to do a series of let bindings and want to do them in a for loop (I know, I know). When I put a let binding in the for loop the "done;;" becomes a syntax error. I assume I'm missing something obvious. Any other pointers for the following MWE are welcome:
module PF = Map.Make(struct type t = int let compare = compare end);;
module Factorize = Map.Make(struct type t = int let compare = compare end);;
let a = PF.empty;;
let a = PF.add 2 5 a;;
let a = PF.add 3 3 a;;
let b = PF.empty;;
let b = PF.add 3 1 b;;
let b = PF.add 5 2 b;;
let print_map = PF.iter (fun a b -> Printf.printf "%d %d\n" a b);;
print_map a;;
print_map b;;
(*THESE LET BINDINGS WORK*)
let primeFactors = Factorize.empty;;
let primeFactors = Factorize.add 1 (PF.add 1 3 PF.empty) primeFactors;;
let primeFactors = Factorize.add 2 (PF.add 2 3 PF.empty) primeFactors;;
let primeFactors = Factorize.add 3 (PF.add 3 3 PF.empty) primeFactors;;
(*BUT WE CANNOT DO A BUNCH OF THEM IN A LOOP*)
for k = 4 to 20 do
let primeFactors = Factorize.add k (PF.add k 3 PF.empty) primeFactors;
done;; (* THIS LINE FAILS*)
It's not strictly a MWE, but the idea is that there are two mappings. The first maps int to int and the second maps int to a PF mapping.
The let binding itself seems to work. But somehow having a let binding causes the done to fail. My guess is that it is supposed to be returning a (), but I couldn't figure out how to make it do that.
Upvotes: 0
Views: 1008
Reputation: 4098
This can be a bit confusing at first. The let
keyword functions differently at the top level then it does in the context of an expression.
The first group of let
bindings are at the top-level of your module. The syntax for top level bindings is let x = expr
. This is what you have in the let bindings preceding the for
loop (the trailing ;;
aren't needed unless you're working in the interactive top-level, but they won't hurt anything).
However, in any other context (e.g., in your for
loop), let
bindings bind a variable in
a local scope. The syntax for this is let x = expr1 in expr2
-- note the in
. The in
separates the binding of the variable (here x
to the value expr1
) from the following scope in which this binding holds (here expr2
).
E.g.,
for k = 4 to 20 do
let v = k * k in
print_endline (string_of_int v)
done
Note that, when doing imperative programming with ref
cells, you don't need let bindings to update the values of the ref
cell:
let v = ref 0 in
for k = 4 to 20 do
v := !v + k
done;
print_endline (string_of_int !v);
(* => 204 *)
Please let me know if this seems unclear or you have any followup questions!
Upvotes: 3
Reputation: 35210
TL;DR; let
is not an assignment :=
. Let creates a lexical binding, it doesn't change any values.
First of all, you're confusing assignment with shadowing.
When you do
let x = 42
let x = 52
You're not changing the value of x
, you bind the variable x
to the new value 52
. In OCaml, variables are purely mathematical, in a sense that they do not act as a storage cell and do not store anything inside themselves. They are just names, which are used by a programmer to refer to a result of an expression.
Moreover, you can even bind values of different types to the same identifier, e.g.,
let x = 42
let x = "hello"
Now, to create a reference cell, which as an object where you can store a value, you can use the ref
function. It will return a box with a place ready to store
your values.
let x = ref 42
and to change the value stored in this cell you have to use the (:=)
function, e.g.,
x := 56
Now, the value stored in the cell which we reference by the name x
is 56
, and to retrieve this value, you have to use the (!)
function. For example, the following will double the value stored in the cell:
x := !x * !x
And you can do this in a cycle,
for i = 0 to 9 do
x := !x * !x
done
Now, let's look on what did you do :)
let primeFactors = ref Factorize.empty;;
You create a cell that contains an empty map and bound it to the primeFactors
name. The value bound to the primeFactors
variable has type Factorize.t ref
, i.e., a reference to Factorize.t
. So far so good!
let primeFactors = Factorize.add 1 (PF.add 1 3 PF.empty) primeFactors;;
First of all, it won't compile, since Factorize.add
expects Factorize.t
but you're giving it a value of type Factorize.t ref
. Though, it is syntactically correct, it is not a valid program. However, even if we will make it well-typed, by dereferencing the cell to its value via the (!)
function
let primeFactors = Factorize.add 1 (PF.add 1 3 PF.empty) !primeFactors
It will be still a wrong program (not all programs that are well-typed are correct!).
What we did here, we rebound the primeFactors
variable to a new value which is a map from 1
to (PF.add 1 3 PF.empty)
value. So we changed the value associated with the primeFactors
variable, as well as its type. What we should do, is
primeFactors := Factorize.add 1 (PF.add 1 3 PF.empty) !primeFactors
Upvotes: 2