我想知道为什么: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
的类型在第一种情况下是多态的而在第二种情况下不是多态有关吗?我想知道内部发生了什么。
答案 0 :(得分:10)
要点是多态xs
它有一种形式
xs :: Num a => [a]
引擎盖下的类型实际上只是函数,它们需要额外的参数,GHC自动填充包含类型类函数的记录。所以你可以想到xs
具有类型
xs :: NumDict a -> [a]
所以当你运行
Prelude> length xs
必须为a
选择一些值,并找到相应的NumDict
值。 IIRC它将用Integer
填充它,所以你实际上正在调用函数并检查结果列表的长度。
当你然后: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