在嵌套的递归类型构造函数中的证明

时间:2017-11-29 04:40:07

标签: idris

我在使用下面的代码时遇到了一些问题。基本上我想创建一个切片类型。动机来自Python,其中切片为[start:end:step],用于从列表中切割子列表。这在概念上与索引序列[start, start+step, start+2*step, ..., end]相同。

我尝试捕获它的方式Slice n可以应用于Vect (n+m) a。基本构造函数FwdS将创建一个非零步骤的切片(证明步骤NZ)。 SLen构造函数将按其Vect(使用step计算)增加现有切片的stepOf大小要求。同样,SStart会将切片的Vect尺寸要求增加1。

然后,最终值在概念上对应于:

start := # of SStart in slice
stop  := start + (# of SLen) * step 
step  := constructor argument in FwdS

如果切片是[start:stop:step]

mutual
  data Slice : Nat -> Type where
    FwdS   : (step : Nat) -> {stepNZ  : Not (step = Z)} -> Slice Z
    SLen   : (x : Slice len) -> Slice (len + (stepOf x))
    SStart : Slice len -> Slice (S len)

  stepOf : Slice n -> Nat
  stepOf (FwdS step)    = step
  stepOf (SLen slice)   = stepOf slice
  stepOf (SStart slice) = stepOf slice

length : Slice n -> Nat
length (FwdS step )   = Z
length (SLen slice)   = let step = stepOf slice
                            len  = length slice
                         in len + step
length (SStart slice) = length slice

select : (slice: Slice n) -> Vect (n+m) a ->  Vect (length slice) a
select (FwdS step) xs           = []
select (SStart slice) (x :: xs) = select slice xs
select (SLen slice) (xs)        = ?trouble

麻烦在于最后一种模式。我不确定问题是什么 - 如果我尝试在xs上进行案件分割,我就无法[](_::_)。理想情况下,我希望将此案例改为:

select (SLen slice) (x :: xs) = let rec = drop (stepOf slice) (x::xs)
                                 in x :: (select slice rec)

让Idris认识到如果第一个参数是SLen构造函数,则第二个参数不能是[]。我的直觉是,在SLen级别,Idris不理解它已经证明stepOf slice不是Z。但我不确定如何测试这个想法。

1 个答案:

答案 0 :(得分:1)

  

我的直觉是,在SLen级别,Idris不明白它已经有一个证明stepOf切片不是Z.

你是对的。使用:t trouble,您会发现编译器没有足够的信息来推断(plus (plus len (stepOf slice)) m)不是0。

  a : Type
  m : Nat
  len : Nat
  slice : Slice len
  xs : Vect (plus (plus len (stepOf slice)) m) a
--------------------------------------
trouble : Vect (plus (length slice) (stepOf slice)) a

您必须解决两个问题:为某些stepOf slice S k获取kgetPrf : (x : Slice n) -> (k ** stepOf x = (S k))的证明,然后将Vect (plus (plus len (stepOf slice)) m) a重写为类似Vect (S (plus k (plus len m))) a xs所以编译器至少可以知道这个select不是空的。但是从那以后它并不容易。 : - )

基本上每当你有一个在参数中使用函数的函数时,你可能会将这些信息重写为该类型。与length slice SLenstepOf x data Slice : (start : Nat) -> (len : Nat) -> (step : Nat) -> (cnt : Nat) -> Type where FwdS : (step : Nat) -> Slice Z Z step Z SLen : Slice Z len step cnt -> Slice Z (S step + len) step (S cnt) SStart : Slice start len step cnt -> Slice (S start) len step cnt 一样。以下是一个示例实现:

len

您从中获益良多:您可以直接访问参数steplength,而无需先验证函数stepOfSLen $ SStart $ SLen $ SStart $ FwdS 3。您还可以更好地控制允许的数据。例如,在你的定义中select本来是有效的,混合步骤并开始增量。

select : Slice start len step cnt -> Vect (start + len + m) a -> Vect cnt a select (FwdS k) xs = [] select (SStart s) (x :: xs) = select s xs select (SLen s) [] impossible select (SLen s {step} {len} {cnt}) (x::xs) {m} {a} = let is = replace (sym $ plusAssociative step len m) xs {P=\t => Vect t a} in (x :: select s (drop step is)) 可能如下所示:

select : Slice start len step cnt -> Vect (len + start + m) a -> Vect cnt a

如果您想要一个带证明的练习,您可以尝试实施start + len,以便切换> select (SStart $ SLen $ SLen $ FwdS 3) [0,1,2,3,4,5,6,7,8,9] [1, 5] : Vect 2 Integer

从4开始,将两个元素从1开始:

http://www.example.com/service-name-1
http://www.example.com/service-name-2