由于缺少实例,无法实例化类

时间:2013-03-13 15:35:30

标签: haskell typeclass

我有这个haskell代码。我已经创建了两种数据类型,然后我想创建一个新的类Mord,它可以将函数与Mlist类型进行比较。

import Data.List

data Mlist a = Mlist [a]

data Mordering = MLT deriving (Eq, Show)

s = Mlist [1, 2, 3]
t = Mlist [1, 4, 2, 3]

class Mord a where 
    mcompare :: a -> a -> Mordering


instance Mord a => Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys) = MLT

但是如果我尝试mcompare s t我就会

<interactive>:1:1:
    No instance for (Mord Integer)
      arising from a use of `mcompare'
    Possible fix: add an instance declaration for (Mord Integer)
    In the expression: mcompare s t
    In an equation for `it': it = mcompare s t

有没有人看到这个问题?

修改

这是我的新代码:

import Data.List

data Mlist a = Mlist [a]

data Mordering = MEQ | MIN deriving (Eq, Show)

s = Mlist [1, 2, 3]
t = Mlist [1, 4, 2, 3]

class Mord a where 
    mcompare :: a -> a -> Mordering

instance Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys)
           | length xs == length ys && null (xs \\ ys) = MEQ
           | otherwise = MIN

但我现在得到的错误是:

    No instance for (Eq a)
      arising from a use of `\\'
    In the first argument of `null', namely `(xs \\ ys)'
    In the second argument of `(&&)', namely `null (xs \\ ys)'
    In the expression: length xs == length ys && null (xs \\ ys)
Failed, modules loaded: none.

3 个答案:

答案 0 :(得分:4)

Haskell读到了这个:

instance Mord a => Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys) = MLT

含义“对于我有a实例的每个Mord,我也有一个Mord Mlist a实例。但是,如果我不这样做Mord有一个a个实例,我Mord也没有Mlist a个实例。抱歉,让我们有时再出去玩!“

实例声明的Mord a部分称为上下文;您可以在A Gentle Introduction to Haskell中了解详情。

实例上下文对于类型构造函数很有用,例如代码中的[]Mlist。例如,我们可以查看标准Eq class[a]的{​​{3}}:

instance (Eq a) => Eq [a] where
    []     == []     = True
    (x:xs) == (y:ys) = x == y && xs == ys
    _xs    == _ys    = False

这个例子意味着什么?两个空列表是相同的;如果两个非空列表的头部相等且它们的尾部相等,则它们是相等;任何其他两个非空列表都是不相等的;一个空的非空列表是不相等的。

我加粗了上一段的部分内容,因为它有助于回答“为什么实例上下文有用?”好吧,在列表示例中,列表相等的部分定义来自列表的结构,但另一部分基于其元素的相等性。换句话说,除非可以比较其元素的相等性,否则无法比较列表的相等性。

函数是值的经典示例,无法对相等性进行有意义的比较。那么我们如何检查函数列表是否相等?

ghci> (+1) == (*2)
<interactive>:6:6:
    No instance for (Eq (a0 -> a0))
      arising from a use of `=='
    Possible fix: add an instance declaration for (Eq (a0 -> a0))
    In the expression: (+ 1) == (* 2)
    In an equation for `it': it = (+ 1) == (* 2)

ghci> [(+4)] == [\x -> 2 * x / 3]
<interactive>:8:8:
    No instance for (Eq (a0 -> a0))
      arising from a use of `=='
    Possible fix: add an instance declaration for (Eq (a0 -> a0))
    In the expression: [(+ 4)] == [\ x -> 2 * x / 3]
    In an equation for `it': it = [(+ 4)] == [\ x -> 2 * x / 3]

因此,要使代码正常工作,您可以删除未使用的实例上下文,或者您需要提供适当的基础实例,即:

instance Mord Int where
    mcompare ... = ...

s :: Mlist Int
s = Mlist [1, 2, 3]
t :: Mlist Int
t = Mlist [1, 4, 2, 3]

答案 1 :(得分:3)

instance Mord a => Mord (Mlist a) where

此处您说Mlist aMord的实例,当且仅当aMord的实例时。 Integer不是Mord的实例,因此Mlist Integer也不是Mord的实例。如果您定义实例Mord Integer,您的代码将起作用。

响应您的编辑:您的新代码不起作用,因为\\仅适用于可以进行相等性比较的值列表(即作为Eq类型类实例的类型的值)。由于您的实例没有Eq约束,因此您说它应该适用于所有类型,但由于\\不适用于所有类型,因此您不能这样做。向您的实例添加Eq约束(即instance (Eq a) => Mord (Mlist a) ...)将解决您的问题。

答案 2 :(得分:0)

我将放大一个特定点,其他答案已经涵盖了当前的问题。

假设您已经设法使用到目前为止所获得的帮助,并且您启动了GHCi并加载了代码。我们试一试:

ghci> mcompare s t
MLT
ghci> mcompare t s
MLT

嗯。这对我来说似乎没什么用处!发生了什么事?

如果我们看一下mcompare在给出两个Mlist时实际做了什么 - 确切地说,在此实例声明中:

instance Mord a => Mord (Mlist a) where
    mcompare (Mlist xs) (Mlist ys) = MLT

我们可以看到根本没有比较;无论xsys是什么,mcompare只返回MLT。以某种方式合乎逻辑,因为Mordering数据类型只有一个值:

data Mordering = MLT deriving (Eq, Show)

但是为了表示比较操作的结果,通常需要3个可能的结果:第一个参数小于,等于或大于第二个参数。你可能会说这也是一个“无法比拟”的选择,但是我们不要进入那里!

所以你希望你的Mordering数据类型包含这三个比较结果,你可以通过添加一些更多的构造函数来实现(这是我在sepp2k的回答的评论中所说的),如: / p>

data Mordering = LessThan 
               | EqualTo 
               | GreaterThan 
                   deriving (Eq,Show)

(我猜你的意思可能是data Mordering = MLT | MEQ | MGT deriving (Eq, Show)?)

这会引发一系列新问题,我会给你一些提示,以帮助你。


首先,我们希望mcompare 3 4LessThanmcompare 5 5EqualTomcompare 7 6GreaterThan,因为3更少比4(等等)。要实现此目的,您需要Mord Integer的实例:

instance Mord Integer where
    mcompare x y
        | x <  y = ...
        | x == y = ...
        | x >  y = ...

填补空白不应该太难,我希望!

第二部分有点棘手:假设我们可以比较a类型的值,我们如何比较Mlist a类型的值?换句话说,应该如何写出instance Mord a => Mord (Mlist a)声明? mcompare (Mlist xs) (Mlist ys)的结果将取决于xs是否为空,ys是否为空,如果两者都不为空,则取决于xs的第一个元素和{ys 1}}比较以及其余元素的比较方式。

我会留下让你弄明白的;不要犹豫,问一个新的问题,显示你有多远,你被困在哪里!