Pattern matching syntactic sugar

Hi, OCaml beginner here. I’m coming from Rust, which has some syntactic sugar/quality of life features around pattern matching and errors. I’m curious what the idiomatic equivalents are for OCaml?

Early return

Basically, if you have an error, you can return early:

let file = match fs::read("hello.txt") {
    Ok(file) => file,
    Err(err) => return Err(err)
};

let file = fs::read("hello.txt")?;

If I understand this thread correctly, there’s no easy way to do early return in OCaml. I suppose the simplest equivalent would be to use some combination of Result.bind and |>? Or let*?

As an aside, this type checks because return creates the type !, i.e. the Never type, which indicates a computation that doesn’t finish.

If let

This is nice for situations where you need to match on a single pattern:

if let Ok(file) = fs::read("hello.txt") {
  println!("opened file");
}
if let Ok(file) = fs::read("hello.txt") {
  println!("opened file");
} else {
  println!("failed!");
}

While let

Equivalent of if let for loops.

while let Some(token) = lexer.next() {
  process_token(token);
}

Let…else

Fairly new feature that lets you pattern match or do something that diverges, i.e. throw an error:

let Expr::Int(i) = parse() else {
  return Err(anyhow!("expected an integer"))
}

todo!

If there’s a branch that you haven’t implemented but you need the code to type check, you can add todo!() and it’ll compile (but panic at runtime):

match parse() {
  Expr::Int(i) => handle_int(i),
  _ => todo!(),
}

Thanks in advance for your help!

Generally speaking, none of these exist in OCaml. Some of them have been considered over the years, but did not gather enough consensus to be merged.

“Early return” can be simulated using exceptions or monadic operators, but there isn’t anything built-in.

Regarding “if let”: see the discussion add a `if let` construct to the language by c-cube · Pull Request #194 · ocaml/ocaml · GitHub.

Cheers,
Nicolas

This one does have an analog: assert false (or Stdlib.exit).

Cheers,
Nicolas

Unless I am missing something, let p = e1 else e2 can be written in OCaml:

match e with p -> e1 | _ -> e2

Cheers,
Nicolas

Does assert false have a type that can be unified with any other type? That’s what makes todo! special, you can do something like:

let i = match parse() {
  Expr::Int(i) => i,
  _ => todo!()
}

And it will type check.

For let...else, the else branch is guaranteed to diverge, i.e. return, panic, or some other control flow. You can read it as “match this pattern or early exit”

Yes.

Cheers,
Nicolas

1 Like

I’d generally do failwith "todo" over assert false. Otherwise you should probably do assert false (* TODO *) to differentiate from an assert false which is meant to fill in an impossible case.

1 Like

let p = e1 else e2 is an abomination. A truly grotesque and disgusting abuse of language.

I usually use the bindings operators when I need to chain Result in successives calls:

let ( let* ) = Result.bind in

let* data = read file in
let* … in
Ok ()