1

The bind function (>>=) has the signature:

m a -> (a -> m b) -> m b

However, I want a function with the signature:

m (t a) -> (a -> m (t b)) -> m (t b)

Specifically, I have a function that given an Integer, it returns a list of integers in within an IO:

f :: Int -> IO [Int]

but I want to apply it to an IO of list of Integers and I can't use the regular bind function because it is wrapped in two containers i.e. a list contained in an IO. Searching on hoogle doesn't help.

I am using the following approach to implement this:

Let's say the implementation of the function is:

f :: Int -> IO [Int]
f x = do
  return $ [x-10, x+10]

I am using two helper functions to get what I want:

f' :: [Int] -> IO [Int]
f' xs = do
  list <- traverse f xs
  return $ join list

f'' :: IO [Int] -> IO [Int]
f'' xs = do
  ys <- xs
  f' ys

The above works but I would like to know if there is a better / idiomatic way to implement this in haskell?

mandark
  • 762
  • 5
  • 21
  • I am not looking for a solution since I already have that. I want an idiomatic way to solve this. – mandark Aug 04 '17 at 20:31
  • I don't know if you've read the answers yet, but *"More generally, what function has type m1 m2 a -> (a -> m1 m2 b) -> m1 m2 b?"* is part of that duplicate question. The answer: it's impossible in the general case. See the second answer. – 4castle Aug 04 '17 at 20:35
  • 1
    If you remove the type signatures on `f'` and `f''` and abstract `f` into a parameter, you'll get the most general type inferred by the typechecker. I would say these implementations are idiomatic, except perhaps the use of `do` notation instead of `>>=` and `fmap` but those are purely stylistic concerns. As it stands, this question is entirely opinion based - can you clarify what about your code is not good enough, i.e., what you'd like to see the 'better' version accomplish? – user2407038 Aug 04 '17 at 22:54

1 Answers1

1

The idiomatic solution would be to use Data.Functor.Compose:

data Compose f g a = Compose { getCompose :: f (g a) }

Since the function you're looking for is trivial to implement when Compose f g is a monad:

bibind :: Monad (Compose f g) => f (g a) -> (a -> f (g b)) -> f (g b)
bibind m h = getCompose (Compose m >>= Compose . h)

As well explained in this answer, it's not sufficient for f and g to be Monads, they also need to commute:

class Commute f g where
  commute :: g (f x) -> f (g x)

instance (Monad f, Monad g, Commute f g) => Monad (Compose f g) where
  return = Compose . return . return
  join = Compose .  fmap join .  join .  fmap commute . getCompose . fmap getCompose

(In general, it's not sufficient for f to be a monad and g to be Traversable)

rampion
  • 87,131
  • 49
  • 199
  • 315