dataToTag参数的严格性

And*_*tin 14 haskell ghc

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

dataToTag# :: a -> Int#
Run Code Online (Sandbox Code Playgroud)

它根据它使用的数据构造函数将任何类型的值转换为整数.这是用来加速的衍生实现Eq,Ord和Enum.在GHC源代码中,用于dataToTag#解释参数应该已经通过评估的文档:

dataToTag#primop应始终应用于已计算的参数.确保这一点的方法是通过GHC.Base中的'getTag'包装器调用它:

getTag :: a -> Int#
getTag !x = dataToTag# x
Run Code Online (Sandbox Code Playgroud)

对我来说,x在dataToTag#调用之前我们需要强制进行评估是完全合理的.我没有得到的是为什么爆炸模式就足够了.定义getTag只是语法糖:

getTag :: a -> Int#
getTag x = x `seq` dataToTag# x
Run Code Online (Sandbox Code Playgroud)

但是,让我们转向seq的文档:

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

在软件包的Control.Parallel模块中parallel,文档进一步阐述:

...序列是它的两个参数严格,所以编译器可以,例如,重新a `seq` b进入b `seq` a `seq` b......

getTag如果seq不足以控制评估顺序,那么如何才能保证工作正常?

dfe*_*uer 16

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

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

if n < bound
then unsafeIndex c n
else error "out of range"
Run Code Online (Sandbox Code Playgroud)

被编译成

case unsafeIndex v n of
  !x -> if n < bound
        then x
        else error "out of range"
Run Code Online (Sandbox Code Playgroud)

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

dataToTag#标记为can_fail.所以GHC看到(在Core中)类似的东西

getTag = \x -> case x of
           y -> dataToTag# y
Run Code Online (Sandbox Code Playgroud)

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