为什么下面的列表推导式可以赋值给变量,但列表推导式无效?

问题描述 投票:0回答:1

在 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

这是为什么?

haskell
1个回答
0
投票

您能够在 GHCi 中这样定义它的原因是您没有指定它应该是什么类型。好的做法是始终将类型签名添加到顶级定义中——尽管公平地说,在 GHCi 中不这样做是很常见的。

看,您可以写出的一些表达式具有虚假类型,尽管它们会进行类型检查。这是一个例子,它的类型是

ghci> :t [2:x | x <- [1, 1, 1]]
(Num a, Num [a]) => [[a]]

这是什么意思?好吧,在解释之前,让我解释一下一些更简单的表达式的类型,这实际上确实有意义:

ghci> :t 'x'
Char

这个应该是不言自明的。

ghci> :t 37
Num a => a

这个也许不是。难道不应该是像

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
    (+)         x y     =  plusDouble x y
    (-)         x y     =  minusDouble x y
    negate      x       =  negateDouble x
    (*)         x y     =  timesDouble x y
    abs         x       =  fabsDouble x
    signum x | x > 0     = 1
             | x < 0     = negateDouble 1
             | otherwise = x -- handles 0.0, (-0.0), and NaN

    {-# INLINE fromInteger #-}
    fromInteger i = D# (integerToDouble# i)

通常,每当您尝试在没有

Num
实例的类型上下文中使用数字文字时,编译器都会给出错误。在您的示例中,上下文需要一个列表。具有相同问题的更简单的示例是

reverse 5 :: [Integer]

嗯,常识告诉我们列表不是数字,因此与

Num
不兼容。但编译器并不知道这一点,实际上理论上您可以定义这样一个实例! (这样做,我只是在这里唱反调) instance Num [Integer] where fromInteger i = [523,i,916]

然后你的代码会突然工作......我的意思是,不是“工作”意义上的做正确的事情,而是在没有错误消息的情况下进行评估:

ghci> reverse 5 :: [Integer] [916,5,523] 因为实际上并没有

instance Num [Integer]
,所以你可能会认为这样写通常会出错。然而,Haskell 的类型类机制的一个怪癖是,在将实例缩小到特定类型之前,您永远无法确定实例是否不存在。但是当你简单地定义时

a = reverse 5

然后编译器将首先尝试为其提供最通用的类型,并附加任何要求(无论是否无意义)作为约束。

ghci> :t a
a :: Num [a] => [a]

实际的解决方案当然是

不使用数字作为列表
。也许你真正想做的是将数字用作列表
elementa

,例如你可以这样: ghci> [[2,x] | x <- [1, 1, 1]] [[2,1],[2,1],[2,1]]

© www.soinside.com 2019 - 2024. All rights reserved.