有没有通用的方法将失败monad上的免费comonad分解为“值流和最终错误”?

时间:2016-12-22 12:03:21

标签: haskell error-handling monads comonad

Cofree comonad对于以错误类型的多态方式迭代部分函数非常有用。它的coiter类似于forM - 在一个错误monad中循环,但它以纯/懒的方式收集生成的值,你只在数据结构中向下看到一个错误。

例如,Cofree Identity(不允许失败!)是无限流,而Cofree MaybeNonEmpty同构,而Cofree (Either e) a基本上是(NonEmpty a, e) (成功迭代值列表加上最后发生的错误)。

现在我想知道在单个错误monad上没有特定模式匹配的情况下,评估结果的最佳方法是什么。由于Foldable实例(例如toList),提取所有非常容易,但我不确定如何最好地掌握错误< / em>的。有可能利用Foldable来获取值的 rid 并留下错误部分:

vals'n'err :: (Monad m, Foldable m)
          => Cofree m a -> (NonEmpty a, (m ()))
vals'n'err (a :< q) = case toList q of
        [] -> (a:|[], const () <$> q)
        l  -> first (pure a<>)
           $ foldr1 (\(bs,e) (cs,f) -> (bs<>cs, e>>f)) $ vals'n'err<$>l

但这感觉有点hackish。有更好的解决方案吗?

1 个答案:

答案 0 :(得分:4)

我认为对于大型流来说这是一个糟糕的转换,因为懒惰会导致空间泄漏。但对于小流,它可能是可用的。

我们可以把这个转变分为两个:

vals :: Foldable f => Cofree f a -> NonEmpty a
vals = NonEmpty.fromList . Foldable.toList

err :: Monad m => Cofree m a -> m b
err (_ :< m) = m >>= err

然后结合在一起:

vals'n'err :: (Monad m, Foldable m) => Cofree m a -> (NonEmpty a, m b)
vals'n'err w = (vals w, err w)