【问题标题】:An alternative Alternative for lists列表的另一种选择
【发布时间】:2020-05-17 23:27:12
【问题描述】:

现在有几次,我发现自己在定义:

(<?>) :: [a] -> [a] -> [a]
[] <?> ys = ys
xs <?> _  = xs

当然,这是一个关联操作,空列表[] 既是左标识又是右标识。它的功能类似于 Python 的 or

在我看来,这将是一个不错的(&lt;|&gt;),比(++) 更好。选择第一个非空列表感觉更像是我对名为 Alternative 的类型类的期望,而不是连接列表。诚然,它也不适合MonadPlus,但我认为这是为救赎付出的小代价。我们在标准库中已经有了(++)(&lt;&gt;);我们需要另一个同义词,还是新功能(据我所知)会更有帮助?

我起初认为这可能是ZipList 的一个很好的Alternative 实例,但this answer 之后关于相关问题的讨论让我相信了。除了向后兼容和保持MonadPlus 合理之外,当前实例而不是这个新实例还有哪些论据?

【问题讨论】:

  • 问题是它是否满足some v = (:) &lt;$&gt; v &lt;*&gt; many vmany v = some v &lt;|&gt; pure [] 定律(有一定的&lt;$&gt;&lt;*&gt; 因此应该满足函子和应用等定律)。我不相信。你当然不需要使用[]上定义的&lt;*&gt;(你可以使用另一个),但它仍然必须是合法的。
  • @WillemVanOnsem 来自here,在我看来somemany 的定义方式可以保证这一点。
  • 同义词很好:您可以使用首先想到的任何一个而不会弄错。使(&lt;|&gt;) 不同于mplus(++)(&lt;&gt;) 不会有帮助。这将是灾难性的。
  • 正如您在我已删除的帖子中评论的那样,ZipList 确实有一个不同的Alternative,它不符合您的定义。这让我觉得somemany 与此有关。您的定义确实符合Monoid 法律,这显然是唯一重要的en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus
  • 您提出的是 Prolog 的“承诺选择”。参看。 condAhere(coundA (g) (f)) x = (g ~~&gt; true ||~ f) x == g x &lt;?&gt; f x。当然++ 是常规的非确定性选择(condE (g) (f)) x = (g ||: f) x == g x ++ f x(使用引用答案中的运算符)。还有两个“条件”,即那里的选择操作,condUcondI,等效地表示为 a +! b == take 1 a &lt;?&gt; ba +/ b == concat $ transpose [a,b]。最后一个令人回味的 zipping,即 ZipList 也以某种方式与这一切联系在一起。

标签: list haskell applicative alternative-functor monadplus


【解决方案1】:

很难直接回答您的问题。孤立地考虑,您提出的实例没有根本性的错误。尽管如此,仍有很多东西可以支持现有的Alternative 列表实例。


诚然,它也不适合MonadPlus,但我认为这是为救赎付出的小小代价。

沿着这条路线走的一个问题是,Alternative 旨在捕捉与MonadPlus 相同的一般概念,但就Applicative 而不是Monad 而言。引用a relevant answer by Edward Kmett:

实际上,AlternativeApplicativeMonadPlusMonad

从这个角度来看,AlternativeMonadPlus 实例不匹配会造成混淆和误导,就像 ApplicativeMonad 实例的类似情况一样。

(对此论点的一个可能的反驳是想知道为什么我们仍然需要关心MonadPlus,因为它表达了相同的概念并提供了与Alternative 基本相同的方法。但应该注意的是, MonadPlus 法则比 Alternative 法则更强,因为它的方法与 Monad 的相关交互不能用 Alternative 来表达。既然如此,MonadPlus 仍然具有以下含义它自己的,并且可以想象的类改革的结果将保留它作为一个仅限法律的类,例如在this answer by Antal Spector-Zabusky的最后一节中讨论的那样。)

鉴于这些考虑,在下文中我将假设MonadPlus 的持续相关性。这使得编写此答案的其余部分变得更加容易,因为 MonadPlus 是 Haskell 中一般概念的原始表达,因此在跟踪 Alternative 的列表实例的起源时非常有必要引用它。


在我看来,这将是一个不错的(&lt;|&gt;),比(++) 更好。选择第一个非空列表感觉更像是我对名为 Alternative 的类型类的期望,而不是串联列表。

不过,追查MonadPlusAlternative 的根源表明,串联列表实例不仅是完善的,而且是典型的。例如,引用 Hutton 和 Meijer 的经典论文,Monadic parsing in Haskell (1998),p。 4:

