【问题标题】:Idiomatic way to implement "m (t a) -> (a -> m (t b)) -> m (t b)" [duplicate]实现“m (t a) -> (a -> m (t b)) -> m (t b)”的惯用方式
【发布时间】:2018-01-12 20:12:15
【问题描述】:

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

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

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

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

具体来说,我有一个给定整数的函数,它返回 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 中实现它?

【问题讨论】:

  • 我不是在寻找解决方案,因为我已经有了。我想要一种惯用的方法来解决这个问题。
  • 我不知道您是否已经阅读了答案,但是 "更一般地说,什么函数的类型为 m1 m2 a -> (a -> m1 m2 b) -> m1 m2 b?" 是该重复问题的一部分。答案是:一般情况下是不可能的。见第二个答案。
  • 如果您删除f'f'' 上的类型签名并将f 抽象为参数,您将获得类型检查器推断出的最通用类型。我想说这些实现是惯用的,除了可能使用do 表示法而不是&gt;&gt;=fmap,但这些纯粹是文体问题。就目前而言,这个问题完全是基于意见的——你能澄清一下你的代码不够好吗,即你希望看到“更好”的版本完成什么?

标签: haskell monads idioms io-monad


【解决方案1】:

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

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

因为当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)

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

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

【讨论】:

    猜你喜欢
    • 2012-04-07
    • 2022-10-13
    • 2015-01-31
    • 2012-07-29
    • 1970-01-01
    • 2014-03-23
    • 2021-08-30
    • 1970-01-01
    • 2021-10-15
    相关资源
    最近更新 更多