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?
相关问题
- Keeping track of variable instances
- How to get the maximum of more than 2 numbers in V
- F#: Storing and mapping a list of functions
- Understanding do notation for simple Reader monad:
- Making Custom Instances of PersistBackend
相关文章
- Is it possible to write pattern-matched functions
- Haskell underscore vs. explicit variable
- Is there something like the threading macro from C
- Top-level expression evaluation at compile time
- Stuck in the State Monad
- Accessing an array element when returning from a f
- Learning F#: What books using other programming la
- Creating a list of functions using a loop in R
+
is the actual function and it is waiting to receive a certain number of parameters (in this case 2 or more) until it returns. If you don't give it two or more parameters, then it will remain a function waiting for another parameter.As a side note, the language Haskell is named after Haskell Curry, who re-discovered the phenomenon of Functional Currying while working on combinatory logic.
In Haskell, you can take a function of two arguments, apply it to one argument, and get a function of one argument. In fact, strictly speaking, + isn't a function of two arguments, it's a function of one argument that returns a function of one argument.
Languages like Haskell or OCaml have a syntax that lends itself particularily to currying, but you can do it in other languages with dynamic typing, like currying in Scheme.
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 functionplus
instead. I'm also going to specialiseplus
to 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 twoInt
s 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 anInt
to (a function from anInt
to 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 functionplus
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:
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) andplus 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 ofplus
to 2 arguments, or the application ofplus
to2
and 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 ofplus
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 toInt
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 argument5
, 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 input2
. The result will be a function. Take that function, and apply it to the input3
. The result of that is a number.Therefore,
(+)
is a function,(5 +)
is a function, and(+) 2 3
is a number.