4 ms·
>The best part of Haskell is that function application is just whitespace. It's the worst part, in my opinion. Is A B C D equal to A(B(C(D))), A(B,C,D), A(B,C(
by tormeh 12y ago
>The best part of Haskell is that function application is just whitespace.
It's the worst part, in my opinion. Is A B C D equal to A(B(C(D))), A(B,C,D), A(B,C(D)), A(B(C),D) or A(B(C,D))? Often you need to disambiguate with parentheses, so I guess there is some default interpretation that you can assume when reading code, but in the end it just looks like an unnecessary mental translation step.
Lisp has weird syntax, but lisp has a reason to have that syntax. Haskell has a weird syntax because it wants to either be different for different's sake or to just be plain inaccessible.
- gamegoblin 12y agoA B C D is necessarily equal to A(B,C,D) in C-family syntax due to the precedence rules, there is no ambiguity. The reason whitespace is more elegant is because your functions can return other functions, which due to function currying and partial application is equivalent to your function simply taking more arguments. I'll use Python as an example. def flip(f): return lambda a,b : f(b,a) That is, it's a function that takes a function of two arguments, and returns a function that does the same thing, but with the argument order flipped. To use these function, it would look like: flip(f)(a,b) In Haskell, a direct translation with the lambda would be: flip f = \a b -> f b a The type of which could be written as: flip :: (a -> b -> c) -> (b -> a -> c) That is, a function which takes one argument, that argument is a function that takes two arguments of types a and b and yields a value of type c, and returns a function that takes two arguments of types b and a and yields a value of type c. We could use this function like: (flip f) a b But due to function precedence we could use flip f a b This segues into an equivalent (and arguably better) way to write the function taking advantage of partial application and currying: flip :: (a -> b -> c) -> b -> a -> c flip f a b = f b a Which would be used the exact same way: flip f a b This illustrates the elegance of currying. a -> b -> c Is equivalent to a -> (b -> c)
- TazeTSchnitzel 12y agoAll Haskell functions are curried, so there's no ambiguity.
- epidemian 12y agoConsider that, in Haskell: 1. all functions take a single argument (i.e., are curried) 2. function application is left-associative So "a b c d" is just applying the function "a b c" with the argument "d", the same as "(a b c) d". "a b c" is the application of "c" to the function "a b", which, in turn, is the application of "b" to "a". So "a b c d" is the same as "((a b) c) d". I personally find using the space symbol for a core concept of the language (function application) very elegant, and not dissimilar to other languages that strive to have simple and consistent syntax, like Smalltalk, where "obj meth1 meth2 meth3" is the same as "((obj meth1) meth2) meth3", even though Haskell and Smalltalk might be as different as programming languages can get :)
- tormeh 12y agoHow does currying help? You still have to figure out which argument belongs to which function. GHC demands parentheses if you try to write A(B(C(D))) as A B C D, so I'm pretty sure I'm right.
- tomp 12y agoNo, you're wrong. `A(B(C(D)))` is different from `(((A B) C) D)` (which is the parenthesized version of `A B C D`). Currying allows simple partial application; if A is a function that "takes 3 arguments" (i.e. it has type `w -> x -> y -> z`, which means that it takes an argument of type `w` and returns a function with type `x -> y -> z`, which takes an argument ...), you can say e.g. A_ = A B C E1 = A_ D1 E2 = A_ D2 where `A_` is the partially applied function `A`.
- tormeh 12y agoI understand currying and partial function application. That's not the problem. The problem is that function application whitespace demands an arbitrary application precedence and obscures function signature to the reader.
- tomp 12y agoah, that. Well, you get used to it. Also, it helps that application has the highest precedence, so it's quite easy to read expressions like `f x y + g z` (i.e. `(f x y) + (g z)`).
- Retra 12y ago>Lisp has weird syntax, but lisp has a reason to have that syntax. Lisp has that syntax because it provides a uniformity to parsing the language, and that's the same thing Haskell offers . If I write a = b + c You might as well parse that as `a(=(b(+(c))))` with the appropriate semantics. In that sense, Haskell is just trying to do something like what Lisp does without all the extra parentheses.
- tormeh 12y agoThat's... actually really neat. So there's no operator precedence?
- Retra 12y agoYes, there is operator precedence, but it only matters for infix notation. Or rather, function application has highest precedence, so there is no ambiguity using it. My broader point was that the space is the most common symbol in English because the function that it performs is the most fundamental action of the language. And since Haskell is a functional language, function application is fundamental, and thus you gain a lot of terseness by using spaces to denote it. Lisp does the same thing: `(+ a b)`, it just puts parentheses around things to make the parsing rules simpler. If you parse it in a different way, you won't need that many parentheses.