玩教堂的数字。我遇到了无法引导高阶类型的GHC类型检查器的情况。
首先我写了一个版本,没有任何类型的签名:
module ChurchStripped where
zero z _ = z
inc n z s = s (n z s)
natInteger n = n 0 (1+)
add a b = a b inc
{-
*ChurchStripped> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero)
5
-}
mult a b = a zero (add b)
{-
*ChurchStripped> natInteger $ mult (inc $ inc zero) (inc $ inc $ inc zero)
6
-}
mult
的推断类型很糟糕,所以我尝试使用类型定义来清理类型:
module Church where
type Nat a = a -> (a -> a) -> a
zero :: Nat a
zero z _ = z
inc :: Nat a -> Nat a
inc n z s = s (n z s)
natInteger :: Nat Integer -> Integer
natInteger n = n 0 (1+)
{- `add :: Nat a -> Nat a -> Nat a` doesn't work, and working signature looks already suspicious -}
add :: Nat (Nat a) -> Nat a -> Nat a
add a b = a b inc
{-
*Church> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero)
5
-}
mult :: Nat (Nat a) -> Nat (Nat a) -> Nat a
mult a b = a zero (add b)
{-
*Church> natInteger $ mult (inc $ inc zero) (inc $ inc $ inc zero)
6
-}
它有效,但类型并不像它们那样干净。遵循我尝试的System F定义:
{-# LANGUAGE RankNTypes #-}
module SystemF where
type Nat = forall a. a -> (a -> a) -> a
zero :: Nat
zero z _ = z
inc :: Nat -> Nat
inc n z s = s (n z s)
natInteger :: Nat -> Integer
natInteger n = n 0 (1+)
{- This doesn't work anymore
add :: Nat -> Nat -> Nat
add a b = a b inc
Couldn't match type `forall a1. a1 -> (a1 -> a1) -> a1'
with `a -> (a -> a) -> a'
Expected type: (a -> (a -> a) -> a) -> a -> (a -> a) -> a
Actual type: Nat -> a -> (a -> a) -> a
In the second argument of `a', namely `inc'
In the expression: a b inc
In an equation for `add': add a b = a b inc
-}
我想应该可以用add
类型签名写Nat -> Nat -> Nat
,但我不知道如何。
P.S。实际上我是从底部开始的,但这样可能更容易出现这个问题。
答案 0 :(得分:5)
bennofs是对的,你真的想在这里帮助类型检查器,特别是add
,你需要在a
forall a . a -> (a -> a) -> a
中Nat
实例化forall a . ...
,相同的newtype Nat' = N Nat
类型。)
一种方法是引入一个包装多态类型的新类型:
Nat
现在,您可以通过Nat'
在N
和unN
之间切换,然后使用unN :: Nat' -> Nat
unN (N n) = n
newtype Nat' = N Nat
(此时值得注意的是,data Nat2 = forall a . N2 (a -> (a -> a) -> a)
与-XExistentialQuantification
的野兽不同。后者需要a
,因为它表示对Nat2
的某些特定选择,你可以制作一个a -> (a -> a) -> a
。另一方面,前者仍然说如果你有a
任意Nat'
,那么你可以制作一个Nat'
。 {1}}您需要-XRankNTypes
,但不需要存在。)
现在我们也可以inc'
增加Nat'
:
inc' :: Nat' -> Nat'
inc' (N n) = N (inc n)
我们准备添加:
add :: Nat -> Nat -> Nat
add n m = unN (n (N m) inc')
这样做的原因是因为现在不是试图说服GHC自己实例化具有多态类型n
的{{1}}类型,而是∀ a . a -> (a -> a) -> a
充当提示。
示例:
N
答案 1 :(得分:2)
我不明白RankNTypes
很好地说明为什么你的原始示例不起作用,但是如果你将Nat打包成数据类型,那么它可以工作:
{-# LANGUAGE RankNTypes #-}
module SystemF where
data Nat = Nat (forall a. (a -> (a -> a) -> a))
zero :: Nat
zero = Nat const
inc :: Nat -> Nat
inc (Nat n) = Nat $ \z s -> s $ n z s
natInteger :: Nat -> Integer
natInteger (Nat n) = n 0 (1+)
add :: Nat -> Nat -> Nat
add (Nat a) b = a b inc
现在Nat
类型是一种真正的数据类型,它有助于类型检查器,因为它不必一直处理多态类型,只有当你真正“解包”它时。
答案 2 :(得分:2)
这是我对教会数字的实现:
type Nat = forall a . (a→a) → (a→a)
zero :: Nat
zero _ = id
one :: Nat
one = id
inc :: Nat → Nat
inc a f = f . a f
add :: Nat → Nat → Nat
add a b f = (a f) . (b f)
mul :: Nat → Nat → Nat
mul a b = a . b
nat :: Integer → Nat
nat 0 = zero
nat n = inc $ nat (n-1)
unnat :: Nat → Integer
unnat f = f (+ 1) 0
使用它们更容易翻转(功能首先应用N次,其参数次数为第二次)。一切都刚刚出来,好吧,自然。
编辑:这个解决方案也是有限的,类型不正确就像原始问题一样,它会在某些时候崩溃。
答案 3 :(得分:0)
似乎通过使用Data.Proxy
,我们可以提示GHC:
{-# LANGUAGE RankNTypes #-}
import Data.Proxy
type Nat = forall a. Proxy a -> (a -> a) -> (a -> a)
zero :: Nat
zero _ _ x = x
suc :: Nat -> Nat
suc n proxy s z = n proxy s (s z)
-- add = \m n f x. m f (n f x)
add :: Nat -> Nat -> Nat
add n m proxy s z = n proxy s (m proxy s z)
-- mult = \m n f. m (n f)
mult :: Nat -> Nat -> Nat
mult m n proxy s z = m proxy (n proxy s) z
然后它有效!
λ > :t let one = suc zero in add one one
let one = suc zero in add one one :: Proxy a -> (a -> a) -> a -> a
λ > let one = suc zero in add one one Proxy (1+) 0
2
λ > :t let two = suc (suc zero) in mult two two
let two = suc (suc zero) in mult two two
:: Proxy a -> (a -> a) -> a -> a
λ > let two = suc (suc zero) in mult two two Proxy (1+) 0
4