用户 'singpolyma' asked on reddit 如果底层有一些通用结构:

data FailList a e = Done | Next a (FailList a e) | Fail e

有人建议使用免费的 monad,但我想知道是否可以通过 applicative functors 更普遍地建模。在 Abstracting with Applicatives 中,Bazerman 向我们展示了两个应用仿函数的和也是一个应用仿函数,有向左/向右的偏置,前提是我们在偏置方向上有一个自然变换。这听起来像是我们需要的!因此,我开始了我的提议,但很快就遇到了问题。任何人都可以看到这些问题的解决方案吗?:

首先,我们从两个仿函数之和的定义开始。我从这里开始是因为我们想要对和类型进行建模 - 成功或成功和失败。
data Sum f g a = InL (f a) | InR (g a)

我们想要使用的两个仿函数是:
data Success a = Success [a]
data Failure e a = Failure e [a]
Success 是直截了当的 - 它本质上是 Const [a] 。但是, Failure e 我不太确定。它不是一个应用仿函数,因为 pure 没有任何定义。然而,它是 Apply 的一个实例:
instance Functor Success where
  fmap f (Success a) = Success a

instance Functor (Failure e) where
  fmap f (Failure e a) = Failure e a

instance Apply (Failure e) where
  (Failure e a) <.> (Failure _ b) = Failure e a

instance Apply Success where
  (Success a) <.> (Success b) = Success (a <> b)

instance Applicative Success where
  pure = const (Success [])
  a <*> b = a <.> b

接下来,我们可以定义这些仿函数的总和,从右到左的自然变换(所以是左偏置):
instance (Apply f, Apply g, Applicative g, Natural g f) => Applicative (Sum f g) where
  pure x = InR $ pure x
  (InL f) <*> (InL x) = InL (f <*> x)
  (InR g) <*> (InR y) = InR (g <*> y)
  (InL f) <*> (InR x) = InL (f <.> eta x)
  (InR g) <*> (InL x) = InL (eta g <.> x)

我们现在唯一要做的就是定义我们的自然转变,这就是一切崩溃的地方。
instance Natural Success (Failure e) where
  eta (Success a) = Failure ???? a

无法创建 Failure 似乎是问题所在。此外,即使是 hacky 并使用 ⊥ 也不是一种选择,因为在您有 InR (Success ...) <*> InL (Failure ...) 的情况下,这将被评估。

我觉得我错过了一些东西,但我不知道它是什么。

这能做到吗?

最佳答案

我很确定“正确”的答案是使 e 成为幺半群,就像你不喜欢 reddit 讨论中的想法一样。

考虑 Failure "oops" [(*1),(*2),(*3)] <*> Failure "doh" [1,2,3] 结果是否应该将“oops”或“doh”作为失败?通过使 e 成为幺半群,我们捕捉到没有规范选择的事实,并让消费者选择他们的毒药(无论是 FirstLast 、 0x2513412 )。

请注意,此解决方案与 [] 表示非常相似,无法正确处理流/潜在无限数据,因为它对结束列表是否失败很严格。

根据后续帖子( http://comonad.com/reader/2013/algebras-of-applicatives/ ),不同的编码将使用应用程序的固定点。

然后你采用那里显示的列表表示( (Maybe e, [a]) )并通过将你的错误幺半群粘贴在单位位置来改变它,以获得以下内容:
FixF (ProductF Embed (Sum (Const ()))) a
请注意,您可以使用 Monid mon => FixF (ProductF Embed (Sum (Const mon))) a 而不是幺半群来获得 Maybe ,但就像使用 FailList 一样,除非您以正确的方式组合一个错误,否则您将无法免费获得应用实例。

另请注意,使用固定点方法,如果我们有等价于 FailList 的值,那么我们会返回一个带有三个元素的 Success [(*1),(*2),(*3)] <*> Failure "doh" [1,2,3,4,5](即我们在失败时确实不严格),而在您建议的方法中,我们会返回一个带有三个元素的 0x25181223134314来自五元素失败列表。这就是流与严格之间的权衡。

最后,也是最直接的,我们可以使用 Success 来使用标准的应用机制并获得非常接近原始数据类型的东西。

关于haskell - 我可以通过应用仿函数的组合对短路失败的成功列表进行建模吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15460794/

10-13 00:58