Vladyslav Babych February 2016

Cannot remove an argument (Point-free style Haskell)

I am having a problem with removing an argument 'e' in this function:

numocc e = map length . (map . filter . (==)) e

The function checks the number of occurences of an element inside a two-dimensional list (lists within list). So you call it as follows:

numocc 1 [[1, 2], [2, 3, 2, 1, 1], [3]]

Result : [1,2,0]

Argument e is on the far right on both sides of the equal sign, and is not encased in any parameters. Therefore, I should be able to remove it to get a point-free function:

numocc = map length . (map . filter . (==))

However I get this error:

Type error in application Expression : map length . map . filter . (==) Term : map length Type : [[b]] -> [Int] Does not match : ([[a]] -> [[a]]) -> [Int]

What is the problem here? Thanks.

Answers


bheklilr February 2016

Your original function is being parsed as

numocc e = (map length) . ((map . filter . (==)) e)

So you can't just remove the e since it doesn't technically appear at the end of the definition. However, if you define the operator

(.:) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(.:) = (.) . (.)

Then you can write it as

numocc = map length .: (map . filter . (==))

The parentheses on map . filter . (==) are required since you can't mix . and .: due to fixity rules.

However, at this point you're just making this function harder to read and maintain. It would be better to break it down into smaller sections and introduce some intermediate names, along with adding the right type signature and giving the function a better name to start with:

numOccurrences :: Eq a => a -> [a] -> Int
numOccurrences valueToCount values = length (filter (== valueToCount) values)

numOccurrencesInSublists :: Eq a => a -> [[a]] -> [Int]
numOccurrencesInSublists valueToCount = map (numOccurrences valueToCount)

This is far more readable, doesn't rely on fancy operators, and expresses your intent more clearly. Since the case for counting in a single list is a separate function, you can now use it elsewhere too.

Remember, elegant code isn't about making it as short as possible, it's about making it as clear as possible. Descriptive names (not necessarily long ones), explicit arguments, and small, composable units are best.

Post Status

Asked in February 2016
Viewed 2,798 times
Voted 7
Answered 1 times

Search




Leave an answer