要点是具有多态性的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