【问题标题】:Typeclass constraint inside typeclass instance类型类实例内的类型类约束
【发布时间】:2018-07-10 08:14:27
【问题描述】:

您好,在做 Real World Haskell 书中的示例时,我遇到了这个示例,但我无法理解它的含义以及它是如何工作的:
instance Num a=>Num (SymbolicManip a)
在这种情况下,我应该翻译成类似:“对于类型为 SymbolicManip 的 Num 实例,有一个关于其类型为 a 的字段的约束,即:a 是 Num 本身的一个实例”? 有人可以告诉我我的解释是否正确或解释吗?
为什么instance Num (SymbolicManip a) 不够?

-- The "operators" that we're going to support
data Op = Plus | Minus | Mul | Div | Pow
        deriving (Eq, Show)

{- The core symbolic manipulation type -}
data SymbolicManip a = 
          Number a           -- Simple number, such as 5
        | Arith Op (SymbolicManip a) (SymbolicManip a)
          deriving (Eq, Show)

{- SymbolicManip will be an instance of Num.  Define how the Num
operations are handled over a SymbolicManip.  This will implement things
like (+) for SymbolicManip. -}
instance Num a => Num (SymbolicManip a) where
    a + b = Arith Plus a b
    a - b = Arith Minus a b
    a * b = Arith Mul a b
    negate a = Arith Mul (Number (-1)) a
    abs a = error "abs is unimplemented"
    signum _ = error "signum is unimplemented"
    fromInteger i = Number (fromInteger i)

P.S所有代码均来自本书(chapter 13 - subchapter-Extended Example-Numeric Types)

【问题讨论】:

  • 尝试在没有 Num 约束的情况下编译代码,看看会发生什么。

标签: haskell typeclass


【解决方案1】:

重要的是要看到SymbolicManip a 不能是Num 的实例,而a 也不能是Num 的实例所以,就像我们在函数中添加约束一样,我们可以将约束添加到类型类:

 instance Num a => Num (SymbolicManip a) where
 --       ^^^^^^^^ "As long as `a` is an instance of `Num`..."
 --                ^^^^^^^^^^^^^^^^^^^^^ "...so is `SymbolicManip a`"

我们必须包含Num a => 约束,因为在实现中,我们使用fromInteger 来生成a 类型的成员。这是不可避免的,就像在函数example a b = 2*a + b 中添加Num 约束,即example :: Num a => a -> a -> a。

这是一个更简单的例子。考虑这种类型:

newtype Identity a = Identity a

注意Identity a 可以是Num 的实例,只要a 也是Num,所以,我们添加一个约束:

instance Num a => Num (Identity a) where
-- (...)

【讨论】:

  • 实例定义不对“a”类型的值使用 (+) 和 (-)。但是它确实使用 fromInteger。
  • “我们必须包含Num a =>约束”可能有点夸大其词。就像其他算术运算被显式表示一样,可以更改 SymbolicManip 以便显式表示 fromInteger - 例如通过将Number a 构造函数替换为Number Integer。 (然后可以完全删除类型参数,或者通过添加 Literal a 构造函数或类似的方法来允许嵌入。)(1/2)
  • 当然,您最终可能仍然需要eval :: Num a => SymbolicManip a -> a,而这里Num 实例更明显需要,不仅用于fromInteger,还用于其他算术运算。 (2/2)
【解决方案2】:

这意味着如果a 是Num 的一个实例,那么SybolicManip a 也是Num 的一个实例。

如果你有:

x :: SymbolicManip Integer
y :: SymbolicManip Integer

然后你可以写x+y 而无需定义它的含义。但是,如果您尝试添加 SymbolicManip String 的两个值,则会出现类型错误,因为 String 不是 Num 的实例。

如果您查看实例,您会看到negate 和fromInteger 都使用Num 的功能。因此,如果您取消约束,那么编译器将给出无法推断出a 是Num 的实例的错误。

【讨论】:

    猜你喜欢
    • 2014-10-02
    • 1970-01-01
    • 2018-06-15
    • 2012-09-06
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多