我有这个代码:
data SafeValue a = SafeValue a a a deriving Eq
class Safe a where
check::a->Bool
(+++)::a->a->a
instance (Num a, Eq a) => Safe (SafeValue a) where
check (SafeValue x y z) | x == y = True
| x == z = True
| y == z = True
| otherwise = False
(SafeValue a b c)+++(SafeValue x y z) = let new_val = SafeValue (a+x) (b+y) (c+z)
in if check new_val then new_val
else error "Error"
Run Code Online (Sandbox Code Playgroud)
我想添加class Safe一个功能,如:
make_new 3 --> SafeValue 3 3 3
Run Code Online (Sandbox Code Playgroud)
我不知道如何添加它,因为tipy应该是这样的:
make_new::b->a
Run Code Online (Sandbox Code Playgroud)
但在istance声明中ghci声称它不确定是什么b.
有人可以帮帮忙吗?
核心问题是你承诺make_new适用于所有类型b的产生价值a.然而,考虑到SafeValue a工作方式,这没有意义:给定一些类型a,你得到了一个SafeValue a.所以你真正想要的是获取make_new某种类型的值,a但给你一个值SafeValue a.更一般地说,您希望结果是某种类型s a,其中s您正在编写实例的实际类型,并且a可以是任何类型.
你需要做的是让类接受"更高级"类型的值.(这意味着类应该期望类似的类型SafeValue需要另一个参数.你可以这样做:
class Safe s where
check :: s a -> Bool
(+++) :: s a -> s a -> s a
make_new :: a -> s a
Run Code Online (Sandbox Code Playgroud)
然后你的实例将如下所示:
instance Safe SafeValue where ...
Run Code Online (Sandbox Code Playgroud)
请注意重要的区别:不使用实例SafeValue a,而是在SafeValue 没有 type参数的情况下进行实例化.
然而,这还有另一个问题:现在你不能限制a成为Num和的一部分Eq!
您可以使用称为多参数类型类的扩展来解决此问题.所以你的最终版本是:
class Safe s a where
check :: s a -> Bool
(+++) :: s a -> s a -> s a
make_new :: a -> s a
Run Code Online (Sandbox Code Playgroud)
你的实例将是:
instance (Num a, Eq a) => Safe SafeValue a where ...
Run Code Online (Sandbox Code Playgroud)
要使所有这些工作,您需要启用两个扩展:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
Run Code Online (Sandbox Code Playgroud)
第一个允许您以更多方式编写实例.通常,您只能为类似于T a b c哪里T的类型编写实例,并且a b c是类型变量; 通过此扩展,限制被解除,您可以编写类似我展示的实例.
多参数类型类扩展允许作用于多种类型的类型类.这允许你创建一个依赖于s 和的类 a.
最后一点:完全使用类型类可能不是你的例子的正确选择.你打算为Safe班级写更多类型吗?如果你不是,那么你根本不应该使用类型类.但是,学习一下多参数类型类仍然很有用,所以你应该考虑在某些时候使用它们.