【问题标题】:Could not match expected type in instance与实例中的预期类型不匹配
【发布时间】:2016-09-23 16:40:09
【问题描述】:

我正在编写一个用于实现类型类的“简单”示例,但是我很难弄清楚为什么它不能编译:

class Euclidean a where
  norm :: (Euclidean a, Floating b) => a -> b

data Point a b = Point a b

instance (Floating x, Floating y) => Euclidean (Point x y) where
  norm (Point x y) = x

它失败了:

Couldn't match expected type ‘b’ with actual type ‘x’
  ‘x’ is a rigid type variable bound by
      the instance declaration at src/Simple.hs:10:10
  ‘b’ is a rigid type variable bound by
      the type signature for
        norm :: (Euclidean (Point x y), Floating b) => Point x y -> b
      at src/Simple.hs:11:3
Relevant bindings include
  x :: x (bound at src/Simple.hs:11:15)
  norm :: Point x y -> b (bound at src/Simple.hs:11:3)
In the expression: x
In an equation for ‘norm’: norm (Point x y) = x

注意:所需的功能实现当然是 sqrt $ (x * x) + (y * y).

【问题讨论】:

    标签: haskell typeclass


    【解决方案1】:

    让我们仔细看看类定义中引用的类型:

    class Euclidean a where
        norm :: (Euclidean a, Floating b) => a -> b
    

    第二行的a 被第一行的a 绑定。但是b 不受任何约束,因此它被隐含地普遍量化。也就是说上面的定义等价于

    class Euclidean a where
        norm :: forall b. (Euclidean a, Floating b) => a -> b
    

    所以对于每个Euclidean 类型a,norm 是一个函数,它接受一个a 值并为任何 Floating b 返回一个b 值。

    因此在Point 的示例中,您提供的norm 的虚拟定义始终返回x 类型的值,而编译器希望您提供一个返回任意Floating 类型值的实现.

    那么你如何解决这个问题?解决方案是做这样的事情:

    instance (Real x, Floating x, Floating y) => Euclidean (Point x y) where
        norm (Point x y) = realToFrac x
    

    我添加了一个Real x 约束,这样我就可以在x 上调用realToFrac 来获取任意浮点值。请注意,将Floating 约束替换为RealFrac 可能更有意义。

    【讨论】:

    • 感谢您的及时答复。现在还有另一个问题:如何将 x 的类型与 y 的类型匹配?我将实现编写为norm (Point x y) = realToFrac $ sqrt $ (x * x) + (y * y)(在实例标头中为这两种类型添加了Real约束),但现在它抱怨它无法将x与y匹配
    • 试试instance (Real x, Floating x) => Euclidean (Point x x) where
    • 它抱怨它需要不同的类型变量,并添加 FlexibleConstraints 以允许这样做
    • 您可以启用FlexibleConstraints 语言扩展,但最好更改Point 的定义:data Point a = Point a a。这会强制两个坐标具有相同的类型。定义一个实例:instance (Real x, Floating x) => Euclidean (Point x)
    • 我找到的另一种解决方案:在应用(+)之前先使用realToFrac进行转换:norm (Point x y) = sqrt $ realToFrac (x * x) + realToFrac (y * y)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    • 2019-03-07
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多