【发布时间】:2019-10-04 16:32:51
【问题描述】:
对于某些应用,我需要长度为 $2^n$ 的向量。为了强制某些操作的长度匹配,我用 ist applicative 实例定义了我的类型,如下所示:
{-# LANGUAGE GADTs, DataKinds, FlexibleInstances, FlexibleContexts #-}
data Nat = Z | N Nat
data Vector n t where
S :: t -> Vector Z t
V :: Vector n t -> Vector n t -> Vector (N n) t
instance Functor (Vector n) where
fmap f (S t ) = S (f t)
fmap f (V t t') = V (fmap f t) (fmap f t')
instance Applicative (Vector Z) where
pure = S
S f <*> S a = S (f a)
instance Applicative (Vector n) => Applicative (Vector (N n)) where
pure a = let a' = pure a in V a' a'
V f f' <*> V a a' = V (f <*> a) (f' <*> a')
我选择了 ghci 建议的语言扩展来编译代码。整个结构的灵感来自How to make fixed-length vectors instance of Applicative?。
当我尝试使用它时,问题就开始了:
instance Num t => Num (Vector n t) where
v + v' = (+) <$> v <*> v'
(*) = undefined
abs = undefined
signum = undefined
fromInteger = undefined
negate = undefined
添加这些行会触发以下错误:
• 无法推断(Applicative (Vector n)) 因使用“”而产生 从上下文:Num t 受 ... 的实例声明约束
• 在表达式中:(+) v > v' 在“+”的等式中: v + v' = (+) v > v' 在“Num (Vector n t)”的实例声明中
我在 Windows 7 上使用 Haskell Platform 8.0.2-a。
知道发生了什么吗?在链接的问题中,同样的技巧似乎有效!? (在第一行添加 KindSignatures 没有帮助,如果没有 FlexibleInstances/Contexts 我会收到编译器错误。)
【问题讨论】:
-
你应该把它写成
instance (Num t, Applicative (Vector n)) => Num (Vector n t) where,否则你确实不能使用(<*>)。 -
非常感谢,我不知何故错误地认为编译器知道所有 (Vector n) 都是 Applicatives。
-
@JohnPoe 与直觉相反,出于非常微妙的原因,并非所有这些都是应用程序。如果
F x :: Nat是一个“卡住”的类型族(例如,F Bool未定义),则不允许编译器解析Applicative (Vector (F Bool)),因为它无法将F Bool评估为具体的Nat,所以它不能选择实例。有一种Nat的类型表达式不计算为具体的Nat是很奇怪的,但这就是 GHC 的工作原理。 -
@chi:非常感谢您的解释,但可能超出了我目前对haskell的理解。我是否正确理解问题出在 Vector nt 中的 n,因为编译器必须能够证明它是具体 Nat x 的 Z 或 N x,但它不能,因为有人可能会插入一个“奇怪的仅“声称”为 Nat 的 n 的事物”?
-
@JohnPoe 是的,这是一个很好的表达方式。仅当类型中有明确的
Z或N x时才会使用您的实例。如果有像n这样的类型变量或“卡住”类型族F Bool,GHC 不知道要选择什么实例并且不会解决约束。有一些方法可以解决这个问题,例如利用合适的 GADT 来证明“n确实是两种可能的形式之一”——不过这有点高级,所以我建议先熟悉 GADT。
标签: haskell