如何使用更高级别的类型

时间:2014-01-21 13:45:21

标签: haskell

玩教堂的数字。我遇到了无法引导高阶类型的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。实际上我是从底部开始的,但这样可能更容易出现这个问题。

4 个答案:

答案 0 :(得分:5)

bennofs是对的,你真的想在这里帮助类型检查器,特别是add,你需要在a forall a . a -> (a -> a) -> aNat实例化forall a . ... ,相同的newtype Nat' = N Nat 类型。)

一种方法是引入一个包装多态类型的新类型:

Nat

现在,您可以通过Nat'NunN之间切换,然后使用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