haskell - 无法将预期类型与实际类型匹配。类型变量不明确

标签 haskell type-inference typeclass type-families

尝试创建 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)

没有什么强制23Int,甚至是彼此相同的类型。因此,Vector (a, a) 实例可能不适用。据 GHC 所知,您可能打算编写另一个实例 Vector (Float, Double) ,其中 type Scalar (Float, Double) = Int 以及完全不同的 实现vdotres 仍会进行类型检查。因此,正如 GHC 告诉您的那样,23 的类型是不明确的。

听起来你真的想说:一对 (a, b) 只能Vector 的实例,当 ba 类型相同(然后使用您编写的实例)。您可以在 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 是一个等式约束,断言 ab 两种类型相同。

现在您的示例 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::Int3::Int 的结论,并且由于 (5, 5)::v code> 另外,我们还有 5::Int5::Int

  • 现在我们已经确定了表达式中每个重载名称使用哪个类型类(2355vdot),因此没有歧义,我们最终可以计算表达式。

关于haskell - 无法将预期类型与实际类型匹配。类型变量不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34435306/

相关文章:

Haskell - Bags - 如何在 Haskell 中使用多态性?

generics - ToString 为单位值抛出 NullReferenceException ()

f# - 与 F# 中的 Haskell GADT 和类型类最接近的是什么?

haskell - 存在函数依赖关系时类型推断如何工作

haskell - 了解何时使用类型类或 GADT?

Haskell:标准库是否假设 Eq 和 Ord 兼容?

haskell - 如何使用 Haskell 的 mmap 库读取共享内存?

multithreading - Haskell 如何在多核机器/集群上处理并行计算

parsing - 使用 Free Monad 实现词法分析器

c++ - lambda 中的递归调用 (C++11)