Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 递归定义的实例和约束_Haskell_Typeclass - Fatal编程技术网

Haskell 递归定义的实例和约束

Haskell 递归定义的实例和约束,haskell,typeclass,Haskell,Typeclass,我一直在使用新的datatypes扩展来处理向量和矩阵,其中大小以其类型编码。基本上是这样的: data Nat = Zero | Succ Nat data Vector :: Nat -> * -> * where VNil :: Vector Zero a VCons :: a -> Vector n a -> Vector (Succ n) a 现在我们需要像Functor和Applicative这样的典型实例函子很简单: instance F

我一直在使用新的
datatypes
扩展来处理向量和矩阵,其中大小以其类型编码。基本上是这样的:

data Nat = Zero | Succ Nat

data Vector :: Nat -> * -> * where
    VNil :: Vector Zero a
    VCons :: a -> Vector n a -> Vector (Succ n) a
现在我们需要像
Functor
Applicative
这样的典型实例<代码>函子很简单:

instance Functor (Vector n) where
    fmap f VNil = VNil
    fmap f (VCons a v) = VCons (f a) (fmap f v)
但是对于
Applicative
实例,存在一个问题:我们不知道在纯文本中返回什么类型。但是,我们可以根据向量的大小归纳地定义实例:

instance Applicative (Vector Zero) where
    pure = const VNil
    VNil <*> VNil = VNil

instance Applicative (Vector n) => Applicative (Vector (Succ n)) where
    pure a = VCons a (pure a)
    VCons f fv <*> VCons a v = VCons (f a) (fv <*> v)
我们必须定义一个类型类并添加递归实例声明来实现转置和矩阵乘法。这导致每次使用代码时都必须携带大量约束,即使实例实际上适用于所有向量和矩阵(使约束变得毫无用处)


有没有一种方法可以避免所有这些限制?是否有可能扩展类型检查器,使其能够检测此类归纳结构?

的定义确实是问题的核心。它的类型应该是什么,完全量化和合格的

pure :: forall (n :: Nat) (x :: *). x -> Vector n x            -- (X)
不行,因为在运行时没有可用的信息来确定
pure
是否应该发出
VNil
VCons
。相应地,就目前的情况来看,你不能只拥有

instance Applicative (Vector n)                                -- (X)
你能做什么?在示例文件中,我定义了一个
pure

vec :: forall x. pi (n :: Nat). x -> Vector {n} x
vec {Zero}    x = VNil
vec {Succ n}  x = VCons x (vec n x)
使用
pi
类型,要求在运行时传递
n
的副本。这
pi(n::Nat)。

forall n. Natty n ->
其中,
Natty
,在现实生活中有一个更平淡无奇的名字,是由

data Natty n where
  Zeroy :: Natty Zero
  Succy :: Natty n -> Natty (Succ n)
vec
方程中的花括号只是将
Nat
构造函数转换为
Natty
构造函数。然后我定义以下恶魔实例(关闭默认的函子实例)

我们将约束
{:n::Nat:}
替换为
HasNatty n
,并将相应的术语替换为
(natty::natty n)
。系统地进行这种构造相当于在类型类Prolog中编写Haskell typechecker的一个片段,这不是我的想法,所以我使用计算机

请注意,
可遍历的
实例(请原谅我的习惯用法括号和默认的无提示函子和可折叠实例)不需要这种诡计

instance Traversable (Vector n) where
  traverse f VNil         = (|VNil|)
  traverse f (VCons x xs) = (|VCons (f x) (traverse f xs)|)
这就是在无需进一步显式递归的情况下获得矩阵乘法所需的全部结构

TL;DR使用singleton构造及其关联的类型类将所有递归定义的实例折叠为存在类型级数据的运行时见证,从中可以通过显式递归进行计算。

设计的含义是什么

GHC7.4有类型提升技术,但她仍然提供单体结构
pi
-类型。关于提升的数据类型,一个明显重要的事情是它们是封闭的,但这还没有完全表现出来:单例见证的可构造性就是这种封闭性的表现。不知何故,如果您对所有(n::Nat)都有
那么要求一个单例也是合理的,但是这样做会对生成的代码产生影响:无论它是显式的,如我的
pi
构造,还是隐式的,如
{:n::Nat:}
的字典中,都有额外的运行时信息,一个相对较弱的自由定理

GHC未来版本的一个开放设计问题是如何管理类型级数据的运行时见证的存在与否之间的区别。一方面,我们需要约束。另一方面,我们需要对它们进行模式匹配。例如,应该
pi(n::Nat)。
表示显式

forall (n :: Nat). Natty n ->
还是含蓄

forall (n :: Nat). {:n :: Nat:} =>

??当然,像Agda和Coq这样的语言都有这两种形式,所以Haskell或许应该效仿。当然还有进步的空间,我们正在努力

经过几次阅读和一些实验,我终于掌握了这个答案。基本上,HasNatty约束允许您在值级别而不是类型级别执行递归,从而消除了对其他约束的需要。这很有帮助。然而,我真的很难看到如何根据可遍历性实现矩阵乘法。你能提供一些提示吗?大多数矩阵乘法的实现都是先转置其中一个矩阵。你能用Traversable得到矩阵转置吗?是的。如果上面有
Applicative
Traversable
的实例,那么
transpose
就是
traverse id
。要查看这一点,首先检查类型<代码>遍历::Applicative vm=>(s->vm t)->向量ns->vm(向量nt)
现在取
vm=Vector m
s=Vector mt
,我们得到
遍历id::Vector n(Vector mt)->Vector m(Vector nt)
。在操作上,
traverse id
VNil
转换为
VNil
的向量,并将向量化-
VCons
转换为最上面一行和其他行,使最上面一行变成最左边的列。对,但这仍然需要对内部向量进行HasNatty约束。不过还是不错,谢谢。
instance Traversable (Vector n) where
  traverse f VNil         = (|VNil|)
  traverse f (VCons x xs) = (|VCons (f x) (traverse f xs)|)
forall (n :: Nat). Natty n ->
forall (n :: Nat). {:n :: Nat:} =>