3

我在用 Learn You a Haskell 中的一个例子开玩笑,我不确定出了什么问题。这是模仿真/假语义的原始示例:

class YesNo a where
    yesno :: a -> Bool

一个简单的例子由下式给出:

instance YesNo Int where
    yesno 0 = False
    yesno _ = True

然后后来:

instance YesNo (Maybe a) where
    yesno (Just _) = True
    yesno Nothing = False

这在一定程度上是有道理的,但我觉得这个想法yesno (Just False) == True有点违反直觉,所以我试着像这样修改它:

instance YesNo (Maybe a) where
    yesno (Just b) = yesno b
    yesno Nothing = False

因此,在 Maybe 实例包含一个值的情况下,我们可以得到该值本身的真实性。但是,这会失败并出现错误No instance for (YesNo a) arising from a use ofyesno'`。我究竟做错了什么?

4

3 回答 3

3

您需要告诉编译器该类型a必须有一个YesNo实例:

instance YesNo a => YesNo (Maybe a) where
    yesno (Just a) = yesno a
    yesno Nothing  = False

测试:

> yesno (Just False)
False
于 2013-10-25T23:50:57.773 回答
0

您可以创建一个YesNofor的实例,该实例Maybe a仅在a也是 的实例时才有效YesNo

instance (YesNo a) => YesNo (Maybe a) where
    yesno (Just b) = yesno b
    yesno Nothing = False

说, “(YesNo a) =>对于每种类型a,这a都是”的一个实例YesNo。整个实例声明的内容类似于,“对于每个类型a,例如a的实例YesNoMaybe a也是YesNowhere 的实例......

于 2013-10-25T23:51:47.287 回答
0

因此,在Maybe 实例包含我们得到的值的情况下......

这在 Haskell 中不能很好地完成。类型类实例不应该读作“我现在将此数据类型从非实例集转移到实例集”,而是“我现在描述如何使用该类型作为该类的实例”。没有真正不是类的实例的概念,只是找不到实例。

因此,如果您想根据 中包含的值来决定Maybe,您可能应该按照 Cirdec 和 Mikhail Glushenkov 的建议进行操作instance YesNo a => YesNo (Maybe a):当然,这意味着 eg不会Maybe ()是一个实例。对于所有类型,您要么获得相同的行为,要么获得另一种,但对于特定类型集的行为又完全相同。但是根据类型是否在类中,您无法获得不同的行为。YesNo

实际上有一种方法可以实现这一点,但它有点不受欢迎:

{-# LANGUAGE OverlappingInstances #-}

instance YesNo (Maybe a) where
  yesno (Just _) = True
  yesno Nothing = False

newtype YesNo_ a = YesNo_ a

instance YesNo a => YesNo (Maybe (YesNo_ a)) where
  yesno (Just (YesNo_ a)) = yesno a
  yesno Nothing  = False

这不仅麻烦,而且还需要一些不安全的OverlappingInstances扩展。

于 2013-10-26T00:06:18.123 回答