Jon*_*her 4 polymorphism haskell types impredicativetypes rank-n-types
在 Haskell 中,如果启用RankNTypes扩展
{-# Language RankNTypes #-}
Run Code Online (Sandbox Code Playgroud)
然后可以定义在 System-F 中编码的自然数:
type Nat = forall a. a -> ((a -> a) -> a)
zero :: Nat
zero = \z s -> z
succ :: Nat -> Nat
succ n = \z s -> s (n z s)
fold :: a -> (a -> a) -> Nat -> a
fold z s n = n z s
Run Code Online (Sandbox Code Playgroud)
好极了!下一步是定义case操作:想法是
caseN :: Nat -> a -> (Nat -> a) -> a
caseN n z f = "case n of
zero -> z
succ m -> f m"
Run Code Online (Sandbox Code Playgroud)
当然,这不是直接可能的。可能的一件事是正常定义自然数{data Nats = Zero | Succ Nats}并定义Nat和之间的“转换” Nats,然后使用case内置于 Haskell的语法构造。
在无类型的 lambda 演算中,caseN可以写成
caseN n b f = snd (fold (zero, b) (\(n0, _) -> (succ n0, f n0)) n)
Run Code Online (Sandbox Code Playgroud)
遵循 Kleene 显然发现的用于定义前驱函数的技巧。这个版本的caseN看起来确实应该使用上面给出的类型进行类型检查。 (zero, b) :: (Nat, b)并且\(n0, _) -> (succ n0, f n0) :: (Nat, b) -> (Nat, b),这样fold (zero, b) (\(n0, _) -> (succ n0, f n0)) n :: (Nat, b)。
但是,这不会在 Haskell 中进行类型检查。试图内功能隔离\(n0, _) -> (succ n0, f n0)用
succf :: (Nat -> b) -> (Nat, b) -> (Nat, b)
succf f (n, _y) = (succ n, f n)
Run Code Online (Sandbox Code Playgroud)
表明ImpredicativeTypes可能需要扩展,因为succf似乎需要扩展。对于更典型的{data Nats = Zero | Succ Nats},该caseN构造确实有效(在更改为适当的fold、 和Zero、 之后Succ)。
可以直接caseN上班Nat吗?需要不同的技巧吗?
我认为典型的技巧是使用数据类型(或newtype评论者指出的)包装器。首先,Nat您可以将其定义为:
newtype Nat = Nat { unNat :: forall a. a -> ((a -> a) -> a) }
Run Code Online (Sandbox Code Playgroud)
这与您的定义同构,只是您必须显式包装和解开内容。
我们可以继续编写与您相同的定义:
zero :: Nat
zero = Nat $ \z s -> z
succ :: Nat -> Nat
succ (Nat n) = Nat $ \z s -> s (n z s)
fold :: a -> (a -> a) -> Nat -> a
fold z s (Nat n) = n z s
Run Code Online (Sandbox Code Playgroud)
这基本上是您已经拥有的,但现在使用显式包装和解包Nat(作为构造函数和模式)。
此时,您的最终定义才有效:
caseN :: Nat -> b -> (Nat -> b) -> b
caseN n b f = snd (fold (zero,b) (\(n0,_) -> (succ n0,f n0)) n)
succf :: (Nat -> b) -> (Nat, b) -> (Nat, b)
succf f (n,_y) = (succ n, f n)
Run Code Online (Sandbox Code Playgroud)