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.
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.
function(SpecialCase ...) ->
...
handle_odd_special_case;
function(DefaultWhatever ...) ->
...
main_business_logic.
This is more cleaner _sometimes_ than say function(...) ->
case Arg of
SpecialCase ->
...
handle_odd_special_case;
Default ->
...
main_business_logic
end
Now imagine another level of case statements and all of the sudden the main path of the algorithm is squashed to the right with only a 40 columns left.1. Don't have to name the parameter
2. less nesting.
You can get the best of all worlds with lambdacase style constructs. You don't need to write out the name of the function a bunch of times.
function = \case
SpecialCase ->
...
handle_odd_special_case;
Default ->
...
main_business_logic;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.
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);
}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 = ... factorial 0 = 1
factorial n = n * factorial (n - 1)
// or:
factorial n = case n of
0 -> 1
n -> n * factorial (n - 1) parent(alice, brad).
parent(alice, cecilia).
parent(cecilia, david).
parent(david, emily).
parent(brad, felix).
parent(felix, ginny).
ancestor(A, B) :- parent(A, B).
ancestor(A, B) :- parent(A, M), ancestor(M, B).
The first section defines a small family: alice is the parent of brad and cecilia, cecilia is the parent of david, etc. (identifiers beginning with lower-case letters are atoms, the ones beginning with upper-case are variables). Note that for these first definitions without a body (called "facts" in prolog), it's perfectly natural to have multiple "definitions": you can have as many parent-child relations as you want.The second predicate ("ancestor") is more interesting: it has two definitions: if A and B are people, then A is the ancestor of B if A is the parent of B, or if A is the parent of M, and M is the ancestor of B.
Notice that there are is no pattern matching happening (Prolog of course has pattern matching, it more or less invented the concept, but I'm not using it here). When you use "ancestor", Prolog has no idea which definiton you want, the patterns are identical. It has to try the first one, and if it fails, it backtracks and tries the second one.
Some examples of using this in the repl. First, "who are alice's children":
?- parent(alice, Child).
Child = brad ;
Child = cecilia.
The semi-colon means "or", so this means "Child is equal to brad, or Child is equal to cecilia". Second, "who are ginny's ancestors": ?- ancestor(Ancestor, ginny).
Ancestor = felix ;
Ancestor = alice ;
Ancestor = brad.
The central idea here is that there is no "one definition" of a predicate or fact, you can have as many as you want, and prolog will search through them. Similarly, any query can have multiple values for the variables that make them true. Repeating the predicate name is the most natural way of expressing this idea. You don't have to, you could rewrite these so that they had only one definition, but that just makes what's going on less clear.> Every function clause must be together. It is an error to put another item between two function clauses.
So, it would not. Then again, the proposal wasn't accepted so it's kind of a moot point. But you're totally right that without this it would be very tricky. Given that match arms have an ordering, I'm not sure you could come up with a semantic where it would work across compilation units.