函数的eta等效性是否可以与Haskell的seq操作兼容?


14

引理:假设等式我们有(\x -> ⊥) = ⊥ :: A -> B

证明:⊥ = (\x -> ⊥ x)通过η等价,并(\x -> ⊥ x) = (\x -> ⊥)通过λ下的减少。

Haskell 2010报告第6.2节seq通过两个方程式指定了该函数:

序列:: a-> b-> b
seq⊥b =⊥
seq ab = b,如果a≠⊥

然后声明“因此,⊥与\ x-> not不同,因为seq可用于区分它们。”

我的问题是,这真的是定义的结果seq吗?

隐含的说法似乎是seq将不可计算如果seq (\x -> ⊥) b = ⊥。但是我还不能证明这样的seq说法是没有争议的。在我看来,seq这既是单调的,又是连续的,这使它处于可计算的领域。

诸如seq之类的算法可能会通过枚举以starting开头的域来尝试搜索某些x位置f x ≠ ⊥而工作f。尽管这样的实现,即使有可能,一旦我们想要使seq多态成为现实,也会变得非常麻烦。

是否有证据证明不存在可计算seq的是标识(\x -> ⊥)⊥ :: A -> B?另外,有一些建筑的seq,它识别(\x -> ⊥)⊥ :: A -> B

Answers:


6

首先,让我们明确了解如何seq区分λ X λx.

bottom :: a
bottom = bottom

eta :: a -> b
eta x = bottom

-- This terminates
fortytwo = seq eta 42

-- This does not terminate
infinity = seq bottom 42

因此,它是一种实验事实,即在Haskell λ X 在操作上区别开来。这也是一个事实,而且很明显,可以计算,因为Haskell对其进行了计算。关于Haskell的更多信息。您正在询问Haskell文档的非常特殊的措词。我读这句话的意思是应该满足两个给定的方程,但是这两个方程不足以定义。这是为什么:我可以给你的(简单地键入)两种型号λ演算中是可计算的,并且满足给定的公式,但在车型之一λ X λx.seqseqseqλseqλx. 同意,而对方则不同意。

在一个简单的结构域的理论模型 -expressions在连续函数的域解释[ d ë ]我们有= λ X ,效果显着。采用有效的Scott域名或类似域名,使一切都可计算。在这样的模型中很容易定义。λ[DE]=λx.seq

我们也可以有一个模型演算中区分λ X ,那么η-规则当然不能成立。例如,我们可以通过解释域[ D E ] 中的函数来做到这一点,即具有附加底部的函数空间域。现在是,井的底部[ d ë ] ,而λ X 是它上方的元素。无法通过应用区分它们,因为它们都评估λseqλx.η[DE][DE]λx.,无论您将它们应用于什么(它们在扩展上都是相等的)。但是,我们确实拥有域之间的映射,并且alwyas可以将底部与所有其他元素区分开。seq


1
这是一个实验事实,在GHC和/或Hugs中,⊥和λx.⊥。幸运的是,Haskell未被实现定义。我的问题是建议Haskell关于seq的规格不足。
罗素·奥康纳

您能否提及“有效的Scott域”的含义,大概并不意味着偏序是可确定的。同样,STLC不是多态的,但是Haskell是。通常,Haskell在系统F或其派生之一中解释。这如何影响您的论点?
罗素·奥康纳

我的博士学位第1.1.4节 论文andrej.com/thesis/thesis.pdf对有效的Scott域进行了简短定义,这实际上是第一个免费提供的Google热门搜索。
安德烈·鲍尔

2
如果为我编写证明,您将获得Haskell 98的实现,其中eta-rule支持允许将(folder(\ ab-> fab)z xs)优化为(foldr fz xs),从而导致O渐近性能提高(n ^ 2)至O(n)(请参阅ghc.haskell.org/trac/ghc/ticket/7436)。更具说服力的是,它可以优化(NewTypeWrapper。f)中的NewTypeWrapper,而不必强制f进行eta展开,并且可以防止GHC中newTypes当前施加的一些渐近性能损失(例如,在使用folder时)。
罗素·奥康纳

1
事实上,你必须确保你的编译器总是器具。也就是说,您可能会倾向于不总是收缩,因此原则上λx 将是“有时可区分的”,这是非常危险的情况。为确保不是这种情况,您需要以一种聪明的方式来实现,该方式涉及产生无限多个进程,每个进程都将您的函数应用于基本元素。如果任何过程终止,则可以继续进行。看看我们是否可以按顺序执行此操作将很有趣。嗯 λx.λx.seqseq
安德烈·鲍尔

2

请注意,seq引用的规范不是其定义。引用Haskell报告“函数seq 由等式定义:[然后是您提供的等式]”。

建议的论点似乎是,如果seq(\ x->⊥)b =⊥,则seq将不可计算。

这种行为将违反的规范seq

重要的是,由于seq是多态的,seq因此无法根据两个参数中的任何一个的解构函数(投影/模式匹配等)进行定义。

是否有证据表明没有可计算的seq用⊥:: A-> B来标识(\ x->⊥)?

如果为seq' (\x -> ⊥) b,则可能会认为我们可以将第一个参数(这是一个函数)应用于某个值,然后得出。但是,由于其参数多态类型,seq因此永远无法使用函数值来识别第一个参数(即使它碰巧是一个用于seq的参数)。参数化意味着我们对参数一无所知。而且,seq永远不能采用表达式来决定“这是⊥吗?” (参见停顿问题),seq只能尝试对其进行评估,而其自身会偏离⊥。

