实现“m (t a) -> (a -> m (t b)) -> m (t b)” 的惯用方法 [重复]

Idiomatic way to implement "m (t a) -> (a -> m (t b)) -> m (t b)" [duplicate]

提问人:mandark 提问时间:8/5/2017 更新时间:8/5/2017 访问量:128

问:

函数 () 具有以下签名: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 中包含的列表。在 hoogle 上搜索无济于事。IO of list of Integers

我正在使用以下方法来实现这一点:

假设该函数的实现是:

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 中实现这一点?

Haskell Monads 习语 io-monad

评论

0赞 mandark 8/5/2017
我不是在寻找解决方案,因为我已经有了。我想要一种惯用的方法来解决这个问题。
0赞 4castle 8/5/2017
我不知道你是否已经读过答案,但“更一般地说,什么函数的类型为 m1 m2 a -> (a -> m1 m2 b) -> m1 m2 b?”是重复问题的一部分。答案是:在一般情况下这是不可能的。请参阅第二个答案。
1赞 user2407038 8/5/2017
如果删除 和 上的类型签名并抽象为参数,则将获得类型检查器推断的最通用类型。我想说这些实现是惯用的,除了可能使用符号而不是 and,但这些纯粹是风格问题。就目前而言,这个问题完全是基于意见的——你能澄清一下你的代码不够好的地方吗,即你希望看到“更好”的版本完成什么?f'f''fdo>>=fmap

答:

1赞 rampion 8/5/2017 #1

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

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

由于您要查找的函数在 monad 时实现起来很简单:Compose f g

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

正如这个答案所解释的那样,这还不够 对于 S 和 be s,他们还需要通勤:fgMonad

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

(一般来说,f 是单子和 g可遍历是不够的)