Login

Surprising syntax

or: non-obvious basic syntax

Files are module definitions

If you go to Expressions in the OCaml manual, you'll find three syntaxes for let that all have mandatory in expr as part of the syntax. (let-binding is a BNF breakdown from the first let syntax, not an additional syntax).

Thus, in the expression sublanguage of OCaml, let always comes in the form of let ... in expr. That's what a let expression is. You're not saying that a value has a name for no reason - you are naming it and immediately saying more about it.

But if you've seen any OCaml, you've also seen another kind of let:

let greet name = "Hello, " ^ name

That's from a different section of the manual, Module expressions. Whereas let x = 1 in x+x evaluates to 2, the previous code is binding greet in an enclosing module expression that evaluates to the module containing greet. Like so:

# module M = struct let greet name = "Hello, " ^ name end;;
module M : sig val greet : string -> string end
# M.greet "Alice";;
- : string = "Hello, Alice"

Or starting at a bash prompt,

$ echo 'let greet name = "Hello, " ^ name' > newfile.ml
$ ocamlc newfile.ml
$ utop
# #load "./newfile.cmo";;
# Newfile.greet "Alice";;
- : string = "Hello, Alice"

I created a file, compiled it, loaded it, and that bare greet was defined in a new module named after the file.

You can mix expressions with module expressions, somewhat awkwardly:

# module M = struct print_endline "starting module";;let x = 1;; print_endline "ending module";; end;;
starting module
ending module
module M : sig val x : int end
# 

Module expressions being different isn't weird

In C, what does int a[500]; mean? That depends on where it appears. If it appears at the top level of a file, it reserves some space in static memory in the .bss segment(?) that's loaded and zeroed when the program starts, and not freed until the program ends. If it appears within a function, it reserves some space on the stack which is valid only during execution of the function. If it appears in a function's arguments, it actually means int *a and you can forget about the 500!

In C, but also Erlang and other languages, you can't have executable code outside of functions. There, you have other code:

-module(kal).
-export([main/1]).
-include_lib("eunit/include/eunit.hrl").

-define(GOAL_WEIGHT, 150).
-define(GOAL_DEFICIT_HIGH, 700).
-define(GOAL_DEFICIT_LOW, 400).

-record(eat, {
    kcal :: non_neg_integer(),
    fat :: non_neg_integer(),
    carb :: non_neg_integer(),
    protein :: non_neg_integer(),
...

None of that's an Erlang expression with a value. Erlang has an expression sublanguage but also this module definition sublanguage.

OCaml's the same way, but OCaml puns its sublanguages a little more.