3
votes

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

3
You do not flip twice, you flip the flip function.Willem Van Onsem
And just for fun, try flip flip flip.augustss

3 Answers

10
votes

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')
6
votes

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.

4
votes

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