I am trying to understand how to convert functions to point-free notation in Haskell. I saw this example, but it is more complicated than what I am looking for. I feel like I un
Conversion from lambda-calculus (which Haskell is a variant of) terms to SKI terms (totally pointfree functions, using only const
(K), id
(I) and <*>
(S)) can be done with the following simple rules:
\x -> x
translates to id
;\x -> y
without x
occurring in y
translates to const y
;\x -> f g
translates to f' <*> g'
where
f'
is a translation of \x -> f
andg'
is a translation of \x -> g
.Now you may wonder where does the .
come in. There is a special case of the last translation: if f
does not have any free occurrences of x
, then \x -> f g
translates to const f <*> (\x -> g)
, which is equal to f . (\x -> g)
.
Using those rules we can convert your function:
f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule
((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f)
((+) 5) . ((/) 8)
Eta-reduction is not necessary to complete the translation, but without it we'd get something messier. For example, the last step would yield ((+) 5) . ((/) 8) . id
instead.
The "pointfree" program can be installed with cabal install pointfree
, and shows you how to write an expression in pointfree style. For example:
$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)
Explanation of this conversion:
(a +) == \b -> a + b
and (+ a) == \b -> b + a
.
function takes the result of the second parameter, which is a one-argument function, and applies it to the first argument.You were really close. Allow me to add one more $
to illustrate:
f x = (+) 5 $ (/) 8 $ x
It should be clear that the expression (+) 5
is a function that takes one numeric input and produces a numeric output. The same goes for the expression (/) 8
. So you take whatever number is input, x
, and first apply the (/) 8
"function", and then apply the (+) 5
"function".
Whenever you have a chain of functions separated by $
, you can replace all except the rightmost with .
Meaning, if you have a $ b $ c $ d
, this is equivalent to a . b . c $ d
.
f x = (+) 5 . (/) 8 $ x
At this point, let's actually remove the $
and parenthesize instead.
f x = ((+) 5 . (/) 8) x
Now it should be clear that you can remove the trailing x
from both sides:
f = (+) 5 . (/) 8
That is the main idea. If you have f x = expr x
, you can "eta reduce" it to f = expr
. In order to produce pointfree code, you need simply recognize how the larger function is composed of smaller functions. Partial application is sometimes necessary for point free code (as in this case, (+) 5
and (/) 8
are partially applied). The "pointfree" program is quite helpful for when you don't want to think about it; Lambdabot on the #haskell irc channel uses this program as a plugin, so you don't even have to install it yourself; just ask:
<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
$
has a very low precedence. So, f x = (+) 5 $ (/) 8 x
actually means f x = (+) 5 $ ((/) 8 x)
. Instead, rewrite that as
f x = (+) 5 ( (/) 8 x)
f x = ((+) 5) ( ((/) 8) x)
f x = ((+) 5) . ( ((/) 8) ) x
f = ((+) 5) . ( (/) 8 )
f = (5+) . (8/)
The last expression makes sense: f is the composition of two operations, first divide 8 by what one has, and then add 5 to the result. Remember, g.h
means "apply h, then apply g the the result of that".