Haskell:理解ghci中的"没有实例"错误消息

art*_*lla 13 haskell ghc ghci winghci

问题1

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

3 4
Run Code Online (Sandbox Code Playgroud)

然后我收到的错误消息是

<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
Run Code Online (Sandbox Code Playgroud)

究竟是什么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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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


[回应ehird]

(问题来自答案评论):

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

2)嗨,当你说"但是没有功能的Num实例"时,和前者一样,你的意思是什么?对不起,我不清楚实例的概念是什么.此外,出于好奇,您是否可以使用实例Num (a -> b)方法以某种方式告诉解释器解释3 44 modulo 3

Chr*_*lor 16

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

3 4
Run Code Online (Sandbox Code Playgroud)

然后Haskell解释器认为你试图将函数3应用于任何东西4.为了让Haskell解释3为一个函数,它需要调用该函数

fromInteger :: Integer -> (a -> b)
Run Code Online (Sandbox Code Playgroud)

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

instance Num x where
    fromInteger :: Integer -> x
Run Code Online (Sandbox Code Playgroud)

即,当您将类型x作为类的实例时Num,您将给出一个实现fromInteger,告诉Haskell如何将整数文本转换为x.在您的情况下,x是功能类型a -> b.那就让我们做吧!


首先,一些样板.要创建Haskell x的实例Num,我们还需要将它作为Show和的实例Eq:

instance Show (a -> b) where show f = "<function>"
instance Eq (a -> b) where f == g = False
Run Code Online (Sandbox Code Playgroud)

现在让我们说我们要解释3 4为"4模3".然后我们需要告诉Haskell如何将任何整数解释为调用的函数mod.而且,由于mod只接受整数类型(它有签名mod :: Integral a => a -> a -> a),所以我们需要限制类型ab整数:

instance (Integral a, Integral b) => Num (a -> b) where
Run Code Online (Sandbox Code Playgroud)

为了使一个实例Num,我们需要给的实现(+),(-),(*)fromIntegral(其实我们应该定义几个其他的功能太多,但让我们不要担心,现在).

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

    f + g = \x -> f x + g x
    f - g = \x -> f x - g x
    f * g = \x -> f x * g x
Run Code Online (Sandbox Code Playgroud)

即当您添加两个函数fg,你会得到既适用新的功能fg它的参数,然后将它们放在一起.由于我们要求应用的结果f并且g是整数类型,我们知道将它们的输出相加是有意义的.

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

    fromInteger n = \m -> fromIntegral m `mod` fromIntegral n
Run Code Online (Sandbox Code Playgroud)

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

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

    abs f = undefined
    signum f = undefined
Run Code Online (Sandbox Code Playgroud)

我们可以测试一下.我的代码名为numfun.hs.我启动Haskell解释器并加载我的文件:

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

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

*Main> let f = (+ 1)
*Main> let g = (* 2)
Run Code Online (Sandbox Code Playgroud)

我可以添加或减去它们:

*Main> (f + g) 3   -- (3+1) + (3*2)
10
*Main> (f - g) 3   -- (3+1) - (3*2)
-2
Run Code Online (Sandbox Code Playgroud)

我可以将数字称为函数:

*Main> 3 4         -- 4 `mod` 3
1
Run Code Online (Sandbox Code Playgroud)


ehi*_*ird 14

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

至于后者,两个错误消息是等价的; GHC生成的名称没有特别的意义.这些字母通常来自您正在使用的函数类型中的类型变量,并且附加数字以保持事物的明确性.在这种情况下,第二个表达式相当于(+) 2 (3 4)(因为函数应用程序比任何中缀运算符更紧密),这与您的第一段代码不完全相同.