The meaning of curry
can be easier to be seen when the type signature is written as
curry :: ((a, b) -> c) -> (a -> b -> c)
that is, a function taking a single parameter of type (a, b)
and yielding a result of type c
is turned into a function taking two separate values of types a
and b
yielding the same result type c
.
We might define curry for this signature like this:
curry f = g
where f :: ((a, b) -> c)
and g :: (a -> b -> c)
.
f
is passed by the user, but how do we get g
? We know g
is a function taking two parameters and yielding a result type, so it must structurally look like \x -> \y -> z
, where x :: a
, y :: b
, and z :: c
(\v -> e
is the syntax for lambda-expressions in Haskell). We now have to find a way to get the result z
from x
and y
such that the types work out correctly.
As both result types c
must be the same, the only way to get z
is to use f
and apply it to a value of type (a, b)
. So we know z = f (?, ?)
where (?, ?)
must have type (a, b)
. Where do we get values of these types from? Again, as the types must match exactly, the only option is to use x
and y
here. We get
curry f = g
where g = \x -> \y -> f (x, y)
This would be a proper implementation for curry
. One that is easier to read is to inline g
:
curry f = \x -> \y -> f (x, y)
As Haskell allows us to define functions with multiple parameters, we can resolve the lambda-expression and simplify this to
curry f x y = f (x, y)
which is (beside different variable names) the same implementation as in your question.
For uncurry
the procedure is similar (note that you had missing parentheses in the type signature for uncurry
in your original question which lead to a different type).
From
uncurry :: (a -> b -> c) -> ((a, b) -> c)
we conclude
uncurry f = g
where g = \(x, y) -> z
with f :: (a -> b -> c)
, g :: (a, b) -> c
, x :: a
, y :: b
and z :: c
. Following the types, z
can only be defined by z = f x y
. From that we get
uncurry f = g
where g = \(x, y) -> f x y
and after inlining g
and removing the lambda
uncurry f (x, y) = f x y