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

李维·罗斯

假设长度为n的列表L插入长度为n +1的列表J中我们想知道,对于J的每个元素,L中的哪个邻居更大。下面的函数需L作为其输入,并产生一个列表K,也长度的n + 1个,使得的K个元素是所期望的邻居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(对我来说是一种新语言),因为我对能够通过类型系统证明此不变式成立感兴趣。借助先前的答案,我可以做到以下几点:

{-# 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)这可能吗?

或者,是否有比类型系统更好的工具来解决此问题?

亚历克

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

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

import Data.Type.Equality

现在,我们有了DataKinds(或TypeInType,可以将任何数据提升到类型级别(具有自己的种类),因此,真正应将类型级别的自然值定义为常规类型data(哎呀,这正是先前链接到的激励示例) GHC文档给出!)。您的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)

现在,为了使证明适用于aux为自然数定义单例类型很有用

-- 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)

现在,我们已经准备好开始证明一些东西。来自Data.Type.Equalitya :~: b表示的证明a ~ b我们需要证明关于算术的一件事。

-- 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

最后,我们可以gcastWith在中使用此证明aux哦,您错过了Ord a约束。:)

aux :: Ord a => 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 = gcastWith (plusSucc (size tl) (SSucc (size acc)))
                                      aux tl hd (Cons (max hd prev) acc)

-- append to a list
(|>) :: List a n -> a -> List a (S n)
Nil |> y = Cons y Nil
(Cons x xs) |> y = Cons x (xs |> y)

-- reverse 'List'
rev :: List a n -> List a n
rev Nil = Nil
rev (Cons x xs) = rev xs |> x

让我知道这是否回答了您的问题-这类事情的开始涉及很多新事物。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

确保输入列表最小长度序言

如何创建一个在X处停止并输出sum和len的用户输入列表

Python假设:确保输入列表的长度相同

使用 for 循环编写函数并输入列表

使用输入列表和部分文件路径查找和复制文件

如何在与脚本一起使用以更改类的已检查输入列表中添加“取消全选”按钮

Typescript:如何使类型系统使用<generics>根据输入准确地确定输出类型

具有相同长度的两个输入列表与具有不同长度的两个输入列表的行为不同(方案)

SQLAlchemy,array_agg和匹配输入列表

类型提示函数,其中返回类型取决于输入列表项类型

输入列表超过一定长度时,多处理代码将挂起

使用JavaScript设置HTML5输入列表值

使用给定的输入列表对DataFrame列进行排序

使用用户Java输入列表的Max()方法

Python:输出列表=输入列表的最大数量

固定长度和类型文字的列表

如何使用 LINQ 加入列表和大型列表/表

如何制作输入列表?

使用输入设置所有字符的列表。使用列表长度检查并替换所有具有相应值的字符

使用Composition API和Typescript输入系统对Vue组件进行强类型输入的道具

插入列表到特定长度?

我想将find的输出传递到scp的输入列表中,怎么办?

默认类型的输入和输出信号SystemVerilog

Python-输入和不输入列表语法错误

Argparse - 制作输入和输出列表

如何在python中为特定的输出模式进行运行长度编码?(不使用 groupby 和列表输出)

TF/Keras 中输入和输出长度不等的 RNN 层

如何显示列表中有多少个对象,输入列表名称和对象?

Haskell输入列表-列表上的递归