也就是说,类型构造函数m是类MonadZero的成员,如果它是类Monad的成员,并且它还配备了指定类型的值zero。以类似的方式,MonadPlus 类通过添加指定类型的 (++) 操作来构建在 MonadZero 类之上。

(请注意,作者确实使用(++) 作为mplus 的名称。)

mplus 在这里捕获的概念是非确定性选择:如果计算 uv 都有一些可能的结果,那么 u `mplus` v 的可能结果将是 @987654375 的所有可能结果@ 和 v。最基本的实现是通过MonadPlus 用于列表,尽管这个想法扩展到涵盖其他非确定性单子,例如 Hutton 和 Meijer 的Parser

newtype Parser a = Parser (String -> [(a,String)])

换一种说法,我们可以将非确定性选择描述为包容性析取,而您提出的操作是(左偏)排他性选择的一种形式。 (值得注意的是,Hutton 和 Meijer 还为他们的 Parser 定义了一个确定性选择运算符 (+++),这与您的运算符很像,只是它只选择第一个成功计算的第一个结果。)

进一步的相关观察:transformers 中没有 mtl 类对应项的 monad 转换器之一是 ListT。之所以如此,是因为概括ListT 功能的类正是MonadPlus。引用a Gabriella Gonzalez comment:

MonadPlus 基本上是“list monad”类型的类。例如:cons a as = return a `mplus` asnil = mzero

请注意,transformers'ListT 的损坏不是问题。一般来说,ListT-done-right 的各种表述都配备了一个串联的MonadPlus 实例(例如:onetwothree)。


我们可能希望保留Alternative []MonadPlus [] 实例的原因如此之多。尽管如此,如果它没有认识到 as Will Ness reminds us 存在多种合理的选择概念,并且您的运营商体现了其中之一,那么这个答案将是缺乏的。

AlternativeMonadPlus 的“官方”法律(即文档中实际提到的法律)没有指定单一的选择概念。既然如此,我们最终会在同一 Alternative/MonadPlus 保护伞下得到非确定性(例如mplus @[])和确定性(例如mplus @Maybe)选择实例。此外,如果有人选择无视我上面的论点并将mplus @[] 替换为您的运营商,那么“官方”法律中的任何内容都不会阻止他们。多年来,有一些关于reforming MonadPlus 的讨论,通过将其划分为具有额外规则的类,以区分不同的选择概念。不过,这种改革实际发生的可能性似乎并不高(大量流失而实际收益相对较小)。

为了对比,有趣的是考虑近乎半透明的解释,这是对 MonadPlusAlternative 的重新想象之一,可能会在假设的类层次结构改革中被调用。有关它的完整描述,请参阅 Rivas、Jaskelioff 和 Schrijvers,A Unified View of Monadic and Applicative Non-determinism (2018)。对于我们目前的目的,只需注意解释通过在幺半群定律中添加Alternative 的“左零”和“左分布”定律来调整类以进行非确定性选择...

empty <*> x = empty
(f <|> g) <*> x = (f <*> x) <|> (g <*> x)

...以及MonadPlus:

mzero >>= k = mzero
(m1 `mplus` m2) >>= k = (m1 >>= k) `mplus` (m2 >>= k)

(那些MonadPlus 的法律比Alternative 的法律严格。)

特别是,您的选择运算符遵循所谓的Alternative 左分布规律,但不是MonadPlus 之一。在这方面,它类似于mplus @MaybeMonadPlus 左侧分布使得很难(可能不可能,尽管我现在手头没有证据)放弃任何结果在 mplus,因为我们无法判断,在法律的右侧,如果不检查 m1m2 的结果,m1 &gt;&gt;= km2 &gt;&gt;= k 是否会失败。为了用有形的东西来结束这个答案,这里是这一点的演示:

-- Your operator.
(<?>) :: [a] -> [a] -> [a]
[] <?> ys = ys
xs <?> _  = xs

filter' :: (a -> Bool) -> [a] -> [a]
filter' p xs = xs >>= \x -> if p x then [x] else []
-- If MonadPlus left distribution holds, then:
-- filter' p (xs `mplus` ys) = filter' p xs `mplus` filter' p ys
GHCi> filter' even ([1,3,5] <|> [0,2,4])
[0,2,4]
GHCi> filter' even [1,3,5] <|> filter' even [0,2,4]
[0,2,4]
GHCi> filter' even ([1,3,5] <?> [0,2,4])
[]
GHCi> filter' even [1,3,5] <?> filter' even [0,2,4]
[0,2,4]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多