【问题标题】::sprint for polymorphic values?:冲刺多态值?
【发布时间】:2014-02-03 01:41:02
【问题描述】:

我想知道为什么: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 的类型在第一种情况下是多态的但在第二种情况下无关这一事实有关?我想知道内部发生了什么。

【问题讨论】:

  • 这正是单态限制旨在防止的那种混淆,所以你应该绝对阅读wiki page on it

标签: haskell ghci thunk monomorphism-restriction


【解决方案1】:

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

【讨论】:

  • 这个解释很难理解。
  • @mljrg 任何特定的部分?
  • 由于类型类和“NumDict”内容的内部工作方式没有详细说明和举例说明,因此很难理解您的答案。你能给出一个链接,这些东西解释得很好吗?无论如何,似乎 :sprint 正在遭受一些实现副作用,这对 Haskell 来说具有讽刺意味......请注意,如果 length xs 必须浏览整个列表,那么 :sprint 应该总是打印 [_,_, ...] 而不管列表中元素的类型。周围有一些错误...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-26
相关资源
最近更新 更多