我想知道为什么 :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 = [_,_,_,_,_,_,_,_,_,_]
注意:我正在使用 -XNoMonomorphismRestriction
运行 ghci
。这是否与 xs
的类型在第一种情况下是多态的但在第二种情况下不是有关?我想知道内部发生了什么。
最佳答案
要点是,对于多态 xs
,它具有以下形式的类型
xs :: Num a => [a]
底层的类型类实际上只是函数,它们采用 GHC 自动填充的额外参数,其中包含类型类函数的记录。所以你可以认为 xs
具有类型
xs :: NumDict a -> [a]
所以当你运行时
Prelude> length xs
它必须为a
选择一些值,并找到相应的NumDict
值。 IIRC 它将用 Integer 填充它,因此您实际上是在调用函数并检查结果列表的长度。
当您使用 :sprint
xs
时,您将再次填充该参数,这次使用新的类型变量。但关键是,您得到了一个完全不同的列表,您给了它一个不同的 NumDict
,因此当您之前调用 length
时,它不会以任何方式强制。
这与显式单态列表非常不同,因为那里实际上只有一个列表,只有一个要强制的值,因此当您调用 length 时,它会强制它用于 xs
的所有 future 使用.
为了让这一点更清楚,请考虑代码
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
关于haskell - :sprint for polymorphic values?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21518584/