Hacker News new | ask | show | jobs
by nicoburns 2405 days ago
I really, really hope JavaScript eventually gets support for:

- User defined Sum types - Pattern matching on these Sum types in switch statements - Blocks and things-that-are-currently-statements becoming expressions that evaluate to a value.

If it got those, then it would pretty close to the perfect dynamic language for me.

3 comments

Considering TypeScript ~success, python type annotations and even PHP looking into sum types it's not far fetched to think js may adopt them.
What is the benefit of pattern matching in a language where any value can be of any type at runtime? You can just pattern match by saying if (obj.type === 'foo') {}; if (obj.type === 'bar') {}; etc in JS, as long as you have set up the convention that you set the 'type' field, and practically this is just as good.

The real benefit of pattern matching is the compiler checking at least that you have valid cases, and ideally that you haven't missed any cases.

Pattern matching goes significantly beyond checking “foo.type”. It adds the ability to specify exactly (as exact as the language and your data allows) what case you’re dealing with. For example, a list of length three where the first and last elements are empty strings. Having to specify this with a series of if statements often leads to verbose and error-prone code, but it’s trivial in a language like reasonml:

    switch (mylist) {
      | [“”, _, “”] => x
      | _ => y
    }
Moreover, pattern matching usually involves pulling information out of the object at the same time. In the above example, maybe you want to do something with the middle element:

    switch (mylist) {
      | [“”, middle, “”] => middle ++ “!”
      | [“foo”, x] => String.reverse(x)
      | _ => “never mind”
    }
All of this would be possible to do with a series of if conditions, but significantly harder to read and implement correctly.

The power of pattern matching grows clearer as more complex data and rules are introduced. For example, it works just as well with nested lists:

    switch (mylist) {
      | [outer, [middle, [inner]]] => outer ++ middle ++ inner ++ “!”
      | _ => “never mind”
    }
While having a type system is helpful in all of the ways a type system is generally helpful, there’s nothing about this code which wouldn’t also be handy in a dynamic language like JavaScript. For further evidence of this see Erlang and Elixir, dynamic languages which make heavy use of pattern matching.

(Code typed on my iPhone so forgive the dopey examples)

Nevertheless, those special cases are the exception, not the rule. Most of the time the pattern matching really is just a more convenient switch statement.
I'm not sure what your experience is, but this seems to differ from mine (lots of recent Haskell experience, some dated OCaml).

I agree that matching on multiple levels in one statement isn't common, if that's what you were talking about. But performing bindings in a match is very common, and not something that's part of a switch statement (except in those languages where a switch statement is doing pattern matching more generally anyway).

Yes, I was just trying to say that matching on multiple levels is rarer. I agree with you that automatically binding new variables is the best part of pattern matching.
My examples were deliberately dumb, but I’ve certainly use this ability to pattern match at arbitrary depth innumerable times. It most commonly appears when looking at a tuple of N values, and I want to enumerate specific pairings of values.

Nevertheless, if that was the only benefit of it, one might argue it’d be more of a party trick. But it’s simply one more facet of a very powerful, simple-to-use technique for expressing how your code reacts to the shape of your data.

I’m curious how much exposure you’ve had to languages which support it — you might find it more useful than you think.

It's more than that though.

Scala example:

   myList match {
     case Nil => 0
     case first :: Nil => first * 3
     case first :: second :: Nil => first - second
     case first :: second :: rest => first * second + sum(rest)
   }
Seen ReasonML? Pretty cool. Does that with a JS-y syntax.