You need the following combinators, which are in the standard library. I have also given their standard names from combinator calculus.
id :: a -> a -- I const :: a -> b -> a -- K (.) :: (b -> c) -> (a -> b) -> (a -> c) -- B flip :: (a -> b -> c) -> (b -> a -> c) -- C (<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S
id :: a -> a -- I const :: a -> b -> a -- K (.) :: (b -> c) -> (a -> b) -> (a -> c) -- B flip :: (a -> b -> c) -> (b -> a -> c) -- C (<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S
Work with one parameter at a time. Move parameters on the left to lambdas on the right, e.g.
f x y = Z
f x y = Z
becomes
f = \x -> \y -> Z
f = \x -> \y -> Z
I like to do this one argument at a time rather than all at once, it just looks cleaner.
Then eliminate the lambda you just created according to the following rules. I will use lowercase letters for literal variables, uppercase letters to denote more complex expressions.
- If you have
\x -> x
, replace withid
- If you have
\x -> A
, whereA
is any expression in whichx
does not occur, replace withconst A
- If you have
\x -> A x
, wherex
does not occur inA
, replace withA
. This is known as "eta contraction".- If you have
\x -> A B
, then- If
x
occurs in bothA
andB
, replace with(\x -> A) <*> (\x -> B)
.- If
x
occurs in justA
, replace withflip (\x -> A) B
- If
x
occurs in justB
, replace withA . (\x -> B)
,- If
x
does not occur in eitherA
orB
, well, there's another rule we should have used already.And then work inward, eliminating the lambdas that you created.