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 understand the logic behind it, but when I am trying to execute some simple examples in code I am getting compile errors. I want to try and write this function in point-free style:
f x = 5 + 8/x
which I rearranged as f x = (+) 5 $ (/) 8 x
So, I thought it might be something like this:
f = (+) 5 $ (/) 8
but when I run this in ghci I get this message:
No instance for (Num (a0 -> a0))
arising from the literal `5' at Test.hs:3:9
Possible fix: add an instance declaration for (Num (a0 -> a0))
In the first argument of `(+)', namely `5'
In the first argument of `($)', namely `(+) 5'
In the expression: (+) 5 $ (/) 8
Failed, modules loaded: none.
I don't understand the "No instance for..." message. What do I need to do to write this function in point-free style?
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 toid
;\x -> y
withoutx
occurring iny
translates toconst y
;\x -> f g
translates tof' <*> g'
wheref'
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: iff
does not have any free occurrences ofx
, then\x -> f g
translates toconst f <*> (\x -> g)
, which is equal tof . (\x -> g)
.Using those rules we can convert your function:
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: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: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 havea $ b $ c $ d
, this is equivalent toa . b . c $ d
.At this point, let's actually remove the
$
and parenthesize instead.Now it should be clear that you can remove the trailing
x
from both sides:That is the main idea. If you have
f x = expr x
, you can "eta reduce" it tof = 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:$
has a very low precedence. So,f x = (+) 5 $ (/) 8 x
actually meansf x = (+) 5 $ ((/) 8 x)
. Instead, rewrite that asThe 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".