如何构建具有依赖类型长度的列表?

2024-04-02

将我的脚趾浸入依赖类型的水域中,我对规范的“具有静态类型长度的列表”示例进行了破解。

{-# LANGUAGE DataKinds, GADTs, KindSignatures #-}

-- a kind declaration
data Nat = Z | S Nat

data SafeList :: (Nat -> * -> *) where
    Nil :: SafeList Z a
    Cons :: a -> SafeList n a -> SafeList (S n) a

-- the type signature ensures that the input list has at least one element
safeHead :: SafeList (S n) a -> a
safeHead (Cons x xs) = x

这似乎有效:

ghci> :t Cons 5 (Cons 3 Nil)
Cons 5 (Cons 3 Nil) :: Num a => SafeList ('S ('S 'Z)) a

ghci> safeHead (Cons 'x' (Cons 'c' Nil))
'x'

ghci> safeHead Nil
Couldn't match type 'Z with 'S n0
Expected type: SafeList ('S n0) a0
  Actual type: SafeList 'Z a0
In the first argument of `safeHead', namely `Nil'
In the expression: safeHead Nil
In an equation for `it': it = safeHead Nil

但是,为了使该数据类型真正有用,我应该能够从运行时数据构建它,而您在编译时不知道其长度。我天真的尝试:

fromList :: [a] -> SafeList n a
fromList = foldr Cons Nil

编译失败,出现类型错误:

Couldn't match type 'Z with 'S n
Expected type: a -> SafeList n a -> SafeList n a
  Actual type: a -> SafeList n a -> SafeList ('S n) a
In the first argument of `foldr', namely `Cons'
In the expression: foldr Cons Nil
In an equation for `fromList': fromList = foldr Cons Nil

我明白为什么会发生这种情况:返回类型Cons折叠的每次迭代都是不同的 - 这就是重点!但我看不到解决办法,可能是因为我对这个主题的阅读不够深入。 (我无法想象所有这些努力都被投入到一个在实践中不可能使用的类型系统中!)

那么:如何从“正常”简单类型数据构建这种依赖类型数据?


按照@luqui的建议,我能够做到fromList编译:

data ASafeList a where
    ASafeList :: SafeList n a -> ASafeList a

fromList :: [a] -> ASafeList a
fromList = foldr f (ASafeList Nil)
    where f x (ASafeList xs) = ASafeList (Cons x xs)

这是我尝试打开包装ASafeList并使用它:

getSafeHead :: [a] -> a
getSafeHead xs = case fromList xs of ASafeList ys -> safeHead ys

这会导致另一个类型错误:

Couldn't match type `n' with 'S n0
  `n' is a rigid type variable bound by
      a pattern with constructor
        ASafeList :: forall a (n :: Nat). SafeList n a -> ASafeList a,
      in a case alternative
      at SafeList.hs:33:22
Expected type: SafeList ('S n0) a
  Actual type: SafeList n a
In the first argument of `safeHead', namely `ys'
In the expression: safeHead ys
In a case alternative: ASafeList ys -> safeHead ys

同样,直观上来说,这将无法编译是有道理的。我可以打电话fromList有一个空列表,所以编译器不能保证我能够调用safeHead关于由此产生的SafeList。这种知识的缺乏大致就是存在主义的ASafeList捕获。

这个问题能解决吗?我觉得我可能已经走上了逻辑的死胡同。


永远不要扔掉任何东西。

如果您打算不厌其烦地沿着列表创建一个长度索引列表(在文献中称为“向量”),您最好记住它的长度。

所以,我们有

data Nat = Z | S Nat

data Vec :: Nat -> * -> * where -- old habits die hard
  VNil :: Vec Z a
  VCons :: a -> Vec n a -> Vec (S n) a

但我们也可以为静态长度提供运行时表示。 Richard Eisenberg 的“Singletons”包将为您完成此操作,但基本思想是为静态数字提供一种运行时表示形式。

data Natty :: Nat -> * where
  Zy :: Natty Z
  Sy :: Natty n -> Natty (S n)

至关重要的是,如果我们有一个类型值Natty n,然后我们可以询问该值以找出什么n is.

Hasochists 知道运行时可表示性通常非常无聊,甚至机器都可以管理它,因此我们将其隐藏在类型类中

class NATTY (n :: Nat) where
  natty :: Natty n

instance NATTY Z where
  natty = Zy

instance NATTY n => NATTY (S n) where
  natty = Sy natty

现在,我们可以对您从列表中获得的长度进行稍微更具信息性的存在主义处理。

data LenList :: * -> * where
  LenList :: NATTY n => Vec n a -> LenList a

lenList :: [a] -> LenList a
lenList []        = LenList VNil
lenList (x : xs)  = case lenList xs of LenList ys -> LenList (VCons x ys)

您将获得与长度破坏版本相同的代码,但您可以随时获取长度的运行时表示,并且不需要沿着向量爬行来获取它。

当然,如果你希望长度为Nat,这仍然是一个痛苦,你反而有一个Natty n对于一些n.

把口袋弄得乱七八糟是错误的。

Edit我想我应该添加一点,以解决“安全头”的使用问题。

首先,让我添加一个解包器LenList这给了你手中的数字。

unLenList :: LenList a -> (forall n. Natty n -> Vec n a -> t) -> t
unLenList (LenList xs) k = k natty xs

现在假设我定义

vhead :: Vec (S n) a -> a
vhead (VCons a _) = a

强化安全属性。如果我有一个向量长度的运行时表示,我可以查看它是否vhead适用。

headOrBust :: LenList a -> Maybe a
headOrBust lla = unLenList lla $ \ n xs -> case n of
  Zy    -> Nothing
  Sy _  -> Just (vhead xs)

所以你看一件事,并在这样做的过程中了解另一件事。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何构建具有依赖类型长度的列表? 的相关文章

  • 生成所有可能的树

    给定以下数据类型定义 data FormTree Empty Node FormTree FormTree deriving Show 我想编写一个函数 它生成一个无限列表 其中包含按长度排序的所有可能的树 例如节点数量 下面的代码几乎满足
  • 为什么 Haskell 中有协函子和逆变函子的区别,而范畴论却没有区别?

    这个答案是从范畴论的角度来看的 https math stackexchange com a 661989 72174包括以下语句 事实是 协函子和逆变函子之间没有真正的区别 因为每个函子只是一个协变函子 More in details a
  • 导入 Haskell 模块

    我是哈斯克尔的新手 为什么当我尝试使用时Days from Data Time我收到此错误 Could not find module Data Time It is a member of the hidden package time
  • 如何从 haskell 中的 IOError 获取 errno?

    我在 haskell 平台上 GHC 6 12 1 作为 apt get 安装在 Debian Squeeze 上 鉴于我需要在与最初引发它的线程不同的线程上使用它 如何从 IOError 中获取底层 errno 我需要这个的原因是因为我正
  • 为什么 Haskell 的默认字符串实现是一个字符链接列表?

    Haskell 默认值的事实String众所周知 实现在速度和内存方面都效率不高 据我所知 lists一般来说 在 Haskell 中实现为单链表 并且适用于大多数小型 简单数据类型 例如Int 这似乎不是一个好主意 但是对于String这
  • Haskell:无法预期类型“Integer”与实际类型“Int”

    我已经盯着这段代码有一段时间了 但我无法理解该错误消息 divisors Integer gt Integer divisors n t t lt 1 n mod n t 0 length a gt Integer length 0 len
  • Haskell:是的,没有类型类。为什么是整数?

    我有一个关于 GHCi 如何假定整数类型的问题 我正在阅读 Learn you a Haskell 是 否类型的课程 如果您想阅读全文 这里有一个链接 http learnyouahaskell com making our own typ
  • 以下两个 lambda 函数的空间复杂度

    我正在阅读以下内容 https en wikibooks org wiki Haskell Graph reduction https en wikibooks org wiki Haskell Graph reduction 其内容如下
  • 如何在 Haskell 中安装库?

    我尝试使用控制 Monad Extra andM https hackage haskell org package extra 1 7 10 docs Control Monad Extra html import Control Mon
  • Haskell 中列表列表的笛卡尔积

    给定一个长度列表的列表x所有子列表的长度都相同y 输出y x长度列表x包含每个子列表中的一项 例子 x 3 y 2 1 2 3 4 5 6 Output 2 3 8不同的输出 1 3 5 1 4 5 1 3 6 1 4 6 2 3 5 2
  • Haskell 中的尾递归字符串分割

    我正在考虑分割字符串的问题s在一个字符处c 这表示为 break c s 其中 Haskell 库定义break c 足够接近 br br s h t if c h then s else let h t br t in h h t 假设我
  • Haskell:Data.Numbers.Primes 库在哪里?

    我尝试导入 Data Numbers Primes import Data Numbers Primes 伦哈斯克尔给了我 5 hs 1 8 Could not find module Data Numbers Primes Use v t
  • 用于遇到 [...] 的 Haskell Parsec 解析器

    我正在尝试使用 Parsec 在 Haskell 中编写一个解析器 目前我有一个可以解析的程序 test x 1 2 3 end 执行此操作的代码如下 testParser do reserved test v lt identifier
  • 你能识别 Haskell 程序中的无限列表吗? [复制]

    这个问题在这里已经有答案了 可能的重复 如何判断列表是否是无限的 https stackoverflow com questions 7371730 how to tell if a list is infinite 在Haskell中 你
  • Haskell - lambda 表达式

    我试图了解什么是有用的以及如何在 Haskell 中实际使用 lambda 表达式 我不太明白使用 lambda 表达式相对于定义函数的约定方式有何优势 例如 我通常会执行以下操作 let add x y x y 我可以简单地打电话 add
  • 为什么 ZipList 不是 List 的默认应用实例

    我目前正在学习 Haskell 中的应用程序 如果我没记错的话 列表有两个不同的应用实例 List and ZipList 第二个被定义为包装列表值的新类型 这ZipList应用实例对我来说似乎更直观 这可能是一个愚蠢的问题 但有具体原因吗
  • 如何通过“cabal build”或“stack build”构建带有图标的项目

    我想构建一个带有图标的可执行文件 通过谷歌搜索 我发现这里的说明 https wiki haskell org Setting an executable icon 但它只能通过编译源文件来工作ghc 如果我想构建一个具有可执行文件的项目c
  • Data.Sequence 中的 inits 和 tails 如何工作?

    Louis Wasserman 编写了当前的实现inits and tails in Data Sequence 他表示它们非常高效 事实上 只要查看代码 我就可以看到 无论它们在做什么 它们都是以干净 自上而下的方式进行的 这往往会给惰性
  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采
  • 管道:多个流消费者

    我编写了一个程序来计算语料库中 NGram 的频率 我已经有一个函数 它消耗一串令牌并生成一个订单的 NGram ngram Monad m gt Int gt Conduit t m t trigrams ngram 3 countFre

随机推荐

  • 提取 git 提交中更改的所有文件

    我需要为某人制作一个补丁 他们没有使用 git 由提交更改的文件的 zip 我想像 git archive format zip commitguid gt myfiles zip 但这会提取整个内容 而不仅仅是更改的文件 有什么办法可以做
  • 如何使用 Cocoa UI 制作 Java 应用程序?

    我必须在项目中使用 Java API 但由于我不喜欢 Java UI 并且我有一台 Mac 所以我想围绕 Java 代码构建一个本机 Cocoa 应用程序 我知道 Xcode 曾经为此类事情提供直接支持 但由于它不再提供 那么最好的方法是什
  • 在android webview中全屏播放HTML5视频

    好吧 我已经搜索了几天了 如何在 android WebView 上以全屏模式显示 HTML5 视频 我设法在我的网络视图上播放 HTML5 视频 以全屏模式显示视频时会出现问题 正如我所发现的 android 有两种处理 标签的方法 在
  • 将 Canvas 内容导出为 PDF

    我正在使用 HTML5 Canvas 做一些事情 一切都工作得很好 除了现在 我可以使用 Canvas2image 将画布内容导出为 PNG 但我想将其导出为 PDF 我做了一些研究 我很确定这是可能的 但我似乎无法理解我需要在代码中更改什
  • 网络共享的锁定行为有所不同

    我一直在尝试锁定文件 以便其他克隆服务无法访问该文件 然后我读取该文件 完成后移动该文件 通过使用允许移动FileShare Delete 然而 在后来的测试中 我们发现如果我们正在查看网络共享 则这种方法不起作用 我知道我的方法可能不是最
  • iPhone/iOS自定义控件

    我想知道如何从头开始创建自定义 iPhone 控件 或者使用现有的库或框架 我已经看到了 Three20 库 以及 Tapku 和 Touch Customs 它们对于专门的 iOS 控件 例如表格视图等 非常有用 但我在这里讨论的是制作完
  • Laravel - 如何设置 morphOne 关系

    我正在尝试为类别实现一个可变形表 现在我有以下内容 Snippet Table id title body Post Table id title body Category Table id name 我希望能够将帖子和片段改为只有一个类
  • 2038 年问题 - 64 位(Linux 操作系统、php、mysql)[重复]

    这个问题在这里已经有答案了 select unix timestamp 2038 01 19 回报2147472000 while select unix timestamp 2038 01 20 回报0 我查了一下年份2038问题 我的l
  • hibernate ManyToMany 与可连接的顺序

    我有以下数据库设置 T PARTICIPANT MOVEMENT ParticipantMove SID BigInt PK Participant SID BigInt FK MoveType SID BigInt FK MoveReas
  • cygdb ImportError:没有名为“Cython”的模块

    我想调试我的 Cython 代码并按照描述的确切步骤进行操作here http docs cython org src userguide debugging html 我的 Cython 代码编译 cython gdb xxx pyx进而
  • R strptime/as.POSIXct 中的未知时区名称

    在哪里可以找到 R 函数的所有合法时间名称的列表as POSIXct as POSIXct 1970 01 01 tz CST 生成一条警告 指出 CST 中部标准时间 未知 时区的东西可以驱动你NUTS 由于位于德国 我过去常常这样做来设
  • ASP.NET MVC:ValueProvider 的执行顺序

    我想知道不同的执行顺序ValueProviders在 ASP NET MVC 中 价值提供者 查询字符串值提供者 路由数据值提供者 表单值提供者 我没有找到信息 如果我没记错的话 优先顺序是这样的 请求中的表单数据 路线数据 请求参数 Ht
  • Matplotlib:图例未正确显示

    我有不同类别的数据点 我想将其可视化 这是我得到的图像 https i stack imgur com qwCLC jpg https i stack imgur com qwCLC jpg 有 10 个类别的 3000 个数据点 每个类别
  • opencv矩阵数据能保证连续吗?

    我知道 OpenCV 矩阵中包含的数据不能保证是连续的 为了让自己清楚 这里有一段Opencv 文档 https docs opencv org 3 4 0 d3 d63 classcv 1 1Mat html details OpenCV
  • 将 EF4 与 Caliburn.Micro 绑定:我应该将我的实体公开为 ViewModel 的属性吗?

    使用 Caliburn Micro 我想知道将 EF4 实体公开为 ViewModel 的属性 讨论过的一种技术 的优缺点here http kboek blogspot com 2011 01 activerecord caliburn
  • 在 jsdom 测试中卸载/销毁组件

    有没有办法卸载和垃圾收集使用安装的 React 组件TestUtils renderIntoDocument在 jsdom 测试中 我正在尝试测试发生的事情componentWillUnmount and TestUtils renderI
  • 如何根据Python中的条件从现有数据帧创建多个数据帧

    我有一个数据框 如下所示 我想根据列 ID 从该数据帧创建多个数据帧 df pd DataFrame results print df 结果是 ID NAME COLOR 0 01 ABC RED 1 01 ABC ORANGE 2 01
  • SQLalchemy 中带有子查询、分组依据、计数和求和函数的高级 SQL 查询

    我写了以下查询 select distinct table3 select count from table2 where table2 cus id table3 id as count select sum amount from ta
  • flutter 中任务 ':app:checkDebugManifest'(类型 'CheckManifest')的配置出现问题

    我正在构建我的应用程序 当构建时发生此错误时 FAILURE Build failed with an exception What went wrong A problem was found with the configuration
  • 如何构建具有依赖类型长度的列表?

    将我的脚趾浸入依赖类型的水域中 我对规范的 具有静态类型长度的列表 示例进行了破解 LANGUAGE DataKinds GADTs KindSignatures a kind declaration data Nat Z S Nat da