Reputation: 141
I am learning Haskell along some basic functions. I was doing some exercises with Flip, which takes a function of two arguments and evaluates the result flipping the order of the arguments. Consider the function flip flip, I would have thought, following the definition of flip, that it flipped the arguments twice, evaluating the original function with the parameters in the original order. When I checked this assumption with ghci checking the function type, it yielded:
flip flip :: b -> (a -> b -> c) -> a -> c
I don't understand why this is the function type of flip flip. It takes parameter b and parameter (a -> b -> c) and yields a function a -> c. Why is this the case ? I would really appreciate an explanation since I am at lost with this. Thanks in advance
Upvotes: 3
Views: 672
Reputation: 1817
Plenty of good and thorough answers here, but I'd just like to add a practical example that shows an expression with flip flip
being evaluated:
flip flip 2 (>) 4
--> flip (>) 2 4
--> (>) 4 2
--> True
If it helps, you may introduce a pair of parentheses to the original expression so you can really "taste the curry": (flip flip 2 (>)) 4
.
As to why would you like to do something like that: even though I'm far from a Haskell expert, if for some reason you're determined to write a function in point-free notation, flipping a flip can come in useful. For instance - in light of the example above - if you wish to define f
by the equation f a g b = g b a
then f = flip flip
(as this answer already points out).
Upvotes: 0
Reputation: 665455
Flipping twice would be \f -> flip (flip f)
, or flip . flip
. This would indeed have the type (a -> b -> c) -> (a -> b -> c)
.
What you are doing here instead is applying flip
on the flip
function, i.e. flipping flip
's argument order. So if we start with
flip :: (a -> b -> c) -> b -> a -> c
-- and, as the type of the argument
flip :: (a' -> b' -> c') -> b' -> a' -> c'
then if we match the types
a = (a' -> b' -> c')
b = b'
c = a' -> c'
we get the result
flip flip :: b' -> (a' -> b' -> c') -> (a' -> c')
Upvotes: 13
Reputation: 2778
Let's look at the types:
flip :: (a -> b -> c) -> b -> (a -> c)
flip :: (d -> e -> f ) -> e -> d -> f
flip flip :: b -> (a -> b -> c) -> (a -> c)
In other words, flip
reverses the first two arguments of its argument, and the first two arguments of flip
are the function to flip and the second argument to that function. So instead of taking arguments in the order 'function', 'second argument', 'first argument', the order becomes 'second argument', 'function', 'first argument' when you flip it.
If you want to flip, and then flip back, you do something like this:
doubleflip x = flip (flip x)
Or equivalently:
doubleflip = flip . flip
The (.)
operator feeds the output of the right-hand side into the left-hand side.
Upvotes: 6
Reputation: 477686
You do not apply the flip
function twice. If you want to apply flip
twice, you are looking for:
flip . flip :: (b -> a -> c) -> b -> a -> c
What you here do is flipping the flip
function. So it takes flip
as the function to flip
.
We can resolve the type of flip1 flip2
(I here use subscripts to make it clear to which flip
we are referring) as:
flip1 :: (a -> b -> c) -> b -> a -> c
flip2 :: (d -> e -> f) -> e -> d -> f
Since flip2
is the parameter of flip1
, so that means that the type of flip2
is the same type of the parameter of flip1
, so that means that:
a -> (b -> c )
~ (d -> e -> f) -> (e -> (d -> f))
Hence that means that a ~ (d -> e -> f)
(the type a
is the same as d -> e -> f
), b ~ e
and c ~ (d -> e)
. So the type of the function flip1 flip2
is the type of the output type of flip1
, but with the equivalences, so that means that:
flip1 flip2 :: b -> a -> c
flip1 flip2 :: e -> (d -> e -> f) -> (d -> e)
We basically thus made a function that first takes the second parameter, then takes the function, and then the first parameter, and then calls that function with the flipped parameters. So if flip2 = flip flip
, it is implemented like:
flip2 :: e -> (d -> e -> f) -> (d -> e)
flip2 y f x = f x y
Upvotes: 4