# Let \_ = \_ in \_ , ';', match, and begin ... end

**URL:** <https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768>\
**Category:** Learning\
**Created:** [March 24, 2023, 1:18am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768 "2023-03-24T01:18:17Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![zeroexcuses](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zeroexcuses/32/1692_2.png) [@zeroexcuses](https://discuss.ocaml.org/u/zeroexcuses)\
**Post date:** [March 24, 2023, 1:18am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/1 "2023-03-24T01:18:18Z")

</div>

There are a number of syntax binding rules that I don’t feel like I fully understand:

1. nested let \_ = \_ in \_ … clauses

2. nested match clauses

3. where I can / cannot sprinkle ‘;’

4. where I have to use `begin ... end` clauses

Is there a short # of rules I can memorize to understand these interactions ?

Right now, my rules are:

1. wait for compiler error

2. sprinkle let \_ = …

3. sprinkle begin … end

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [March 24, 2023, 3:10am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/2 "2023-03-24T03:10:19Z")

</div>

1. `let...in...` expressions have this syntax:

```auto
let PAT = EXP1 in EXP2

```

This whole syntactic form is considered an expression. So either of `EXP1` or `EXP2` can contain more `let...in...` expressions. So you can do

```auto
let
  x = 1
in
  let
    y = 2
  in
    x + y

```

I’m using indentation to show the structure, but since OCaml syntax is not whitespace-sensitive, we usually just write

```auto
let x = 1 in
let y = 2 in
x + y

```

1. Nested `match` expressions can suffer from the ‘dangling else’ problem, e.g.

```auto
match x with
| 1 ->
  match y with
  | 2 -> 0
  | _ -> 1
| _ -> 2

```

This complains about an unused case because the parser thinks it’s:

```auto
match x with
| 1 ->
  match y with
  | 2 -> 0
  | _ -> 1
  | _ -> 2

```

To avoid this we can parenthesize the nested expression:

```auto
match x with
| 1 ->
  (match y with
  | 2 -> 0
  | _ -> 1)
| _ -> 2

```

1. `;` can be used to separate any two expressions `EXP1` and `EXP2` where `EXP1 : unit`. E.g.

```auto
let () =
  print_int 1;
  print_int 2

(* Equivalent to *)

let () =
  let () = print_int 1 in
  print_int 2

```

1. `begin...end` has exactly the same meaning as `(...)`, you can use them interchangeably. E.g. from the previous nested `match` example,

```auto
match x with
| 1 ->
  begin match y with
  | 2 -> 0
  | _ -> 1
  end
| _ -> 2

```

Personally I prefer this to block off multiline expressions.

---

<div class="post-metadata">

**Author:** ![zeroexcuses](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zeroexcuses/32/1692_2.png) [@zeroexcuses](https://discuss.ocaml.org/u/zeroexcuses)\
**Post date:** [March 24, 2023, 3:25am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/3 "2023-03-24T03:25:43Z")

</div>

Interesting, is the following correct:

1. the only real compiler confusion is nested match

2. nested match wants to bind clauses to OUTER most match

3. we get around this issue by either () or begin end

4. we don’t actually need begin end? (i.e. we can always use () instead) ?

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [March 24, 2023, 4:02am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/4 "2023-03-24T04:02:14Z")

</div>

To be precise, it’s a parsing confusion, but yeah that’s pretty much it.

---

<div class="post-metadata">

**Author:** ![Khady](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/khady/32/469_2.png) [@Khady](https://discuss.ocaml.org/u/Khady)\
**Post date:** [March 24, 2023, 4:49am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/5 "2023-03-24T04:49:45Z")

</div>

Btw are you using ocamlformat? With the default profile it makes all (?) those cases explicit by adding parenthesis. So it’s easier to get what is “wrong”

---

<div class="post-metadata">

**Author:** ![hhugo](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/hhugo/32/1143_2.png) [@hhugo](https://discuss.ocaml.org/u/hhugo)\
**Post date:** [March 24, 2023, 7:56am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/6 "2023-03-24T07:56:02Z")

</div>

> [@zeroexcuses](#):
>
> nested match wants to bind clauses to OUTER most match

No, the example above is incorrect, the compiler reads it as

```auto
match x with
| 1 ->
  match y with
  | 2 -> 0
  | _ -> 1
  | _ -> 2

```

---

<div class="post-metadata">

**Author:** ![zeroexcuses](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zeroexcuses/32/1692_2.png) [@zeroexcuses](https://discuss.ocaml.org/u/zeroexcuses)\
**Post date:** [March 24, 2023, 8:08am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/7 "2023-03-24T08:08:05Z")

</div>

> [@Khady](#):
>
> Btw are you using ocamlformat?

This is life changing. I have no idea how I spent all this time without knowing about ocamlformat on save. For some reason I just assumed that the vim “autoindent” was the best there was.

If, in the future, you suspect I’m doing something stupid, please call me out on it. Happy to learn more.

---

<div class="post-metadata">

**Author:** ![zeroexcuses](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zeroexcuses/32/1692_2.png) [@zeroexcuses](https://discuss.ocaml.org/u/zeroexcuses)\
**Post date:** [March 24, 2023, 8:09am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/8 "2023-03-24T08:09:25Z")

</div>

Ah, so it defaults to innermost ? This does line up with past experience. So we need the begin/end () only to force it to NOT bind inner most.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [March 24, 2023, 8:09am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/9 "2023-03-24T08:09:54Z")

</div>

I corrected @yawaramin’s answer because I think it’s otherwise a very good answer.

---

<div class="post-metadata">

**Author:** ![threepwood](https://avatars.discourse-cdn.com/v4/letter/t/8dc957/32.png) [@threepwood](https://discuss.ocaml.org/u/threepwood)\
**Post date:** [March 24, 2023, 6:24pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/10 "2023-03-24T18:24:55Z")

</div>

I would say the only confusion _in functional code_ is nested match. But once you start using `;` there’s lots of potential errors (as in: shift-reduce conflicts where it’s not self-evident which way it goes). For instance, you need to wrap the pattern match in `begin ... end` or `let () = ... in` to make the following correct:

```ocaml
match x with
| Some x -> print_int x
| None -> print_string "No int!";
print_newline () (* this line is inside the None branch! *)

```

Similar issue with `if` clauses (solution: wrap the scope with `begin ... end`):

```ocaml
if b then
  print_string "(yes)";
  print_newline () (* this line executed unconditionally *)

```

Which is why the following does not compile:

```ocaml
if b then
  print_string "(yes)";
  print_newline ()
else ()

```

Then the weird thing is that adding a binding changes the behavior, so that this is wrong:

```ocaml
if b then
  let s = "(yes)" in
  print_string s;
print_newline () (* this line is inside the if *)

```

The solution is to wrap the whole `if` inside `begin ... end`, or `let () = ... in`, which means that to guard against both previous risks you need `begin ... end` both around the `if` expression and inside each branch.

Then of course we have the dangling else problem (tbh I’m not sure that one is a “confusion”, I find the behavior rather natural):

```ocaml
if b1 then
   if b2 then
    print_endline "b1 and b2"
else
  print_endline "neither b1 nor b2" (* this line acutally executes if b1 and not b2 *)

```

---

<div class="post-metadata">

**Author:** ![zeroexcuses](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zeroexcuses/32/1692_2.png) [@zeroexcuses](https://discuss.ocaml.org/u/zeroexcuses)\
**Post date:** [March 24, 2023, 7:59pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/11 "2023-03-24T19:59:51Z")

</div>

@threepwood : Thank you, I felt there was some ambiguity with `;`, but could not precisely state it. So in summary are the rules:

1. if preceded by `let .. in` or `match ... with`, `;` binds tightly

2. if preceded by `if .. then`, `;` binds loosely

Does that summarize the binding rules of `;` ?

---

<div class="post-metadata">

**Author:** ![lukstafi](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lukstafi/32/3442_2.png) [@lukstafi](https://discuss.ocaml.org/u/lukstafi)\
**Post date:** [March 24, 2023, 8:10pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/12 "2023-03-24T20:10:15Z")

</div>

The conclusion is correct, but I find the reasoning is misleading. Better way of looking is, `let ... in` and `match ... with` bind as far to the right as possible, while `if ... then` binds tighter than `;` but looser than any other operator: [OCaml library : Ocaml\_operators](https://v2.ocaml.org/api/Ocaml_operators.html).

---

<div class="post-metadata">

**Author:** ![lukstafi](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lukstafi/32/3442_2.png) [@lukstafi](https://discuss.ocaml.org/u/lukstafi)\
**Post date:** [March 24, 2023, 8:14pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/13 "2023-03-24T20:14:29Z")

</div>

Also, a trailing `;` is optional but allowed when it doesn’t upset the logic of what you want to express. E.g. `(print_char 'a'; print_char 'b';)` is fine.

---

<div class="post-metadata">

**Author:** ![zeroexcuses](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zeroexcuses/32/1692_2.png) [@zeroexcuses](https://discuss.ocaml.org/u/zeroexcuses)\
**Post date:** [March 24, 2023, 8:30pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/14 "2023-03-24T20:30:11Z")

</div>

> [@lukstafi](#):
>
> The conclusion is correct, but I find the reasoning is misleading. Better way of looking is, `let ... in` and `match ... with` bind as far to the right as possible

Ah, I see. let/in and match/with are “greedy” when consuming `;`, whereas if/then is not. Yeah, that is probably a “more useful” “mental parsing” rule.

---

<div class="post-metadata">

**Author:** ![threepwood](https://avatars.discourse-cdn.com/v4/letter/t/8dc957/32.png) [@threepwood](https://discuss.ocaml.org/u/threepwood)\
**Post date:** [March 25, 2023, 10:49am UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/15 "2023-03-25T10:49:03Z")

</div>

I’m not sure if this always been in the manual but in the current version the [table of precedences](https://v2.ocaml.org/manual/expr.html#ss%3Aprecedence-and-associativity) actually has all this information. Binding constructs (let, match, fun etc.) are the loosest binders (which is equivalent to saying they eat up everything if you think in terms of a recursive descent parser), then semicolons, then `if then else`, then everything else.

---

<div class="post-metadata">

**Author:** ![igstan](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/igstan/32/183_2.png) [@igstan](https://discuss.ocaml.org/u/igstan)\
**Post date:** [April 1, 2023, 4:19pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/16 "2023-04-01T16:19:58Z")

</div>

I’m curious, has it ever been any discussion of allowing the use of `;` (semicolon) instead of the `in` keyword in `let ... in` expressions? I’m sure people would have very strong feelings about this, but it seems possible in theory, right?

---

<div class="post-metadata">

**Author:** ![octachron](https://avatars.discourse-cdn.com/v4/letter/o/49beb7/32.png) [@octachron](https://discuss.ocaml.org/u/octachron)\
**Post date:** [April 1, 2023, 5:11pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/17 "2023-04-01T17:11:58Z")

</div>

It is not possible in theory; that would make the grammar ambiguous.

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [April 2, 2023, 4:40pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/18 "2023-04-02T16:40:23Z")

</div>

That was done in ReasonML, which introduced a C-like syntax for OCaml, complete with statement-terminator semicolons. It was pretty controversial when it originally came out, and honestly I think C-like syntax for ML semantics doesn’t really make sense. `let...in...` is a syntactic form that packages up a binding in an expression with a clear structure. In C-like syntax that syntactic structure is gone but we are expected to keep the semantics in mind.

---

<div class="post-metadata">

**Author:** ![igstan](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/igstan/32/183_2.png) [@igstan](https://discuss.ocaml.org/u/igstan)\
**Post date:** [April 3, 2023, 12:43pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/19 "2023-04-03T12:43:13Z")

</div>

Thanks. Yes, I’m aware of ReasonML, but as you said they went beyond just allowing semicolons in place of `in`.

---

<div class="post-metadata">

**Author:** ![Frederic\_Loyer](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/frederic_loyer/32/3472_2.png) [@Frederic\_Loyer](https://discuss.ocaml.org/u/Frederic_Loyer)\
**Post date:** [April 3, 2023, 4:58pm UTC](https://discuss.ocaml.org/t/let-in-match-and-begin-end/11768/20 "2023-04-03T16:58:08Z")

</div>

Yes, in ReasonML, `let a=42;65;` is translated `let a=42 let _=65`. And the Ocaml `let a=42;64` is translated into `let a={42;65;};`.

Then the two language have a very different syntax.
