14

我想知道为什么在这种情况下:sprint报告:xs = _

Prelude> xs = map (+1) [1..10]
Prelude> length xs
10
Prelude> :sprint xs
xs = _

但在这种情况下不是:

Prelude> xs = map (+1) [1..10] :: [Int]
Prelude> length xs
10
Prelude> :sprint xs
xs = [_,_,_,_,_,_,_,_,_,_]

注意:我正在ghci运行-XNoMonomorphismRestriction. 这是否与xs第一种情况下的类型是多态的但在第二种情况下无关的事实有关?我想知道内部发生了什么。

4

1 回答 1

10

要点是具有多态性的xs它具有一种形式

xs :: Num a => [a]

引擎盖下的类型类实际上只是函数,它们采用 GHC 自动填充的额外参数,其中包含类型类函数的记录。所以你可以想到xs有类型

xs :: NumDict a -> [a]

所以当你跑

Prelude> length xs

它必须为 选择一些值a,并找到相应的NumDict值。IIRC 它会用 填充它Integer,所以你实际上是在调用一个函数并检查结果列表的长度。

当你 then 时:sprint xs,你再次填写那个参数,这次是一个新的类型变量。但关键是你得到了一个完全不同的列表,你给了它一个不同NumDict的列表,所以当你之前打电话时它不会以任何方式被强迫length

这与显式单态列表非常不同,因为那里真的只有一个列表,只有一个值可以强制,所以当你调用 length 时,它会强制它用于xs.

为了使这一点更清楚,请考虑代码

data Smash a = Smash { smash :: a -> a -> a }
-- ^ Think of Monoids

intSmash :: Smash Int
intSmash = Smash (+)

listSmash :: Smash [a]
listPlus = Smash (++)

join :: Smash a -> [a] -> a
join (Smash s) xs = foldl1' s xs

这就是类型类的本质,GHC 会自动Smash a为我们填充第一个参数。现在您的第一个示例就像join,我们无法对输出将是什么做出任何假设,因为我们将其应用于不同类型,但是您的第二个示例更像

join' :: [Int] -> Int
join' = join intSmash
于 2014-02-03T05:52:31.390 回答