dcastro
dcastro

Reputation: 68730

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.

Upvotes: 3

Views: 244

Answers (1)

Bergi
Bergi

Reputation: 665130

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 =<</>>=.

Upvotes: 4

Related Questions