14

问题 1

嗨,如果在 WinGHCi 中我故意执行以下错误的代码:

3 4

然后我得到的错误信息是

<interactive>:1:1:
    No instance for (Num (a0 -> t0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a0 -> t0))
    In the expression: 3
    In the expression: 3 4
    In an equation for `it': it = 3 4

究竟是什么No instance for (Num (a0 -> t0))意思?

问题2

为什么下面的代码:

(+) 2 3 4
<interactive>:1:7:
    No instance for (Num (a0 -> t0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a0 -> t0))
    In the second argument of `(+)', namely `3'
    In the expression: (+) 2 3 4
    In an equation for `it': it = (+) 2 3 4

产生与第二段代码略有不同的错误:

2+3 4
<interactive>:1:3:
    No instance for (Num (a1 -> a0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a1 -> a0))
    In the expression: 3
    In the second argument of `(+)', namely `3 4'
    In the expression: 2 + 3 4

即在第一段代码中,我们No instance for (Num (a0 -> t0))在第二段代码中拥有 where No instance for (Num (a1 -> a0))


[对 ehird 的回应]

(从答案评论中移出的问题):

1)我很欣赏后两种表达方式的不同,但你是说我不应该试图理解为什么解释器选择(Num (a0 -> t0))前者和(Num(a1 -> a0))后者,除了它们不同的事实吗?

2)嗨,当你说“但没有函数的 Num 实例”时,前者是什么意思?抱歉,我不清楚实例的概念是什么。此外,出于好奇,您能否使用您的实例Num (a -> b)方法以某种方式告诉解释器解释3 44 modulo 3

4

2 回答 2

16

我的意图是用更多的解释来补充 ehird 的回答。当你写下表达式

3 4

然后 Haskell 解释器认为您正在尝试将该函数3应用于任何4内容。为了让 Haskell 解释3为函数,它需要调用函数

fromInteger :: Integer -> (a -> b)

为了a -> b从整数中获取函数(即类型的东西)3。现在,在类型类fromInteger中定义Num为具有签名

instance Num x where
    fromInteger :: Integer -> x

即,当您将类型x设置为类的实例时Num,您给出了一个实现,fromInteger它告诉 Haskell 如何将整数文字转换为x. 在您的情况下,x是函数类型a -> b。所以让我们开始吧!


首先,一些样板。要创建Haskellx的实例Num,我们还需要将其创建为Showand的实例Eq

instance Show (a -> b) where show f = "<function>"
instance Eq (a -> b) where f == g = False

现在假设我们要解释3 4为“4 模 3”。然后我们需要告诉 Haskell 如何将任何整数解释为调用mod. 此外,由于mod只接受整数类型(它有签名mod :: Integral a => a -> a -> a),那么我们需要限制 和 的类型也是a整数b

instance (Integral a, Integral b) => Num (a -> b) where

创建一个实例,Num我们需要给出 、 和 的实现(+)(-)实际上(*)我们fromIntegral也应该定义几个其他函数,但现在不用担心)。

有一种相当自然的方式来定义加法、减法和乘法(这里的所有代码都是Num实例的一部分,应该相对于实例声明缩进)

    f + g = \x -> f x + g x
    f - g = \x -> f x - g x
    f * g = \x -> f x * g x

即,当您添加两个函数fand时g,您会得到一个新函数,该函数将fandg应用于其参数,然后将它们相加。由于我们要求应用的结果fg整数类型,因此我们知道将它们的输出相加是有意义的。

要将整数解释为函数,我们可以编写

    fromInteger n = \m -> fromIntegral m `mod` fromIntegral n

即,当我们有一个整数时n,我们返回一个参数的函数m,当调用该参数时,确保两个参数的类型相同(通过调用fromIntegral它们),然后将它们用作函数的参数mod

最后,更多样板来阻止 Haskell 抱怨:

    abs f = undefined
    signum f = undefined

我们可以对此进行测试。我的代码在一个名为 numfun.hs 的文件中。我启动 Haskell 解释器并加载我的文件:

Prelude> :l numfun.hs
[1 of 1] Compiling Main             ( numfun.hs, interpreted )
Ok, modules loaded: Main.

现在我可以定义一些函数:

*Main> let f = (+ 1)
*Main> let g = (* 2)

我可以添加或减去它们:

*Main> (f + g) 3   -- (3+1) + (3*2)
10
*Main> (f - g) 3   -- (3+1) - (3*2)
-2

我可以将数字称为函数:

*Main> 3 4         -- 4 `mod` 3
1
于 2012-04-30T09:13:18.670 回答
14

发生第一个错误是因为整数文字4可以是具有Num实例的任何类型。也就是说,4具有类型(Num a) => a,因此它可以用作 an Integer、 a Double、 aRational等。由于您应用于3参数 ( 4),它知道在上下文中3必须是函数类型(即a0 -> t0对于 some a0and t0)。但是没有函数的Num实例,所以你3作为函数的使用是无效的。如果您添加了instance Num (a -> b),它会起作用,但您可能不想这样做。

至于后者,两个错误信息是等价的;GHC 生成的名称没有特殊含义。这些字母通常是从您正在使用的函数类型中的类型变量派生的,并且附加了数字以保持明确。在这种情况下,第二个表达式等价于(+) 2 (3 4)(因为函数应用程序比任何中缀运算符绑定得更紧密),这与您的第一段代码不太一样。

于 2012-04-29T20:14:55.263 回答