3

在 中敲打ghci,我碰巧注意到这个表达式(*) 1 [1..5]显然有一个有效的类型。

:t (*) 1 [1..5]
(*) 1 [1..5] :: (Enum t, Num [t], Num t) => [t]

显然它是一个包含多个类型约束的列表,包括Num [t]对我来说看起来不可能的,就像它应该给出一个错误一样。

这是表达式的类型?为什么ghci's:t命令在这里没有给出错误?

4

3 回答 3

11

Num [t]不仅可能,而且很容易:

import Control.Applicative
liftA0 = pure -- hobgoblins, simple minds, etc.
liftA1 = fmap
instance Num t => Num [t] where
    (+) = liftA2 (+)
    (-) = liftA2 (-)
    (*) = liftA2 (*)
    negate = liftA1 negate
    abs    = liftA1 abs
    signum = liftA1 signum
    fromInteger n = liftA0 (fromInteger n)

因此,如果 GHC 产生错误而不是推断您的表达式可以用适当的实例很好地键入,那将是可怕的。

当然,用真实的代码编写这个实例也是很糟糕的,但 GHC 不应该像我们人类那样对代码做出判断。

于 2017-09-15T04:02:45.747 回答
3

让我们看看这些约束是如何解释类​​型的。

数字

在 Haskell 中,文字数字被替换为调用fromInteger(或者fromRational如果它有小数点或“e”)。这样,一个人可以写 '1' 并将其设置为 float 或 double 或 int 或其他。的类型fromInteger

fromInteger :: Num a => a

所以1被脱糖到fromInteger (1::Integer)哪个有类型Num t => t

范围

在 Haskell 中,语法[a..b]被转换为调用enumFromTo a b,类型为enumFromTo :: Enum a => a -> a -> [a]. 把这些放在一起,我们得到

[1..5] == enumFromTo (fromInteger 1) (fromInteger 5) :: (Enum a, Num a) => [a]

把它们放在一起

现在的类型(*)Num b => b -> b -> b,所以我们将这些组合在一起得到:

(Num t,
Num a,
Enum a,
Num b,
t~b,
[a]~b) => b

请注意,这a~b意味着类型ab是相同的。结合这些给出了类型

(Num a, Enum a, Num [a]) => [a]
于 2017-09-15T19:14:24.163 回答
0

这种惯用/应用提升模式存在于Data.Monoid.ApwhereAp [] a指定提升操作的地方pure,fmapliftA2: (+) = liftA2 (+):

>> :set -XDerivingVia
>> :set -XStandaloneDeriving
>>
>> import Data.Monoid (Ap(..))
>>
>> deriving via Ap [] a instance Num a => Num [a]
>>
>> 1 * [1..5]
[1,2,3,4,5]
>> [100,200] * [1..5]
[100,200,300,400,500,200,400,600,800,1000]

列表的行为是通过Ap [] a. 您通过以下方式获得不同的应用程序行为ZipList

>> import Control.Applicative (ZipList(..))
>> 
>> deriving via Ap ZipList a instance Num a => Num [a]
>>
>> 1 * [1..5]
[1,2,3,4,5]
>> [100,200] * [1..5]
[100,400]
于 2021-09-12T18:46:38.137 回答