Search code examples
haskellfunctional-programmingcoding-style

Mixing fmap and bind (>>=)


In Haskell, when I have to mix calls to fmap/<$> and >>=/=<<, I invariably end up with lots of parenthesis.

For example, here I'm calling listDirectoryAbs dir :: IO [String] and then chaining a series of filter and filterM, ending with a fmap.

findDlls :: String -> IO [String]
findDlls dir = f <$> (filterM predicate1 =<< (filter predicate2 <$> (filterM predicate3 =<< listDirectoryAbs dir)))

Because all these nested expressions look messy (at least to me), I end up rewriting f <$> x to return . f =<< x. If I then flip the binds, I get something more readable:

findDlls dir = listDirectoryAbs dir
 >>= filterM predicate3
 >>= return . filter predicate2
 >>= filterM predicate1
 >>= return . f

Is rewriting f <$> x -> return . f =<< x -> x >>= return . f "bad" in any way? Is there a preferable way of avoiding nested expressions in situations such as these?

I could use do notation instead, but I'd like to avoid having to be explicit about data flow and giving every return value a name.


Solution

  • I don't think it's "bad" (unless it forfeits optimisations in fmap of that particular monad), however I don't like its verbosity.

    I'd rather define my own operator for this, <$$> has been suggested or <&> would match the $/&-duality well (see also Anyone ever flip (<$>)):

    infixl 1 <&>
    (<&>) = flip (<$>)
    
    findDlls dir = listDirectoryAbs dir
     >>= filterM predicate3
     <&> filter predicate2
     >>= filterM predicate1
     <&> f
    

    Btw, if your monad is an Arrow (e.g. functions), then <<</>>> combines nicely with =<</>>=.