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?
The most basic way of defining a function in Haskell is to ``declare'' what it does. For example, we can write: double :: Int -> Int double n = 2*n. Here, the first line specifies the type of the function and the second line tells us how the output of double depends on its input.
In a functional language, there are only functions. Although it might seem that a language like Haskell has a lot of different objects and constructs, they can all be reduced to functions. We will demonstrate how variables, tuples, lists, conditionals, Booleans and numbers can all be constructed from lambda functions.
Haskell has first-class functions : functions are values just like integers, lists, etc. They can be passed as arguments, assigned names, etc. … val is value of type Int , and half_of is a value of type Float -> Float .
The shows functions return a function that prepends the output String to an existing String . This allows constant-time concatenation of results using function composition.
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 function plus
instead. I'm also going to specialise plus
to work only on Int
, to reduce the typeclass line noise.
Say we have a function plus
, of type Int -> Int -> Int
. One way to read that is "a function of two Int
s that returns an Int
". 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 be Int -> (Int -> Int)
. This looks much more like it's saying "a function from an Int
to (a function from an Int
to an Int
)". 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 function plus
is 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:
plus :: Int -> Int -> Int
plus 2 :: Int -> Int
plus 2 3 :: Int
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 2
is a function which can be further applied (its type has an arrow) and plus 2 3
is 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 3
as the application of plus
to 2 arguments, or the application of plus
to 2
and then the application of the result to 3
; 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 from plus(2)(3)
, and in languages with this syntax the two versions of plus
involved 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.
In Haskell, all functions take exactly 1 input, and produce exactly 1 output. Sometimes, the input to or output of a function can be another function. The input to or output of a function can also be a tuple. You can simulate a function with multiple inputs in one of two ways:
Use a tuple as input(in1, in2) -> out
Use a function as output*in1 -> (in2 -> out)
Likewise, you can simulate a function with multiple outputs in one of two ways:
Use a tuple as output*in -> (out1, out2)
Use a function as a "second input" (a la function-as-output)in -> ((out1 -> (out2 -> a)) -> a)
*this way is typically favored by Haskellers
The (+)
function simulates taking 2 inputs in the typical Haskell way of producing a function as output. (Specializing to Int
for ease of communication:)(+) :: Int -> (Int -> Int)
For the sake of convenience, ->
is right-associative, so the type signature for (+)
can also be written(+) :: Int -> Int -> Int
(+)
is a function that takes in a number, and produces another function from number to number.
(+) 5
is the result of applying (+)
to the argument 5
, therefore, it is a function from number to number.
(5 +)
is another way to write (+) 5
2 + 3
is another way of writing (+) 2 3
. Function application is left-associative, so this is another way of writing (((+) 2) 3)
. In other words: Apply the function (+)
to the input 2
. The result will be a function. Take that function, and apply it to the input 3
. The result of that is a number.
Therefore, (+)
is a function, (5 +)
is a function, and (+) 2 3
is a number.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With