惯用的实施方式" m(t a) - > (a - > m(t b)) - > m(t b)"

时间:2017-08-04 19:45:17

标签: haskell monads idiomatic io-monad

bind函数(>>=)具有签名:

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

但是,我想要一个带签名的函数:

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

具体来说,我有一个赋予Integer的函数,它返回IO中的整数列表:

f :: Int -> IO [Int]

但是我想将它应用于IO of list of Integers并且我不能使用常规绑定功能,因为它包含在两个容器中,即包含在IO中的列表。 Searching on hoogle没有帮助。

我使用以下方法来实现这个:

让我们说该功能的实现是:

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

我正在使用两个辅助函数来获得我想要的东西:

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

以上是有效的,但我想知道在haskell中是否有更好/惯用的方法来实现它?

1 个答案:

答案 0 :(得分:1)

惯用解决方案是使用Data.Functor.Compose

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

由于当Compose f g是monad时,您正在寻找的功能很难实现:

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

正如in this answer解释的那样,这还不够 对于fgMonad s,他们还需要通勤:

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

(一般来说,it's not sufficient for f to be a monad and g to be Traversable