6 ms·
Here is an historical note for those who might be interested. In 1979 I did a Post-doc with Rod Burstall. I helped implement the programming language Hope, whic
by herodotus 7y ago
Here is an historical note for those who might be interested. In 1979 I did a Post-doc with Rod Burstall. I helped implement the programming language Hope, which was the first functional programming language to use pattern-matching in function definitions. The implementation was written in an older language called POP-2. Robin Milner and his group were at the the time working on LCF and ML, and they eventually incorporated Burstall's pattern ideas. Eventually, I recall there was a decision within the UK functional programming community to consolidate their efforts into what became Haskell. And of course we see how much Haskell has influenced the newest crop of imperative languages.
It is interesting (to me) to compare factorial in Rust with factorial in Hope:
Rust:
fn factorial(i: u64) -> u64 {
match I {
0 => 1,
n => n * factorial(n-1)
}
}
Hope:
dec factorial : num -> num;
--- factorial 0 <= 1;
--- factorial n <= n* factorial(n-1);
Note that, in Hope (unlike its own inspiration, Prolog, and, I think unlike Rust), the order of the rules does not matter: the most specific pattern takes precedence. Hope was primitive with respect to types, and did not use Milner's type inferrencing ideas. I don't think Burstall ever intended it to be a "real" programming language. When I left Edinburgh, Don Sanella took over from me, so I was not involved in writing the paper about Hope, but my contribution is acknowledged.
I implemented the pattern matching code, and the idea of pattern compilation occurred to me. I remember showing it to Rod. He freaked out at first, but after about a five minute harangue, the penny dropped, and I remember him saying "clever Michael", "clever Michael" in his charming way.
- steveklabnik 7y agoVery cool, thank you for this history! > I think unlike Rust Yes, this is correct: Rust looks at patterns in order, top to bottom. Incidentally, some folks have asked for a syntax closer to Hope (and Haskell, and others) https://github.com/rust-lang/rfcs/pull/1564 https://github.com/rust-lang/rfcs/pull/1564 fn factorial(0: u64) -> u64 { 1 } fn factorial(n: u64) -> u64 { n * factorial(n - 1) } I doubt this will ever happen, though.
- hota_mazi 7y agoI really hope this doesn't happen. I've never understood the infatuation for this Haskell type syntax where you need to repeat the name of the function for each and every single match case. It's so needlessly verbose.
- macintux 7y agoI find it dramatically cleaner. Each clause is categorically isolated, unlike a case statement (the alternative in Erlang, with which I’m most familiar) where code can precede the case statement and thus introduce bindings that can muck up the logic.
- hota_mazi 7y agoYes but why repeat the name of the function every time? It might make more sense if such clauses are scattered through the source, but they are pretty much all the time grouped together, so the name of the function doesn't need to be repeated all the time. For example, instead of factorial 0 => 1; factorial n => n* factorial(n-1); Something like factorial { 0 => 1 n => n* factorial(n-1); }
- zamalek 7y agoIt's probably for double dispatch, which Rust doesn't have outside of traits anyway.
- AaronFriel 7y agoRepeating the name allows some optimizations but you'd be unlikely to use or benefit from them unless you were writing functions that returns functions and you could benefit from inlining some patterns and not others. I think I've seen the pattern in some web framework code, byte libraries, etc. Although semantically equivalent, the optimizer treats the following differently: f a = \b -> ... f a b = ...
- macintux 7y agoAs much as I like short functions, not all function clauses are just one line. I don’t see your alternative as helpful for longer clauses.
- ratmice 7y agoLacking this is probably my biggest gripe when trying to pattern match recursive structures, though mostly closures, this saddens me, you could say my hopes have been dashed.
- Callmenorm 7y agoI write Elixir (which uses this syntax) and Rust (which doesn't) and I think both are fine. I'm hoping Rust doesn't support this mostly because I don't think the language support this sugar solves any problems Rust has.
- epage 7y agoErlang adopted function pattern matching https://uniwebsidad.com/libros/concurrent-erlang/chapter-1/pattern-matching https://uniwebsidad.com/libros/concurrent-erlang/chapter-1/p...
- falsissime 7y agoOut of curiosity: What did f 0 0 give for f _ 0 = 1; f 0 _ = 2; ?
- rdtsc 7y agoHope is pretty close to the Erlang version: factorial(0) -> 1; factorial(N) -> N * factorial(N-1). At first I was suspicious of function head clauses but have really come to like them. They make the code much more readable and easier to maintain. You can add another clause and keep your change diff small. It could be a general catch-all clause at the end, or a more specific, restricted case at the front and so on. Tracing is also much nicer. Fire up dbg or recon and it's easy to pick off a particular clause without much effort.