6 haskell
在 ghci 终端中,允许进行以下变量赋值:
a = [2:x | x <- [1, 1, 1]]
但是a打印的时候出现如下错误:
- No instance for (Num [Integer]) arising from a use of 'it'
- In the first argument of 'print', namely 'it'
- In a stmt of an interactive GHCi command: print it
为什么是这样?
您能够在 GHCi 中这样定义它的原因是您没有指定它应该是什么类型。好的做法是始终将类型签名添加到顶级定义 \xe2\x80\x93 中,但公平地说,在 GHCi 中不这样做是很常见的。
\n看,您可以写出的某些表达式具有虚假类型,尽管它们会进行类型检查。您的代码是一个示例,其类型是
\nghci> :t [2:x | x <- [1, 1, 1]]\n(Num a, Num [a]) => [[a]]\n这意味着什么?好吧,在解释它之前,让我解释一下一些更简单的表达式的类型,它实际上确实有意义:
\nghci> :t \'x\'\nChar\n这一点应该是不言自明的。
\nghci> :t 37\nNum a => a\n这也许不是不言自明的。难道不应该是简单的事情Int吗?
嗯,诀窍是,Haskell 的数字文字更加灵活。它们根本不必具有任何特定类型,而是采用上下文所需的任何类型。原因是它允许您编写类似的内容sqrt 2并让结果为类型Double。在这种情况下,文字2也具有类型Double。(其他语言通过从Int到 的自动转换来实现这一点Double,但是它有很多缺点,我不会在这里讨论。)
编译器施加的唯一条件是上下文所需的类型必须具有该类的实例Num。在这种情况下Double,该实例看起来像这样(https://hackage.haskell.org/package/base-4.19.0.0/docs/src/GHC.Float.html#line-545),有一堆可怕的低-您无需担心的关卡详细信息:
instance  Num Double  where\n    (+)         x y     =  plusDouble x y\n    (-)         x y     =  minusDouble x y\n    negate      x       =  negateDouble x\n    (*)         x y     =  timesDouble x y\n    abs         x       =  fabsDouble x\n    signum x | x > 0     = 1\n             | x < 0     = negateDouble 1\n             | otherwise = x -- handles 0.0, (-0.0), and NaN\n\n    {-# INLINE fromInteger #-}\n    fromInteger i = D# (integerToDouble# i)\n通常,每当您尝试在没有实例的类型上下文中使用数字文字时,编译器都会给您一个错误Num。在您的示例中,上下文需要一个列表。具有相同问题的更简单的示例是
reverse 5 :: [Integer]\n好吧,常识告诉我们列表不是数字,因此与Num. 但编译器并不知道这一点,实际上理论上您可以定义这样的实例!(别这么做,我只是在这里唱反调)
instance Num [Integer] where\n  fromInteger i = [523,i,916]\n然后你的代码会突然工作......我的意思是,不是在做正确的事情的意义上工作,而是在没有错误消息的情况下进行评估:
\nghci> reverse 5 :: [Integer]\n[916,5,523]\n因为实际上不存在instance Num [Integer],所以您可能会认为编写此代码通常会出错。然而,Haskell 的类型类机制的一个怪癖是,在将实例缩小到特定类型之前,您永远无法确定实例是否不存在。但是当你简单地定义
a = reverse 5\n那么编译器将首先尝试为其提供最通用的类型,并附加任何要求(无论是否无意义)作为约束。
\nghci> :t a\na :: Num [a] => [a]\n实际的解决方案当然是不使用数字作为列表。也许您真正想要做的是将数字用作列表elementa,例如您可以这样:
\nghci> [[2,x] | x <- [1, 1, 1]]\n[[2,1],[2,1],[2,1]]\n