Literally the only feature preventing Typescript from being my perfect language.
Literally the only feature preventing Typescript from being my perfect language.
I'm not sure if it's stalled or not. But it's being discussed.
"We didn't do ..." is so incredibly important.
But I definitely agree with the sentiment - every new feature is another thing that a developer needs to learn if they encounter that code. There should be a large "barrier to entry" for new features.
Not to mention JS is not typed, but pattern matching that can't match typescript types would be terrible.
Trying to do simple things like matching the content of a variable will fail if you forget you have to use a dotted path if you want to avoid binding.
Trying to do advanced things such as "match a dict with variable key names and values, but you wish to enforce the number and the types and unpack them in variables" is ridiculously twisted to do, when it's even possible.
I use match/case, but it like the early years of type hints or asyncio: terrible ergonomics, and we know there is a ceiling to how much it can improve.
Either way, the benefit has to be enormous. It's a feature that can easily break existing code.
You could say “return match…”
It’s not easy to introduce without breakage. Not saying it cannot be done.
But PHP didn’t manage to for example. It also isn’t real pattern matching but that’s a different issue.
Java and C# show how the syntax can be done in a fully backwards-compatible manner. It looks like it's just not the JS way by choice - apparently every new keyword that was added since ES5 is an actual keyword reserved in all contexts, not context-dependent?
// Given an Option
val maybeThing: Option[String] = getThing()
// Classic
if (maybeThing.isDefined) {
useThing(maybeThing.get)
} else {
NotFoundResponseEtc()
}
// Pattern matching (not too IDE auto generated exhaustive match cases!)
maybeThing match {
case Some(thing) => useThing(thing)
case None => NotFoundResponseEtc()
}
This scales well when matching a higher cardinality of things like a variety of Exceptions or enums or other tuple responses like Either etc.What convinced me of the power of pattern matching was seeing a red-black binary tree being implemented effortlessly in Ocaml (I think), while in C++ and Java it was a really difficult algorithm to implement.
When you have provably exhaustive pattern matching (i.e. the compiler forces you to handle every possible case), certain things that are very difficult to write otherwise become very easy.
a = b + c
d = e - f
g = a * d
But the compiler doesn't allow: g = (b + c) * (e - f)
You have expressions that produce, but they don't compose. You can't produce a value from a more complex, nested expression. We, rightly, no longer use languages like that.Pattern matching parallels that, except for assignment and decomposing values. Many languages today let you write:
topLeft = rect.topLeft.x;
left = topLeft.x;
top = topLeft.y;
bottomRight = rect.bottomRight;
right = bottomRight.x;
bottom = bottomRight.y;
Or even: left = rect.topLeft.x;
top = rect.topLeft.y;
right = rect.bottomRight.x;
bottom = rect.bottomRight.y;
(Because at least you can compose expressions on the RHS.) But they don't let you write: (topLeft, bottomRight) = rect;
(left, top) = topLeft;
(right, bottom) = bottomRight;
Or even: ((left, top), (right, bottom)) = rect;
Pattern matching gives you that. It is freely composable destructuring.Also, the "matching" part means that in many languages you can also ask questions about values as you destructure them, which enables a particularly nice style of programming.
I started implementing Lox with Java's sealed classes + pattern matching on switch. The exhaustiveness has been really nice to ensure I cover each new token/expression as I add them.
It's one of those things that when you get used to, you wonder why other languages don't implement it.
[1] https://docs.ruby-lang.org/en/3.0/syntax/pattern_matching_rd...