1

我有这个代码:

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

有人可以帮忙吗?

4

1 回答 1

5

核心问题是您承诺make_new适用于所有类型b以产生a. 然而,考虑到它是如何工作的,这没有任何意义SafeValue a:给定一些 type 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您在 forSafeValue 没有类型参数的情况下创建实例,而不是创建实例。

但是,这还有另一个问题:现在您不能限制a成为Numand的一部分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 cwhereT是类型并且a b c是类型变量的类型编写实例;有了这个扩展,限制就解除了,你可以像我展示的那样编写实例。

多参数类型类扩展允许作用于一种以上类型的类型类。这允许您创建一个依赖于s a的类。

最后一点:使用类型类可能根本不是您的示例的正确选择。您打算为该Safe课程编写更多类型吗?如果你不是,那么你根本不应该使用类型类。但是,学习一点关于多参数类型类的知识仍然很有用,因此您应该考虑在某个时候使用它们。

于 2012-09-21T18:47:24.673 回答