【问题标题】:How to specialize mapM for IO in Haskell如何在 Haskell 中为 IO 专门化 mapM
【发布时间】:2018-07-30 00:26:49
【问题描述】:

假设我有一个任务表示从 kv 的一些计算,其中一些输入必须从外部获取。

newtype Task k v = Task { run ∷ ∀ f. Monad f ⇒ (k → f v) → f v }

对于某些任务,将使用mapM,例如获取多个密钥。我想将mapM 专门用于一些单子。特别是对于 IO monad,我想使用 Control.Concurrent.Async.mapConcurrently 来同时执行 IO 操作。

我的第一反应是引入一个包装器类型

newtype AsyncIO a = AsyncIO { io :: IO a }

然后介绍

instance Monad AsyncIO

但这不起作用,因为在当前的 GHC 实现中,mapM 是根据 traverse 定义的,它位于 Traversable 中。

有没有一个优雅的解决方案?

【问题讨论】:

  • 我不太明白您所说的“对于某些任务将使用mapM”是什么意思。你能举一个Task的例子吗?

标签: haskell monads io-monad


【解决方案1】:

好吧,traverse 只需要一个Applicative。您可以通过使用替代Applicative for IO 使mapM 并行执行其操作(这就是mapConcurrently 的实现方式)。但是,这个Applicative 没有合法的Monad 实例:(>>=) 和其他Monad 操作将与(<*>) 和其他Applicative 操作不一致。例如。 mf >>= \f -> mx >>= \x -> return (f x) 将不等同于 mf <*> mx,因为 (>>=) 不能并行执行其参数,但 (<*>) 会。 (您可能可以使用unsafeInterleaveIO 创建一个工作的Monad 实例,但是,好吧,unsafeInterleaveIO。)

您可以做的一件事是传递Tasks 和Applicative 函子,与Monad 分开,然后提供自然转换以将前者中的每个计算注入到后者中。查找函数也应该在Applicative 上下文中。

newtype Task k v = Task { run ∷ ∀f f'. (Monad f, Applicative f')
                              ⇒ (∀a. f' a → f a)
                              → (k → f' v) → f v
                        }

如果你没有涉及到任何特殊的Applicative,只需使用id作为自然变换:

runSimple ∷ Monad f ⇒ Task k v → (k → f v) → f v
runSimple t = run t id

对于IO,特殊的Applicative 函子已经很好地打包在Control.Concurrent.Async.Concurrently 中:

runWithParallelIO ∷ Task k v → (k → IO v) → IO v
runWithParallelIO t lookup = run t runConcurrently (Concurrently . lookup)

你会这样写Task

task ∷ Task _k _v
task = Task go
  where go exec lookup = do _
                            xs <- exec $ mapM lookup _
                            _

如果您发现自己编写的 Task 根本无法从单独的 MonadApplicative 上下文中受益,您可以使用这个智能构造函数

taskSimple ∷ (∀f. Monad f ⇒ (k → f v) → f v) → Task k v
taskSimple r = Task (\exec lookup -> r (exec . lookup))

避免将每个lookup 包装在exec 中。 AFAICT、runSimple . taskSimple = idtaskSimple . runSimple 是幂等的。

【讨论】:

  • 谢谢!这正是我所需要的。我知道在 Haskell 中必须有一个优雅的高阶解决方案:)
【解决方案2】:

已运行将mapMmapConcurrently 作为附加参数,或者作为隐式参数传递的可能较少,但我不确定后者是否会与 f 的量化发生冲突。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 2021-08-03
    • 2015-06-15
    • 1970-01-01
    相关资源
    最近更新 更多