【问题标题】:Lists of fixed length and type literals固定长度和类型文字的列表
【发布时间】:2015-04-23 12:21:57
【问题描述】:

我正在尝试在 Haskell 中为固定长度的列表定义一种类型。当我使用标准方法将自然数编码为一元类型时,一切正常。但是,当我尝试在 GHC 的类型文字上构建所有内容时,我遇到了很多问题。

我对所需列表类型的第一次尝试是

data List (n :: Nat) a where
  Nil :: List 0 a
  (:>) :: a -> List n a -> List (n+1) a

不幸的是,它不允许使用 type 编写 zip 函数

zip :: List n a -> List n b -> List n (a,b)

我可以通过将(:>)类型的类型变量n减1来解决这个问题:

data List (n :: Nat) a where
  Nil :: List 0 a
  (:>) :: a -> List (n-1) a -> List n a -- subtracted 1 from both n's

接下来,我尝试定义一个追加函数:

append :: List n1 a -> List n2 a -> List (n1 + n2) a
append Nil       ys = ys
append (x :> xs) ys = x :> (append xs ys)

不幸的是,GHC 告诉我

Couldn't match type ‘(n1 + n2) - 1’ with ‘(n1 - 1) + n2’

将约束 ((n1 + n2) - 1) ~ ((n1 - 1) + n2) 添加到签名并不能解决问题,因为 GHC 抱怨

Could not deduce ((((n1 - 1) - 1) + n2) ~ (((n1 + n2) - 1) - 1))
from the context (((n1 + n2) - 1) ~ ((n1 - 1) + n2))

现在,我显然陷入了某种无限循环。

所以,我想知道是否可以使用类型文字为固定长度的列表定义一个类型。我是否可能正是为了这个目的而监督图书馆?基本上,唯一的要求是我想写类似List 3 a 而不是List (S (S (S Z))) a

【问题讨论】:

  • 您可以在 Hasochism 论文中找到关于类型级向量长度相关问题的一些讨论:personal.cis.strath.ac.uk/conor.mcbride/pub/hasochism.pdf
  • “Hasochism”听起来很诱人。尽管如此,我还是会试一试这篇论文。谢谢。
  • 在带有Nat 的普通列表周围创建一个新类型的包装器可能更容易,类似于Linear.V 的做法。您可以在一个模块中定义一些原语并隐藏构造函数以确保一切安全。

标签: haskell ghc type-inference


【解决方案1】:

这不是真正的答案。

使用https://hackage.haskell.org/package/ghc-typelits-natnormalise-0.2,这个

{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}

import GHC.TypeLits

data List (n :: Nat) a where
  Nil :: List 0 a
  (:>) :: a -> List n a -> List (n+1) a

append :: List n1 a -> List n2 a -> List (n1 + n2) a
append Nil       ys = ys
append (x :> xs) ys = x :> (append xs ys)

... 编译,所以显然它是正确的。

【讨论】:

  • 我根本不知道类型检查器的插件,但这确实很好用。谢谢。
  • 我将如何实现一个函数fill :: a -> List n a,用一个常数值填充List?是否有某种模式匹配可以在这两种情况之间切换:fill x = Nil; fill x = x :> fill x
【解决方案2】:

类型级数字字面量还没有我们可以进行归纳的结构,内置的约束求解器只能找出最简单的情况。目前最好坚持使用Peano naturals。

但是,我们仍然可以使用文字作为语法糖。

{-# LANGUAGE
  UndecidableInstances,
  DataKinds, TypeOperators, TypeFamilies #-}

import qualified GHC.TypeLits as Lit

data Nat = Z | S Nat

type family Lit n where
    Lit 0 = Z
    Lit n = S (Lit (n Lit.- 1))

现在你可以写List (Lit 3) a 而不是List (S (S (S Z))) a

【讨论】:

  • 我也有类似的想法,但使用UndecidableInstances 总是让我有点害怕。使用另一种类型的同义词,我什至可以到达List 3 a
  • 这里类型族显然终止了,所以UndecidableInstances 的问题为零。即使在一般情况下,我也不觉得它真的很可怕。如果我们不小心编写了不同的类型级代码,我们几乎总是会遇到上下文深度限制错误。我们很少能真正得到 GHC 循环,我们可以用 Ctr-c 轻松纠正这个问题。
猜你喜欢
  • 2019-03-20
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-28
  • 2011-11-17
相关资源
最近更新 更多