尝试创建 Vector
适用于元组的类型类我遇到了一些问题
{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
class Vector v where
type Scalar v :: *
vplus :: v -> v -> v
vmult :: v -> Scalar v -> v
vdot :: v -> v -> Scalar v
instance Num a => Vector (a, a) where
type Scalar (a,a) = a
(a, b) `vplus` (c, d) = (a + c, b + d)
(a, b) `vmult` m = (a * m, b * m)
(a, b) `vdot` (c, d) = a * c + b * d
问题是我需要显式的类型声明,这样 GHC 才不会混淆。这当然是一个小小的不便,除了 vdot
看起来根本不想工作。
res :: Int
res = (2, 3) `vdot` (5, 5)
-- error: Couldn't match expected type 'Int' with actual type 'Scalar (t0, t1)'
-- The type variables 't0', 't1' are ambiguous
如果我这样做,这个错误就会消失:
res :: Int
res = ((2, 3) :: (Int, Int)) `vdot` (5, 5)
但是现在我们已经达到了冗长代码的境界,以至于它不再实用了。 Haskell 应该是美丽而简洁的;不显式类型 hell
我本以为 GHC 能够解决 type Scalar (a, a) = a
本身,但即使我完全删除实例声明,错误仍然存在。它甚至会提示 Vector (Int, Int)
是唯一可用的实例。
那么这是怎么回事?有没有办法让它完美地工作?
最佳答案
在
res :: Int
res = (2, 3) `vdot` (5, 5)
没有什么强制2
和3
是Int
,甚至是彼此相同的类型。因此,Vector (a, a)
实例可能不适用。据 GHC 所知,您可能打算编写另一个实例 Vector (Float, Double)
,其中 type Scalar (Float, Double) = Int
以及完全不同的 实现vdot
和 res
仍会进行类型检查。因此,正如 GHC 告诉您的那样,2
和 3
的类型是不明确的。
听起来你真的想说:一对 (a, b)
只能仅是 Vector
的实例,当 b
与 a
类型相同(然后使用您编写的实例)。您可以在 GHC 中表达如下:
instance (a ~ b, Num a) => Vector (a, b) where
type Scalar (a,b) = a
(a, b) `vplus` (c, d) = (a + c, b + d)
(a, b) `vmult` m = (a * m, b * m)
(a, b) `vdot` (c, d) = a * c + b * d
a ~ b
是一个等式约束,断言 a
和 b
两种类型相同。
现在您的示例 res
可以正常工作:
*Main> (2, 3) `vdot` (5, 5) :: Int
25
这里的推理意味着类型不再含糊不清。
vdot
的类型为Vector v => v -> v -> Scalar v
。因此,对于res
进行类型检查,我们需要找到v
使得(2, 3)::v
,(5 , 5)::v
和标量 v ~ Int
。但是
(2, 3)
具有(a, b)
形式的类型,并且有一个实例,其头部的形式为矢量(a,b)
。因此,我们必须使用该实例。 (在您的原始程序中,我们无法执行此步骤,因为没有足够通用的实例。)该实例的关联类型定义告诉我们
<Scalar (a, b) ~ a
。但我们知道Scalar (a, b)
应该是Int
,所以我们必须有a ~ Int
。该实例的约束告诉我们
a ~ b
并且应该有一个实例Num a
。因此,我们也必须有b ~ Int
(而且Num Int
确实成立)。因此,我们得出
2::Int
和3::Int
的结论,并且由于(5, 5)::v
code> 另外,我们还有5::Int
和5::Int
。现在我们已经确定了表达式中每个重载名称使用哪个类型类(
2
、3
、5
、5
和vdot
),因此没有歧义,我们最终可以计算表达式。
关于haskell - 无法将预期类型与实际类型匹配。类型变量不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34435306/