我有这段代码:
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"
我想向class Safe
添加一个函数,例如:
make_new 3 --> SafeValue 3 3 3
我不知道如何添加它,因为这应该是:
make_new::b->a
但在istance
声明中ghci
声称它不确定b
是什么。
有人可以帮忙吗?
答案 0 :(得分:5)
核心问题是,您承诺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
然后您的实例将如下所示:
instance Safe SafeValue where ...
请注意重要的区别:不是为SafeValue a
创建实例,而是在没有类型参数的情况下使用SafeValue
。
但是,这还有另一个问题:现在您不能将a
限制为Num
和Eq
的一部分!
您可以使用称为多参数类型类的扩展来解决此问题。所以你的最终版本将是:
class Safe s a where
check :: s a -> Bool
(+++) :: s a -> s a -> s a
make_new :: a -> s a
,您的实例将是:
instance (Num a, Eq a) => Safe SafeValue a where ...
要完成所有这些工作,您需要启用两个扩展程序:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
第一个允许您以更多方式编写实例。通常,您只能为类似T a b c
的类型编写实例,其中T
是类型,a b c
是类型变量;通过此扩展,限制被解除,您可以编写类似我所展示的实例。
多参数类型类扩展允许作用于多种类型的类型类。这使您可以创建一个取决于s
和 a
的课程。
最后一点:完全使用类型类可能不是你的例子的正确选择。您是否计划为Safe
课程编写更多类型?如果你不是,那么你根本不应该使用类型类。但是,学习一下多参数类型类仍然很有用,所以你应该考虑在某些时候使用它们。