Dr Xorile
Dr Xorile

Reputation: 1009

let statement inside an ocaml for loop

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

Answers (2)

Shon
Shon

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

ivg
ivg

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

Related Questions