-
How is this possible, what is going on there?
-
Is there a name for this?
-
What other languages have this same behavior?
-
Any without the strong typing system?
How is this possible, what is going on there? Is there a name for
Share
Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.
Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.
Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
This behaviour is really simple and intuitive if you look at the types. To avoid the complications of infix operators like
+, I’m going to use the functionplusinstead. I’m also going to specialiseplusto work only onInt, to reduce the typeclass line noise.Say we have a function
plus, of typeInt -> Int -> Int. One way to read that is “a function of twoInts that returns anInt“. But that notation is a little clumsy for that reading, isn’t it? The return type isn’t singled out specially anywhere. Why would we write function type signatures this way? Because the->is right associative, an equivalent type would beInt -> (Int -> Int). This looks much more like it’s saying “a function from anIntto (a function from anIntto anInt)”. But those two types are in fact exactly the same, and the latter interpretation is the key to understanding how this behaviour works.Haskell views all functions as being from a single argument to a single result. There may be computations you have in mind where the result of the computation depends on two or more inputs (such as
plus). Haskell says that the functionplusis a function that takes a single input, and produces an output which is another function. This second function takes a single input and produces an output which is a number. Because the second function was computed by first (and will be different for different inputs to the first function), the “final” output can depend on both the inputs, so we can implement computations with multiple inputs with these functions that take only single inputs.I promised this would be really easy to understand if you looked at the types. Here’s some example expressions with their types explicitly annotated:
If something is a function and you apply it to an argument, to get the type of the result of that application all you need to do is remove everything up to the first arrow from the function’s type. If that leaves a type that has more arrows, then you still have a function! As you add arguments the right of an expression, you remove parameter types from the left of its type. The type makes it immediately clear what the type of all the intermediate results are, and why
plus 2is a function which can be further applied (its type has an arrow) andplus 2 3is not (its type doesn’t have an arrow).“Currying” is the process of turning a function of two arguments into a function of one argument that returns a function of another argument that returns whatever the original function returned. It’s also used to refer to the property of languages like Haskell that automatically have all functions work this way; people will say that Haskell “is a curried language” or “has currying”, or “has curried functions”.
Note that this works particularly elegantly because Haskell’s syntax for function application is simple token adjacency. You are free to read
plus 2 3as the application ofplusto 2 arguments, or the application ofplusto2and then the application of the result to3; you can mentally model it whichever way most fits what you’re doing at the time.In languages with C-like function application by parenthesised argument list, this breaks down a bit.
plus(2, 3)is very different fromplus(2)(3), and in languages with this syntax the two versions ofplusinvolved would probably have different types. So languages with that kind of syntax tend not to have all functions be curried all the time, or even to have automatic currying of any function you like. But such languages have historically also tended not to have functions as first class values, which makes the lack of currying a moot point.