使用类型系统检查输出长度与输入列表

时间:2017-02-09 23:29:48

标签: haskell types proof dependent-type

假设长度为 n 的列表L在列表J中交织,长度为 n + 1 。 我们想知道,对于J的每个元素,它的哪个邻居来自L是更大的。 以下函数将L作为其输入,并生成列表K,也是长度 n + 1 ,这样K的 i 元素就是J的 i 元素的理想邻居。

aux [] prev acc = prev:acc
aux (hd:tl) prev acc = aux tl hd ((max hd prev):acc)

expand row = reverse (aux row 0 [])

我可以非正式地向自己证明这个功能的结果的长度(我 最初在Ocaml中写的)比输入的长度大一个。但是我 跳到Haskell(我的新语言),因为我对自己感兴趣 能够通过类型系统证明这个不变量成立。有了。。的帮助 this previous answer,我是 能够达到以下目的:

{-# LANGUAGE GADTs, TypeOperators, TypeFamilies #-}

data Z
data S n

type family (:+:) a b :: *
type instance (:+:) Z n = n
type instance (:+:) (S m) n = S (m :+: n)

-- A List of length 'n' holding values of type 'a'
data List a n where
    Nil  :: List a Z
    Cons :: a -> List a m -> List a (S m)

aux :: List a n -> a -> List a m -> List a (n :+: (S m))
aux Nil prev acc = Cons prev acc
aux (Cons hd tl) prev acc = aux tl hd (Cons (max hd prev) acc)

但是,最后一行会产生以下错误:

* Could not deduce: (m1 :+: S (S m)) ~ S (m1 :+: S m)
  from the context: n ~ S m1
    bound by a pattern with constructor:
               Cons :: forall a m. a -> List a m -> List a (S m),
             in an equation for `aux'
    at pyramid.hs:23:6-15
  Expected type: List a (n :+: S m)
    Actual type: List a (m1 :+: S (S m))
* In the expression: aux tl hd (Cons (max hd prev) acc)
  In an equation for `aux':
      aux (Cons hd tl) prev acc = aux tl hd (Cons (max hd prev) acc)
* Relevant bindings include
    acc :: List a m (bound at pyramid.hs:23:23)
    tl :: List a m1 (bound at pyramid.hs:23:14)
    aux :: List a n -> a -> List a m -> List a (n :+: S m)
      (bound at pyramid.hs:22:1)

似乎我需要做的是教编译器(x :+: (S y)) ~ S (x :+: y)。这可能吗?

或者,对于这个问题,还有比类型系统更好的工具吗?

1 个答案:

答案 0 :(得分:8)

首先,一些导入和语言扩展:

{-# LANGUAGE GADTs, TypeInType, RankNTypes, TypeOperators, TypeFamilies, TypeApplications, AllowAmbiguousTypes #-}

import Data.Type.Equality

我们现在有DataKinds (or TypeInType),它允许我们将任何数据提升到类型级别(具有自己的类型),因此类型级别自然真的值得定义为常规{ {1}}(哎呀,这是完全以前GHC文档链接的激励示例!)。您的data类型没有任何变化,但List确实应该是已关闭的类型系列(现在通过类似(:+:)的类型)。

Nat

现在,为了使证明适用于-- A natural number type (that can be promoted to the type level) data Nat = Z | S Nat -- A List of length 'n' holding values of type 'a' data List a n where Nil :: List a Z Cons :: a -> List a m -> List a (S m) type family (+) (a :: Nat) (b :: Nat) :: Nat where Z + n = n S m + n = S (m + n) ,为自然数定义singleton types非常有用。

aux

现在,我们正在开始证明一些东西。从Data.Type.Equality开始,-- A singleton type for `Nat` data SNat n where SZero :: SNat Z SSucc :: SNat n -> SNat (S n) -- Utility for taking the predecessor of an `SNat` sub1 :: SNat (S n) -> SNat n sub1 (SSucc x) = x -- Find the size of a list size :: List a n -> SNat n size Nil = SZero size (Cons _ xs) = SSucc (size xs) 代表a :~: b的证据。我们需要证明一个关于算术的简单事情。

a ~ b

最后,我们可以使用gcastWith-- Proof that n + (S m) == S (n + m) plusSucc :: SNat n -> SNat m -> (n + S m) :~: S (n + m) plusSucc SZero _ = Refl plusSucc (SSucc n) m = gcastWith (plusSucc n m) Refl 中使用此证明。哦,你错过了aux约束。 :)

Ord a

如果这回答了你的问题,请告诉我 - 开始使用这类东西需要很多新东西。