dataToTag参数的严格性

时间:2017-07-11 19:27:18

标签: haskell ghc

GHC.Prim中,我们找到了一个名为dataToTag#的神奇函数:

dataToTag# :: a -> Int#

它根据它使用的数据构造函数将任何类型的值转换为整数。这用于加速EqOrdEnum的派生实现。在GHC源代码中,docs for dataToTag#解释了该参数应该已经通过评估:

  

dataToTag#primop应始终应用于已评估的参数。   确保这一点的方法是通过' getTag'来调用它。在GHC.Base包装:

getTag :: a -> Int#
getTag !x = dataToTag# x

我觉得我们需要在调用x之前强制dataToTag#的评估。我没有得到的是为什么爆炸模式就足够了。 getTag的定义只是语法糖:

getTag :: a -> Int#
getTag x = x `seq` dataToTag# x

但是,让我们转向docs for seq

  

关于评估顺序的注释:表达式seq a b不保证在b之前评估a。 seq给出的唯一保证是在seq返回值之前将评估a和b两者。特别是,这意味着可以在a之前评估b。如果您需要保证特定的评估顺序,则必须使用" parallel"中的函数pseq。封装

Control.Parallel包的parallel模块中,文档elaborate further

  

... seq在两个参数中都是严格的,因此编译器可以将a `seq` b重新排列为b `seq` a `seq` b ...

鉴于getTag不足以控制评估顺序,seq如何保证行事有效?

1 个答案:

答案 0 :(得分:16)

GHC跟踪有关每个primop的某些信息。一个关键数据是primop" can_fail"。这个标志的最初含义是如果一个primop可能导致硬故障,它就会失败。例如,如果索引超出范围,则数组索引可能会导致分段错误,因此索引操作可能会失败。

如果一个primop失败,GHC会限制它周围的某些转换,特别是不会将它从任何case表达式中浮出来。这将是相当糟糕的,例如,如果

if n < bound
then unsafeIndex c n
else error "out of range"

编译为

case unsafeIndex v n of
  !x -> if n < bound
        then x
        else error "out of range"

其中一个底部是个例外;另一个是段错误。

dataToTag#标记为can_fail。所以GHC看到(在Core中)类似

getTag = \x -> case x of
           y -> dataToTag# y

(请注意,case在Core中是严格的。)因为dataToTag#被标记为can_fail,所以它不会从任何case表达式中浮出。