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中是否有更好/惯用的方法来实现它?
答案 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解释的那样,这还不够
对于f
和g
为Monad
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
)