remix logo

Hacker Remix

The Ultimate Conditional Syntax

144 points by azhenley 3 days ago | 48 comments

dan-robertson 3 days ago

I like that there is this left-to-right flow. I think it’s a bit nicer to read than if-let ordering where the pattern comes before the thing it will be matched against. I think it’s also good for ocaml-style constructor disambiguation, which tends to go in lexical order.

Another nice aspect of making guards a less special case is that it avoids complexities in deciding if a binding is unused. I believe this logic was a source of lots of compiler warning bugs in ocaml.

This syntax doesn’t seem to solve the following problem with matching where there are two paths to the same binding, (say you have an option in one branch but it isn’t optional in the other, and maybe you’d like to handle both cases with the same code. Currently you can do that with a match (match …) with … pattern.

I worry that the semantics around exhaustiveness and mutable values may be confusing, though I guess OCaml already has that problem:

  type t = { mutable x : bool }

  let n = function true -> 1 | false -> 0

  let f t =
    match t with
    | { x = false } when ( t.x <- true; false) -> -1
    | { x } -> n x * 2 + n t.x
What does t { x = false } return? Similarly if you changed the second case to be two cases instead of binding x?

lptk 3 days ago

As mentioned in a response to a sibling comment, we plan to support `or`, which should address the problem you mention. (If not, would you have an example of what you mean?)

> I worry that the semantics around exhaustiveness and mutable values may be confusing, though I guess OCaml already has that problem

Indeed, and it was until very recently a source of unsoundness: https://icfp24.sigplan.org/details/mlworkshop-2024-papers/8/...

munificent 2 days ago

Oh, wow, that's interesting.

We added pattern matching and exhaustiveness to Dart not that long ago, and dealing with exhaustiveness and mutability was a big concern since Dart (unlike more strictly functional languages) generally doesn't avoid mutability.

Our solution was that whenever a pattern accesses a property, the language implicitly caches that value. Any future accesses to the same property in that switch statement/expression use the previously cached value. That way, an entire set of switch cases is always operating on an immutable snapshot of data so that exhaustiveness can't be violated by side effects. Spec:

https://github.com/dart-lang/language/blob/main/accepted/3.0...

klauserc 2 days ago

C# pattern matching gets very close. I think C# can do everything except for their last example ("splitting conditional prefixes in arbitrary places"). One of the things I miss when switching to Rust.

    if foo(args)
        == 0 then "null"
        |> abs
            > 100 then "large"
            < 10 then "small"
            else "medium"
That last syntax took me a while to parse in the paper, but I can imagine numerous places in our everyday code where such syntax would more concisely capture intent.

lptk 2 days ago

Genuine question, as I'm not up to date with C#'s recent developments: can C# do this?

    if e is
      ...
      Lit(value)
        and Map.find_opt(value) is Some(result)
        then Some(result)
      ...
where the `...` may include many cases and may contain other Lit cases.

Or this variation:

      ...
      Lit(value)
        and Map.find_opt(value) is Some(result)
        and computation(result) is
          Left(a)  then ...
          Right(b) then ...
      ...

tobbebex 2 days ago

plaguuuuuu 2 days ago

I'm wondering if you could use Roslyn to implement your snippet's syntax anyway.

upghost 2 days ago

Kudos to the authors. It takes some fortitude to try to make a contribution to something as fundamental and ubiquitous as the "if" statement. Appreciate providing the grammar and the thorough explanations, will save a lot of pain when trying to implement!

zelphirkalt 2 days ago

> Below, the code on the left is equivalent to the more verbose ML expression on the right:

Wait a sec ... ML has the pipe of Elixir? Damn, I didn't know that!

I like the brevity, but I don't like the (at least visual) need to indent or whitespace focus. I guess I am too much a sucker for S-expressions now. But a similar structure could probably be written as a macro in a lisp, avoiding any whitespace focus or even dependency on it.

zeckalpha 2 days ago

Other way around... Elixir has the pipe of ML.