How do I re-write a Haskell function of two argument to point-free style

£可爱£侵袭症+ 提交于 2019-11-28 17:53:55

and also to prefer pointfree code where possible.

Not "where possible", but "where it improves readability (or has other manifest advantages)".

To point-free your

agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

A first step would be to move the ($) right, and replace the one you have with a (.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y

Now, you can move it even further right:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y

and there you can immediately chop off one argument,

agreeLen x = length . takeWhile (uncurry (==)) . zip x

Then you can rewrite that as a prefix application of the composition operator,

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)

and you can write

f (g x)

as

f . g $ x

generally, here with

f = (.) (length . takeWhile (uncurry (==)))

and g = zip, giving

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x

from which the argument x is easily removed. Then you can transform the prefix application of (.) into a section and get

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip

But, that is less readable than the original, so I don't recommend doing that except for practicing the transformation of expressions into point-free style.

You could also use:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y

Idiomatic Haskell is whatever is easier to read, not necessarily what is most point-free.

As pointed out in Daniel's excellent answer, your problem is to compose f and g when f as one argument and g two. this could be written f ??? g with the correct operator (and with a type signature of (c -> d) -> (a -> b -> c) -> a -> b -> d. This correspond to the (.).(.) operator (see there) which is sometimes defines as .:. In that case your expression becomes

length . takeWhile (uncurry (==)) .: zip

If you are used to the .: operator, then this point free version is perfectly readable. I can also instead use (<$$$>) = fmap fmap fmap and get

length . takeWhile (uncurry (==)) <$$$> zip

Another concise, point-free solution:

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

Equivalently:

agreeLen = (.) (length . takeWhile id) . zipWith (==)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!