我正在编写一个实现类型类的“简单”示例,但是我很难弄清楚为什么这不会编译:
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)
。
答案 0 :(得分:3)
让我们仔细看看类定义中引用的类型:
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
值并返回 any <{1}}的值 b
。
因此,在Floating 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
上调用realToFrac
来获取任意浮动值。请注意,将x
约束替换为Floating
可能会更有意义。