Error message could be improved here *Not Sure*

Hello OCaml community,

Pardon my query if this is something very obvious, I am new to both OCaml and functional programming.

I was experimenting with Modules and I got this error Syntax error for the code below at the ‘in’ definition

module MyList = struct
  type 'a mylist = 
  | Nil
  | Cons of 'a * 'a mylist

  let rec map f  = function
    | Nil -> Nil
    | Cons(h, t) -> Cons (f h, map f t)

  let rec fold_left (f:'a -> 'a -> 'a) acc = function 
  | Nil -> acc
  | Cons(h, t) -> fold_left f (f acc h) t
end

let a = MyList.map (fun x -> x + 1) (Cons(1, Cons(2, Nil))) in
let b = MyList.fold_left ( + ) 0 a in
Printf.printf "%d\n" b

and after some debugging, I could fix it as

module MyList = struct
  type 'a mylist = 
  | Nil
  | Cons of 'a * 'a mylist

  let rec map f  = function
    | Nil -> Nil
    | Cons(h, t) -> Cons (f h, map f t)

  let rec fold_left (f:'a -> 'a -> 'a) acc = function 
  | Nil -> acc
  | Cons(h, t) -> fold_left f (f acc h) t
end

let a = MyList.map (fun x -> x + 1) (Cons(1, Cons(2, Nil)));;
let b = MyList.fold_left ( + ) 0 a in
Printf.printf "%d\n" b

Why does the type deducer fail when I use nested ‘let in’ syntax? If this is expected behavior, wouldn’t it be more appropriate to provide a clearer error message instead of just Syntax error?

Let’s first reindent this block of code according to its meaning:

let a =
  MyList.map (fun x -> x + 1) (Cons(1, Cons(2, Nil))) in
  let b = MyList.fold_left ( + ) 0 a in
  Printf.printf "%d\n" b

which makes it clearer why this is a syntax error.

This is indeed a very misleading error, which best remediation comes from the very fact that it occurs so frequently that you will quickly learn to reckognize and fix it. :frowning:

The issue is that top level definitions use almost, but quite not exaclty, the same syntax as in-block definitions: let x = v vs let x = v in ...

Note: your solution was to add the REPL delimiter (the double semi-colon). Notice that, unless you actualy intend to copy and paste this code into the REPL, the double-semi-colon is useless and can just as well be ommited.

Note that a single expression (eg let ... in ...) is not allowed at the top-level of a module. Instead, only the form let ... = ... (without the in) is allowed, which defines a top-level binding valid for the rest of the file. If you want to execute an expression at the top-level of a module (eg for its side-effects, as in your example), you can reuse this same construct, using let () = ...:

let () =
  let a = ... in
  let b = ... in
  Printf.printf ...

If you want to learn more about the syntax allowed at top-level of a module, I recommend taking a look at OCaml - The OCaml language.

Sure! PRs welcome :slight_smile:

Cheers,
Nicolas

1 Like

Isn’t that the kind of error it took @let-def a PhD thesis to report cleanly ? :–)

3 Likes

In full generality, sure…I am not knowledgeable enough to assert whether something simpler could not be done in some simple cases, though, so I try not to discount the possibility outright :slight_smile:

Cheers,
Nicolas

2 Likes

Probably you mean something different?

# let x = 1 in print_int x;;
1- : unit = ()

You could also put the ;; after the end

Well, yes, and no. It is true that it is indeed possible to enter a top-level expression followed by a double semicolon. This usage, which is borrowed from the toplevel ocaml, is discouraged outside of the REPL nowadays and the use via let () = ... is preferred as a more regular alternative. Having said that, some old-timers still prefer it because it can sometimes result in better localisation of syntax errors. So as you can see, there’s something for everyone :slight_smile:

Cheers,
Nicolas

1 Like