所以我考虑了一下,并取得了一些进展。这Set : Set
是以组合方式编码Martin-Löf令人愉快的简单(但不一致)系统的第一步。这不是完成的好方法,但这是最容易上手的地方。这种类型理论的语法只是带有类型注释,Pi类型和Universe集的Lambda微积分。
目标类型理论
为了完整起见,我将介绍规则。上下文有效性仅表示您可以通过邻接居住在Set
s的新鲜变量来从空构建上下文。
G |- valid G |- S : Set
. |- valid G, x:S |- valid
现在我们可以说如何在任何给定的上下文中综合术语的类型,以及如何根据事物所包含的术语的计算行为来改变某种事物的类型。
G |- valid G |- S : Set G |- T : Pi S \ x:S -> Set
G |- Set : Set G |- Pi S T : Set
G |- S : Set G, x:S |- t : T x G |- f : Pi S T G |- s : S
G |- \ x:S -> t : Pi S T G |- f s : T s
G |- valid G |- s : S G |- T : Set
G |- x : S G |- s : T
与原始版本相比,我做了一个很小的改动,使lambda成为唯一的绑定运算符,因此Pi的第二个参数应该是一个函数,该函数计算返回类型取决于输入的方式。按照惯例(例如,在Agda中,但不幸的是在Haskell中不是),lambda的范围尽可能向右扩展,因此当抽象是高阶运算符的最后一个参数时,您通常可以不加括号:与Pi。您的Agda类型(x : S) -> T
变为Pi S \ x:S -> T
。
(Digression。如果您希望能够综合抽象类型,则必须在lambda上添加类型注释。如果您将类型检查转换为操作方式,则仍然需要注释来检查beta-redex (\ x -> t) s
,因为您无法我要建议现代设计师检查类型,并从语法中排除beta-redexes。)
(离题。该系统不一致,因为Set:Set
允许对各种“骗子悖论”进行编码。当马丁·洛夫提出这一理论时,吉拉德在他自己的不一致的系统U中向他发送了对它的编码。我们知道的最近的有毒建筑。)
组合语法和归一化
无论如何,我们有两个额外的符号Pi和Set,所以我们也许可以使用S,K和两个额外的符号来管理组合转换:我为宇宙选择了U,为乘积选择了P。
现在我们可以定义无类型的组合语法(带有自由变量):
data SKUP = S | K | U | P deriving (Show, Eq)
data Unty a
= C SKUP
| Unty a :. Unty a
| V a
deriving (Functor, Eq)
infixl 4 :.
请注意,我a
在此语法中包括了包含由类型表示的自由变量的方法。除了反映自己的意思(每个值得称呼的语法都是带有return
嵌入变量和>>=
置换替换的免费monad )之外,在转换术语并绑定到其组合形式的过程中,还很容易代表中间阶段。
这是标准化:
norm :: Unty a -> Unty a
norm (f :. a) = norm f $. a
norm c = c
($.) :: Unty a -> Unty a -> Unty a
C S :. f :. a $. g = f $. g $. (a :. g)
C K :. a $. g = a
n $. g = n :. norm g
infixl 4 $.
(读者的一项工作是为普通形式定义一种类型,并增强这些操作的类型。)
代表类型理论
现在,我们可以为类型理论定义语法。
data Tm a
= Var a
| Lam (Tm a) (Tm (Su a))
| Tm a :$ Tm a
| Pi (Tm a) (Tm a)
| Set
deriving (Show, Functor)
infixl 4 :$
data Ze
magic :: Ze -> a
magic x = x `seq` error "Tragic!"
data Su a = Ze | Su a deriving (Show, Functor, Eq)
我以Bellegarde和Hook的方式(由Bird和Paterson推广)使用de Bruijn索引表示形式。类型Su a
比包含更多元素a
,我们将其用作Ze
绑定器下的自由变量的类型,将其用作新的绑定变量,并Su x
作为旧自由变量的移位表示形式x
。
将术语翻译成组合词
完成此操作后,我们将基于括号抽象获得通常的翻译。
tm :: Tm a -> Unty a
tm (Var a) = V a
tm (Lam _ b) = bra (tm b)
tm (f :$ a) = tm f :. tm a
tm (Pi a b) = C P :. tm a :. tm b
tm Set = C U
bra :: Unty (Su a) -> Unty a
bra (V Ze) = C S :. C K :. C K
bra (V (Su x)) = C K :. V x
bra (C c) = C K :. C c
bra (f :. a) = C S :. bra f :. bra a
键入组合器
翻译显示了我们使用组合器的方式,这为我们提供了有关它们的类型应有的线索。U
并且P
只是集合构造函数,因此,编写未翻译的类型并为Pi允许使用“ Agda表示法”,我们应该
U : Set
P : (A : Set) -> (B : (a : A) -> Set) -> Set
该K
组合子被用于某种类型的值解除A
过一些其它类型的常数函数G
。
G : Set A : Set
K : (a : A) -> (g : G) -> A
该S
组合子被用来提升应用超过一个类型,在其上所有的部分可以依赖。
G : Set
A : (g : G) -> Set
B : (g : G) -> (a : A g) -> Set
S : (f : (g : G) -> (a : A g) -> B g a ) ->
(a : (g : G) -> A g ) ->
(g : G) -> B g (a g)
如果您查看的类型S
,您会发现它准确地说明了类型理论的上下文应用规则,这就是使其适合反映应用程序构造的原因。这就是它的工作!
然后我们只申请封闭物品
f : Pi A B
a : A
f a : B a
但是有一个障碍。我用普通类型理论而不是组合类型理论写了组合器的类型。幸运的是,我有一台可以翻译的机器。
组合式系统
U : U
P : PU(S(S(KP)(S(S(KP)(SKK))(S(KK)(KU))))(S(KK)(KU)))
G : U
A : U
K : P[A](S(S(KP)(K[G]))(S(KK)(K[A])))
G : U
A : P[G](KU)
B : P[G](S(S(KP)(S(K[A])(SKK)))(S(KK)(KU)))
S : P(P[G](S(S(KP)(S(K[A])(SKK)))(S(S(KS)(S(S(KS)(S(KK)(K[B])))(S(KK)(SKK))))
(S(S(KS)(KK))(KK)))))(S(S(KP)(S(S(KP)(K[G]))(S(S(KS)(S(KK)(K[A])))
(S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KP)))(S(KK)(K[G]))))
(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(S(KS)(S(KK)(KS)))
(S(S(KS)(S(KK)(KK)))(S(KK)(K[B])))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))
(S(KK)(KK))))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(KK)(KK)))
(S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))(S(KK)(KK)))))))
M : A B : U
M : B
因此,在所有难以理解的荣耀中,您都拥有了它Set:Set
!
还有一个问题。系统的语法使您无法仅凭术语猜测G
,A
和的B
参数,S
以及类似K
的。相应地,我们可以通过算法验证类型派生,但是我们不能像原始系统那样仅对组合器项进行类型检查。可能的工作是要求类型检查器的输入带有有关S和K使用的类型注释,从而有效地记录派生。但这是另一种蠕虫……
如果您足够敏锐地开始,那么这里是个好地方。其余的是“幕后”东西。
生成组合器的类型
我使用括号从相关类型理论术语中提取了这些组合类型。为了说明我是如何做到的,并且使这篇文章并非毫无意义,请允许我提供设备。
我可以编写组合器的类型,对它们的参数进行完全抽象,如下所示。我利用了方便的pil
函数,该函数结合了Pi和lambda以避免重复域类型,并且非常有帮助地允许我使用Haskell的函数空间来绑定变量。也许您几乎可以阅读以下内容!
pTy :: Tm a
pTy = fmap magic $
pil Set $ \ _A -> pil (pil _A $ \ _ -> Set) $ \ _B -> Set
kTy :: Tm a
kTy = fmap magic $
pil Set $ \ _G -> pil Set $ \ _A -> pil _A $ \ a -> pil _G $ \ g -> _A
sTy :: Tm a
sTy = fmap magic $
pil Set $ \ _G ->
pil (pil _G $ \ g -> Set) $ \ _A ->
pil (pil _G $ \ g -> pil (_A :$ g) $ \ _ -> Set) $ \ _B ->
pil (pil _G $ \ g -> pil (_A :$ g) $ \ a -> _B :$ g :$ a) $ \ f ->
pil (pil _G $ \ g -> _A :$ g) $ \ a ->
pil _G $ \ g -> _B :$ g :$ (a :$ g)
定义好这些内容后,我提取了相关的开放子术语并通过翻译对其进行了处理。
De Bruijn编码工具包
这是建造方法pil
。首先,我定义了一类Fin
用于变量的ite集合。每个这样的集合emb
在上面的集合中都有一个保留构造函数的edding,以及一个新top
元素,您可以将它们区分开:embd
函数告诉您值是否在的图像中emb
。
class Fin x where
top :: Su x
emb :: x -> Su x
embd :: Su x -> Maybe x
我们当然可以,实例Fin
为Ze
和Suc
instance Fin Ze where
top = Ze
emb = magic
embd _ = Nothing
instance Fin x => Fin (Su x) where
top = Su top
emb Ze = Ze
emb (Su x) = Su (emb x)
embd Ze = Just Ze
embd (Su x) = fmap Su (embd x)
现在,我可以使用弱化操作来定义不等值。
class (Fin x, Fin y) => Le x y where
wk :: x -> y
该wk
函数应将的元素x
作为的最大元素嵌入y
,以使其中的多余内容y
较小,从而以de Bruijn索引而言,绑定范围更广。
instance Fin y => Le Ze y where
wk = magic
instance Le x y => Le (Su x) (Su y) where
wk x = case embd x of
Nothing -> top
Just y -> emb (wk y)
一旦您解决了这些问题,剩下的工作就由排名靠前的n位行凶手完成。
lam :: forall x. Tm x -> ((forall y. Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
lam s f = Lam s (f (Var (wk (Ze :: Su x))))
pil :: forall x. Tm x -> ((forall y . Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
pil s f = Pi s (lam s f)
高阶函数不仅为您提供表示变量的术语,还为您提供了重载的东西,它在任何可见变量的范围内都可以正确表示该变量。就是说,我麻烦按类型区分不同的范围这一事实为Haskell typechecker提供了足够的信息来计算转换为de Bruijn表示形式所需的移位。为什么要养狗并自己吠叫?