Search code examples
haskellpointfree

How to make this function pointfree without making the code harder to read?


I have written this function:

-- Either exact match or is suffix of ".example.com" (mind the dot)
-- This prevents for overlapping like "myexample.com"
isDomainOf :: B.ByteString -> B.ByteString -> Bool
isDomainOf a b = (a == b) || a `B.isSuffixOf` (B.append "." b)

If I apply pointless to the function I get:

isDomainOf = ap (ap . ((||) .) . (==)) ((. B.append ".") . B.isSuffixOf)

Which is obviously even longer and harder to read than the pointful version.

The problem is the or operator, because I can't just write the left and right hand sides of (||) like this:

isDomainOf = (==) || (. append ".") . isSuffixOf

Is there any better way to express this?


Solution

  • The general scheme is that you have

    foo :: A -> B -> C    -- foo = (==)                           :: BStr->BStr->Bool
    bar :: A -> B -> D    -- bar = (\a b -> a`isSuffixOf`(':':b)) :: BStr->BStr->Bool
    comb :: C -> D -> E   -- comb = (||)                          :: Bool->Bool->Bool
    

    and want to express \a b -> comb (foo a b) (bar a b). First of course it's a good idea to hoogle for that specific combinator, but it's a bit too crazy for this to expect results.

    To try making a point-free version ourselves (as bheklir remarks, it's probably a bad idea here to consider that in the first place), first observe it becomes much easier when the infixes aren't curried.

    foo' :: (A,B) -> C
    bar' :: (A,B) -> D
    

    why, if you then unify (A,B) ~ P, we simply need to put foo' and bar' in parallel and feed the results to comb. With the Arrow combinators, this looks thus:

            uncurry comb . (foo' &&& bar')
    

    which isn't too bad. Of course, writing out all with the necessary uncurrying makes it nevertheless a nightmare to read:

       curry $ uncurry (||) . (uncurry(==) &&& \(a,b) -> a`isSuffixOf`(':':b))
    

    Since we still have a lambda in there, it's still not point-free anyway; though that one can of course be unpointed as well:

       curry $ uncurry (||) . (uncurry(==) &&& uncurry(((':':).) . isSuffixOf))