Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Flipping a curried dollar operator

Tags:

haskell

Say I define this function:

f = ($ 5)

Then I can apply it:

> f (\x -> x ^ 2)
25

Its type is:

:t f
f :: (Integer -> b) -> b

Which makes sense, it gets a function as an argument, and returns this function applied on the Integer 5.

Now I define this function:

g = flip f

I would expect this to not make sense, because f is a function of a single argument.

But, checking its type:

:t g
g :: b -> (Integer -> b -> c) -> c

So now g is a function of 2 arguments!

Applying it on some values:

> g [2, 4, 6] (\x y -> x:y)
[5,2,4,6]

What is going on here? What does flip ($ 5) really mean?

like image 517
AmirW Avatar asked May 14 '15 18:05

AmirW


People also ask

What does flip do in Haskell?

flip f takes its (first) two arguments in the reverse order of f. flip the order of the first two arguments of a function.

What does A -> B mean in Haskell?

a -> b Bool means... forall (a :: *) (b :: * -> *). a -> b Bool. b is therefore a type constructor taking a single type argument. Examples of single-argument type constructors abound: Maybe , [] , IO are all examples of things which you could use to instantiate b .

What is a curried function Haskell?

From HaskellWiki. Currying is the process of transforming a function that takes multiple arguments in a tuple as its argument, into a function that takes just a single argument and returns another function which accepts further arguments, one by one, that the original function would receive in the rest of that tuple.


1 Answers

Follow the types:

($ 5) :: (Int -> a) -> a
flip  :: (x -> y -> z) -> y -> x -> z

But since -> is right associative, the type x -> y -> z is equivalent to x -> (y -> z), so

flip  :: (x         -> (y -> z)) -> y -> x -> z
($ 5) :: (Int -> a) -> a

So x ~ (Int -> a) and (y -> z) ~ a, so substituting back in:

($ 5) :: (Int -> (y -> z)) -> (y -> z)

And simplified

($ 5) :: (Int -> y -> z) -> y -> z

So

flip ($ 5) :: y -> (Int -> y -> z) -> z

Which is equivalent to the type you're seeing (although I used Int instead of Integer to save typing).

What this is saying is that the type of ($ 5) gets specialized when passed to flip such that it takes a function of 2 arguments. It is perfectly valid to have something like ($ 5) const, where const :: a -> b -> a and ($ 5) const :: b -> Int. All ($ 5) is doing is applying 5 as an argument to a function, not necessarily the argument for a function. This is an example of partial application, where not all of the arguments are supplied to a function. That's why you can do things like map (subtract 1) [1, 2, 3].

An example of how to use flip ($ 5) is:

> flip ($ 5) 2 (**)
25.0
> flip ($ 5) 1 (-)
4.0
> let f x y = (x, y)
> flip ($ 5) 1 f
(5, 1)
like image 89
bheklilr Avatar answered Sep 20 '22 08:09

bheklilr