Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the prerequisites for a point-free function in Haskell

I always thought that the prerequisites for a pointfree function were to get the function arguments to the end of the definition. E.g.

-- This can be made pointfree quite easily:    
let lengths x = map length x    
let lengths' = map length

-- However this cannot:
let lengthz x = length `map` x
-- let lengthz' = length `map` (parse error)

I originally came across this reading this question. There we have this example:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y
-- This may look like it can easily be made pointfree, however it cannot
-- agreeLen' :: (Eq a) => [a] -> [a] -> Int
-- agreeLen' = length $ takeWhile id $ zipWith (==) (zipWith is applied to too few arguments)

So why can my first example be made pointfree, but the other two cannot?

like image 899
hgiesel Avatar asked Nov 29 '22 13:11

hgiesel


1 Answers

-- However this cannot:
let lengthz x = length `map` x
-- let lengthz' = length `map` (parse error)

\x -> length `map` x written point free is simply map length. The infix backticks are just syntactical sugar. (As chepner points out, if you really want it you can use a section, i.e. (length `map`).)

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y
-- This may look like it can easily be made pointfree, however it cannot

The key word here is "easily". Pretty much anything can be made point-free if you try hard enough. In this case, omitting the y parameter is easy if we write agreeLen in terms of (.) rather than ($):

agreeLen x y = (length . takeWhile id . zipWith (==) x) y
agreeLen x = length . takeWhile id . zipWith (==) x

As for x, we can handle it by treating the use of (.) to compose zipWith (==) x with the other functions as just another case of a value being modified with a function:

agreeLen x = (.) (length . takeWhile id) (zipWith (==) x)
agreeLen x = ((length . takeWhile id) .) (zipWith (==) x) -- cosmetical change
agreeLen x = (((length . takeWhile id) .) . zipWith (==)) x
agreeLen = ((length . takeWhile id) .) . zipWith (==)

It is not something you'd actually want to do in practice, but it is certainly possible.

like image 88
duplode Avatar answered Dec 05 '22 02:12

duplode