Haskell类型级别lambda演算错误(或缺少该错误)

Haskell类型级别lambda演算错误(或缺少该错误),haskell,ghc,ghci,Haskell,Ghc,Ghci,我一直在阅读Haskell wiki上的页面,在类型系统中嵌入的lambda演算部分遇到了一些问题。从那一节中,我得出结论,下面的代码不应该与GHC/GHCi一起工作——显然GHC不应该能够确定g的类型签名 {-# OPTIONS -fglasgow-exts #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} data X data App t u data Lam t class Subs

我一直在阅读Haskell wiki上的页面,在类型系统中嵌入的lambda演算部分遇到了一些问题。从那一节中,我得出结论,下面的代码不应该与GHC/GHCi一起工作——显然GHC不应该能够确定g的类型签名

{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}

data X
data App t u
data Lam t

class Subst s t u | s t -> u
instance Subst X u u
instance (Subst s u s', Subst t u t') => Subst (App s t) u (App s' t')
instance Subst (Lam t) u (Lam t)

class Apply s t u | s t -> u
instance (Subst s t u, Eval u u') => Apply (Lam s) t u'

class Eval t u | t -> u
instance Eval X X
instance Eval (Lam t) (Lam t)
instance (Eval s s', Apply s' t u) => Eval (App s t) u

f :: Eval (App (Lam X) X) u => u
f = undefined
g :: Eval (App (Lam (App X X )) (Lam (App X X ))) u => u
g = undefined
请注意,我必须添加flexiblecontext和undedicatableinstances,因为没有这些扩展,给定的代码似乎无法编译。但是,当我使用GHCi(版本8.0.2)运行此操作时,会得到以下结果:

*Main> :t f
f :: X
*Main> :t g
g :: u

这对我来说特别奇怪,因为类型u在任何地方都没有定义。这是上面两种语言扩展相互作用和格拉斯哥exts的结果吗?如果是,怎么做?

类型
u
只是一个单独的类型变量——就像
undefined::a
中的
a
一样

将其归结为其最基本的内容,请考虑此备用文件:

{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}

class Loopy a
instance Loopy a => Loopy a

x :: Loopy a => a
x = undefined
如果您在ghci中询问
x
的类型,它会告诉您它有
a
类型,没有上下文。这似乎有点不可思议;您只需认识到GHC中的实例解析完全可以是递归的,并且实现将竭尽全力支持这一点

如果您愿意,我们可以详细介绍您的示例中发生的情况,但它与上面的文件非常类似。详情如下

所以,我们想看看是否允许我们拥有这个实例:

Eval (App (Lam (App X X)) (Lam (App X X))) u
我们知道

instance (Eval s s', Apply s' t u) => Eval (App s t) u
因此,只要我们同时拥有这两项,我们就可以拥有它:

Eval (Lam (App X X)) s'
Apply s' (Lam (App X X)) u
第一个很简单,因为:

instance Eval (Lam t) (Lam t)
因此,当我们有以下条件时,我们可以吃蛋糕:

Apply (Lam (App X X)) (Lam (App X X)) u

为了找到我们的蛋糕,我们应该在这两块石头下面检查一下:

Subst (App X X) (Lam (App X X)) u
Eval u u'

我们学会了什么时候吃蛋糕

Subst X (Lam (App X X)) s'
Subst X (Lam (App X X)) t'
Eval (App s' t') u'
这很容易取得进展,因为:

instance Subst X u u
因此,无论何时,我们都可以使用原始实例:

Eval (App (Lam (App X X)) (Lam (App X X))) u'

但是,嘿,普雷斯托!这是我们正在寻找的原始实例。总之,只要我们有原始实例,我们就可以有原始实例。所以我们声明我们可以拥有我们的原始实例,然后我们可以拥有我们的原始实例!那不是很有趣吗。

那当然很有趣。我本以为GHC会继续计算表达式,但我想不会。那么,这是因为不可判定的事件吗?另外,当我试图运行您的备用文件时,ghci声称我需要FlexibleInstances和AllowambigUstypes。@InThisStyle10s6p中的“这”是什么,“这是由于
不可判定实例的原因吗?”?@InThisStyle10s6p re:其他扩展名,是的,我刚刚修复了备用文件。在我的狂妄自大中,我实际上没有测试我的断言,但我现在已经这样做了。我的意思是,这种行为是由于不可判定的实例造成的吗?我之所以问这个问题,是因为类型算术页面似乎暗示GHC至少在过去的某个时候无法编译该示例。@inthistyle10s6p再说一遍,“此行为”是什么行为?(回答这个问题意味着不仅要说“发生了什么”,还要说“我们正在比较什么样的行为”。)我怀疑类型算术页面是错误的,因为(和我一样)在他们的傲慢中,他们没有测试他们的断言。GHC以递归的方式进行实例解析的事实有一些非常令人惊讶的结果(比如这个)。
instance Subst X u u
Eval (App (Lam (App X X)) (Lam (App X X))) u'