订购Type Level Naturals

时间:2014-09-25 22:03:47

标签: haskell types

我一直在尝试使用DataKinds扩展名在Haskell中实现类型级自然。到目前为止,我的代码看起来像这样:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}

-- the kind of natural numbers
data Nat = Zero | Succ Nat

-- type synonyms for the naturals
type N0 = Zero
type N1 = Succ N0
type N2 = Succ N1
-- ...

-- singleton natural type
data SNat (n :: Nat) where
    SZero :: SNat Zero
    SSucc :: SNat n -> SNat (Succ n)

-- type level lesser-than operator
type family (<) (x :: Nat) (y :: Nat) :: Bool where
    x < Zero = False
    Zero < y = True
    (Succ x) < (Succ y) = x < y

-- type level addition operator
type family (+) (x :: Nat) (y :: Nat) :: Nat where
    Zero + y = y
    x + Zero = x
    (Succ x) + y = Succ (x + y)
    x + (Succ y) = Succ (x + y)

infixr 5 :::
-- type of vectors with a certain length
data Vector (n :: Nat) a where
    Nil :: Vector N0 a
    (:::) :: a -> Vector n a -> Vector (Succ n) a

-- indexing operator
(!) :: ((k < n) ~ True) => Vector n a -> SNat k -> a
(x ::: _) ! SZero = x
(_ ::: xs) ! (SSucc n) = xs ! n

此代码编译正常并按预期工作(在预期时也会出现类型错误)。

> (1 ::: 2 ::: 3 ::: Nil) ! (SSucc SZero)
2
> (1 ::: Nil) ! (SSucc SZero)
Couldn't match type 'False with 'True....

但是,如果我从上面改变其中一行:

(:::) :: a -> Vector n a -> Vector (Succ n) a

对此:

(:::) :: a -> Vector n a -> Vector (n + N1) a

该文件突然无法编译:

Could not deduce ((n2 < n1) ~ 'True)
from the context ((k < n) ~ 'True)
  bound by the type signature for
             (!) :: (k < n) ~ 'True => Vector n a -> SNat k -> a
  at question.hs:41:8-52
or from (n ~ (n1 + N1))
  bound by a pattern with constructor
             ::: :: forall a (n :: Nat). a -> Vector n a -> Vector (n + N1) a,
           in an equation for ‘!’
  at question.hs:43:2-9
or from (k ~ 'Succ n2)
  bound by a pattern with constructor
             SSucc :: forall (n :: Nat). SNat n -> SNat ('Succ n),
           in an equation for ‘!’
  at question.hs:43:15-21
Relevant bindings include
  n :: SNat n2 (bound at question.hs:43:21)
  xs :: Vector n1 a (bound at question.hs:43:8)
In the expression: xs ! n
In an equation for ‘!’: (_ ::: xs) ! (SSucc n) = xs ! n

为什么Haskell能够推导n < Succ n而不是n < n + N1?在这种情况下,如何使我的类型函数正常运行? (我不想使用unsafeCoerce)。

1 个答案:

答案 0 :(得分:7)

您可以通过缩小(+)类型系列的定义来使用更改的类型签名进行编译:

-- type level addition operator
type family (+) (x :: Nat) (y :: Nat) :: Nat where
    -- Zero + y = y
    x + Zero = x
    -- (Succ x) + y = Succ (x + y)
    x + (Succ y) = Succ (x + y)

根据您的原始定义,(n + N1)无法减少,因为GHC不知道它可以应用哪个方程式;第一个可能适用,具体取决于nZero

使用缩减版本清除(在将N1减少到其定义之后)只能应用x + Succ y规则,因此GHC可以将您的新类型减少到原始版本之一。

通过第一个参数的案例分析来定义像(+)这样的操作实际上更为正常,而不是像我在这里做的那样。这样可以使(N1 + n)之类的工作正常而非(n + N1)。但是我认为这只是一个惯例,而不是具有任何特定的优势。

通过同时拥有两组定义,您通常会遇到两个世界中最糟糕的定义。