什么seq是求值第一个参数(不是完全,而是“弱头范式” [1],即最顶层的构造函数),然后返回第二个参数。如果第一个参数恰好是(即非终止计算),则对其求值会导致seq非终止,因此seq ⊥ a = ⊥

[1] seq存在下的自由定理-Johann,Voigtlander http://www.iai.uni-bonn.de/~jv/p76-voigtlaender.pdf


我为seq给出的规范是seq的定义,因为这正是Haskell 2010报告在6.2节中所说的。Haskell 2010报告不支持您对seq的操作定义:“头普通形式”一词在完全不同的上下文中仅在报告中出现一次。这也与我的理解不一致,GHC通常会在第一个参数之前将第二个参数还原为seq,否则第一个参数根本不会被还原,因为严格性分析器已经证明它不是静态的。
罗素·奥康纳

参数性并没有直接说我们不能应用任何解构函数,也没有说我们永远不能用函数值识别第一个参数。对于具有定点的多态Lambda演算,所有parametercity都说seq可以吸收严格的函数,或更普遍地说,对于包含seq的项,某些严格的关系成立。我承认参数化可以用来证明(\ x->⊥)≠是合理的。⊥,但我想看到一个严格的证明。
罗素·奥康纳

对于函数f : forall a . a -> TT其他类型),则f不能将任何解构函数应用于其第一个参数,因为它不知道要应用哪个解构函数。我们不能对类型做一个“案例”。我已尝试改善上述答案(包括引用有关seq评估正常头部形式的信息)。
dorchard

如果有时间的话,我可以稍后尝试进行严格的证明(使用雷诺风格的关系可能是个好方法)。
dorchard

@ RussellO'Connor:seq的描述与这些行为并非“不一致”,它只是一个操作规范(行为是优化,不会改变最终结果)。
Blaisorblade

2

λx.λx.

萨姆森·阿布拉姆斯基(Samson Abramsky)很久以前就考虑过这个问题,并撰写了一篇名为《The Lazy Lambda Calculus》的论文。因此,如果您需要正式定义,则可能会在这里查找。


1
显然,这些细节仅通过将其简化为“ Haskell内核”来定义。它在哪里定义?报告说,以秒为单位。1.2:“尽管未正式指定内核,但它本质上是lambda演算的略加糖的变体,具有直接的指称语义。在介绍语法时,给出了每个语法结构到内核的转换。”
Blaisorblade 2014年

Haskell 2010报告同样如此,令人惊讶。
Blaisorblade

感谢您对Abramsky的引用!我略读了一下,看它如何回答这个问题,然后我想到
Blaisorblade 2014年

2

证明λx。Ω≠Ω中是阿布拉姆斯基给他的懒惰演算理论(第2页的目标之一他的论文,由乌代雷迪已经引用),因为他们都处于弱势头部正常形态。从定义2.7开始,他明确讨论了eta约简λx。M x→M通常不是有效的,但是如果M在所有环境中都终止,则有可能。这并不意味着M必须是一个总函数-只是求值M必须终止(例如,通过减小为lambda)。

您的问题似乎是由实际问题(性能)引起的。但是,即使Haskell报告可能还不够完全清楚,但我怀疑是否等于λx。与‌配合使用将产生Haskell的有用实现;是否实施Haskell '98尚有待商but,但鉴于此,作者显然希望如此。

最后,seq如何为任意输入类型生成元素?(我知道QuickCheck为此定义了Arbitrary类型类,但是您不允许在此处添加此类约束)。这违反了参数化。

更新:我没有设法对此进行编码(因为我不太熟悉Haskel),并且解决此问题似乎需要嵌套runST区域。我尝试使用单个参考单元(在ST monad中)保存这样的任意元素,以后再读取它们,并使其普遍可用。参数性证明break_parametricity不能定义下方(除非返回底部,例如返回错误),但可以恢复提议的seq会生成的元素。

import Control.Monad.ST
import Data.STRef
import Data.Maybe

produce_maybe_a :: Maybe a
produce_maybe_a = runST $ do { cell <- newSTRef Nothing; (\x -> writeSTRef cell (Just x) >> return x) `seq` (readSTRef cell) }

break_parametricity :: a
break_parametricity = fromJust produce_maybe_a

我必须承认,我对此处需要的参数化证明的形式化有些模糊,但是这种非正式的参数化使用在Haskell中是标准的。但是我从德里克·德雷尔(Derek Dreyer)的著作中学到,近几年来,所需的理论正在迅速得到解决。

编辑:

  • 我什至不确定您是否需要那些针对类似于ML的语言,命令式和无类型语言进行了研究的扩展,或者经典的参数化理论是否涵盖了Haskell。
  • 另外,我之所以提到德里克·德雷尔(Derek Dreyer)只是因为我后来才接触到乌代·雷迪(Uday Reddy)的作品-我是最近才从《雷诺兹的本质》中学到的。(我只是在上个月左右才真正开始阅读有关参数的文献)。

评估(\x -> writeSTRef cell (Just x) >> return x)随机输入不会执行对单元的写入。仅执行将其放入顺序传递的ST命令runST。同样,运行main = (putStrLn "Hello") `seq` (return ())不会在显示器上打印任何内容。
Russell O'Connor 2014年

@ RussellO'Connor当然是正确的-测试很困难,因为seq没有我们讨论的行为。但是我仍然认为生成元素本身就破坏了参数化。我将尝试修正答案以举例说明。
Blaisorblade 2014年

嗯,要想解决这个问题,显然需要嵌套runST区域,并在内部区域中使用外部区域中的单元格,但这是不允许的。
Blaisorblade 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.