我应该在哪里以及为什么要使用额外的空模式?
Where and Why should I use extra empty patterns?
例如:
intersectBy : (a -> a -> Bool) -> List a -> List a -> List a
intersectBy _ [] _ = []
intersectBy _ _ [] = []
intersectBy eq xs ys = [x | x <- xs, any (eq x) ys]
[]
有额外的模式,似乎在 Haskell Data.List
中使用了它们,但那是什么优化?这里与 Idris 有什么不同?
我问是因为我听说 "it will make reasoning about it more difficult" 和说我的人没有时间完全解释它。
做"reduce the proof"的功能我都怀疑能不能看懂
有人可以从 Haskell 和 Idris 的立场向我解释额外模式的政治,以便我能够理解并看到差异。
从语义上讲,模式
intersectBy _ [] _ = []
看起来多余,即使从性能的角度来看也是如此。相反,在 Haskell
intersectBy _ _ [] = []
不是多余的,否则
intersectBy (==) [0..] []
会发散,因为理解会尝试尝试所有元素 x <- [0..]
。
不过,我不确定我是否喜欢这种特殊情况。为什么我们不应该添加一个涵盖 intersectBy (==) [0..] [2]
的特例,使其 returns [2]
?此外,如果性能是问题,在许多情况下我想通过预排序使用 O(n log n) 方法,即使这不适用于无限列表并且需要 Ord a
.
@chi 解释了 _ _ []
的情况,但是 _ [] _
也有一个目的:它规定了如何 intersectBy
handles bottom
。定义如下:
λ. intersectBy undefined [] undefined
[]
λ. intersectBy (==) undefined []
*** Exception: Prelude.undefined
删除第一个模式,它变成:
λ. intersectBy undefined undefined []
[]
λ. intersectBy (==) [] undefined
*** Exception: Prelude.undefined
我不是 100% 确定这一点,但我相信在第一个模式中不 绑定 任何东西也有性能优势。最终模式将在没有 评估 eq
或 ys
的情况下为 xs == []
给出相同的结果,但 AFAIK 它仍然是 allocates stack space 他们的 thunks .
无需猜测何时可以通过 git blame
、GHC Trac 和图书馆邮件列表查找历史记录。
原来定义只是第三个方程,
intersectBy eq xs ys = [x | x <- xs, any (eq x) ys]
在https://github.com/ghc/ghc/commit/8e8128252ee5d91a73526479f01ace8ba2537667中添加了第二个方程作为strictness/performance改进,同时添加了第一个方程以使新定义始终至少与定义相同原来的。否则,intersectBy f [] _|_
将是 _|_
,而它之前是 []
。
在我看来,当前的定义现在是最懒惰的:它对任何输入都是尽可能定义的,除了必须选择是先检查左列表还是右列表是否为空。 (而且,正如我上面提到的,这个选择是为了与历史定义一致。)
在 Idris 中 有一个很大的不同:Idris 列表总是有限的!此外,Idris 是一种非常严格的(按值调用)语言,并且可以选择使用完整性检查器,因此可以合理地假设参数列表中不会隐藏任何底部。这种差异的重要性在于,这两个定义在 Idris 中比在 Haskell 中在语义上更接近相同。选择使用哪个可以基于证明函数属性的难易程度,或者可以基于简单性。
例如:
intersectBy : (a -> a -> Bool) -> List a -> List a -> List a
intersectBy _ [] _ = []
intersectBy _ _ [] = []
intersectBy eq xs ys = [x | x <- xs, any (eq x) ys]
[]
有额外的模式,似乎在 Haskell Data.List
中使用了它们,但那是什么优化?这里与 Idris 有什么不同?
我问是因为我听说 "it will make reasoning about it more difficult" 和说我的人没有时间完全解释它。
做"reduce the proof"的功能我都怀疑能不能看懂
有人可以从 Haskell 和 Idris 的立场向我解释额外模式的政治,以便我能够理解并看到差异。
从语义上讲,模式
intersectBy _ [] _ = []
看起来多余,即使从性能的角度来看也是如此。相反,在 Haskell
intersectBy _ _ [] = []
不是多余的,否则
intersectBy (==) [0..] []
会发散,因为理解会尝试尝试所有元素 x <- [0..]
。
不过,我不确定我是否喜欢这种特殊情况。为什么我们不应该添加一个涵盖 intersectBy (==) [0..] [2]
的特例,使其 returns [2]
?此外,如果性能是问题,在许多情况下我想通过预排序使用 O(n log n) 方法,即使这不适用于无限列表并且需要 Ord a
.
@chi 解释了 _ _ []
的情况,但是 _ [] _
也有一个目的:它规定了如何 intersectBy
handles bottom
。定义如下:
λ. intersectBy undefined [] undefined
[]
λ. intersectBy (==) undefined []
*** Exception: Prelude.undefined
删除第一个模式,它变成:
λ. intersectBy undefined undefined []
[]
λ. intersectBy (==) [] undefined
*** Exception: Prelude.undefined
我不是 100% 确定这一点,但我相信在第一个模式中不 绑定 任何东西也有性能优势。最终模式将在没有 评估 eq
或 ys
的情况下为 xs == []
给出相同的结果,但 AFAIK 它仍然是 allocates stack space 他们的 thunks .
无需猜测何时可以通过 git blame
、GHC Trac 和图书馆邮件列表查找历史记录。
原来定义只是第三个方程,
intersectBy eq xs ys = [x | x <- xs, any (eq x) ys]
在https://github.com/ghc/ghc/commit/8e8128252ee5d91a73526479f01ace8ba2537667中添加了第二个方程作为strictness/performance改进,同时添加了第一个方程以使新定义始终至少与定义相同原来的。否则,intersectBy f [] _|_
将是 _|_
,而它之前是 []
。
在我看来,当前的定义现在是最懒惰的:它对任何输入都是尽可能定义的,除了必须选择是先检查左列表还是右列表是否为空。 (而且,正如我上面提到的,这个选择是为了与历史定义一致。)
在 Idris 中 有一个很大的不同:Idris 列表总是有限的!此外,Idris 是一种非常严格的(按值调用)语言,并且可以选择使用完整性检查器,因此可以合理地假设参数列表中不会隐藏任何底部。这种差异的重要性在于,这两个定义在 Idris 中比在 Haskell 中在语义上更接近相同。选择使用哪个可以基于证明函数属性的难易程度,或者可以基于简单性。