~icefox/garnet#64: 
Pattern matching

Time to actually design it.

https://gleam.run/news/v0.33-exhaustive-gleam/ has some good resources.

Status
REPORTED
Submitter
~icefox
Assigned to
No-one
Submitted
1 year, 1 month ago
Updated
3 months ago
Labels
T-DESIGN

~icefox 1 year, 5 days ago

https://compiler.club/compiling-pattern-matching/ has decent implementation stuff.

In terms of actual design, so far I'm fine basically co-opting Rust's semantics without any major innovations or design changes. Captures and conditions are important but not particularly common, at least in Rust code, so I kinda dislike its Foo(x, y) @ x style but don't have a pressing need to even implement it, let alone try to improve it.

match foo with
  | Thing { a, b, _ } -> thing1
  | Other { .x = x, .y = y } -> thing2
  // syntactic sugar for the above?
  | Other2 { x, y } -> thing3
  | _ -> default
end

The .x =... syntax instead of x: ... might make distinguishing variable captures vs. matching on variables easier, ponder it more later.

Questions: if let? while let? I'm going to assume that every let is a pattern match 'cause that's nice; are we going to bother with pattern matching in function args? Eh, probably but it's not critical so far, we'll see how hard it is.

~icefox 9 months ago

https://perso.ens-lyon.fr/thais.baudon/icfp2023.pdf has some good references on compiling pattern matching. https://dl.acm.org/doi/10.1145/1411304.1411311 seems to be the most recent. PDF of it available here: https://www.cs.tufts.edu/~nr/cs257/archive/luc-maranget/jun08.pdf

~icefox 6 months ago*

From https://lobste.rs/s/rnqlef/named_arguments_rust_if_you_want_them:

I wish that you could just put .. at the end of a strict definition to extend from default. I think it would be some nice syntax sugar that makes a common case much less noisy.

let x = Target { bar: 3, .. Default::default() };
// becomes
let x = Target { bar: 3, .. };

~icefox 4 months ago

Also to consider: Haskell/Erlang-y style pattern matching on function signatures, allowing you to define a function as a pattern match. For example, Rust does not let you do this:

fn bar(None: Option<i32>) -> i32 {
    panic!()
}
fn bar(Some(x): Option<i32>) -> i32 {
    x
}

It just complains that the match isn't exhaustive and that bar() is redefined. But really it's just sugar for a function containing a match. If you could write it in Garnet, maybe it would look like this?

fn bar(None Option[I32]) I32 =
    panic!()
or bar(Some(x) Option[I32]) I32 =
    x
end

One nuisance I have with this in Erlang is that it then makes it a bit tricky to actually hunt down the implementation of bar() that you happen to be looking for at any particular time. Using or instead of fn, in the style of OCaml's let ... and ..., is an attempt to at least force people to keep the implementations together. Another alternative is a lint to warn you if the implementations are far apart or too long; if it's more than a screenful of code then it's easy to miss the second impl or the or further down.

This is pure sugar, but it is pretty nice sometimes.

Simon Heath 3 months ago ยท edit

https://dl.acm.org/doi/10.1145/3689746

Interesting paper on pattern matching constructs and syntax.

~akavel 3 months ago

FTR - regarding the paper, one point that is easy to overlook when skimming, is the proposed syntax for languages not using "significant whitespace" for blocks (pg. 6):

if x { > 0 then "positive"; == 0 then "null"; else "?" }
Register here or Log in to comment, or comment via email.