在GHC.Prim,我们找到一个名为dataToTag#的神奇函数:
dataToTag# :: a -> Int#
Run Code Online (Sandbox Code Playgroud)
它根据它使用的数据构造函数将任何类型的值转换为整数.这是用来加速的衍生实现Eq,Ord和Enum.在GHC源代码中,用于dataToTag#解释参数应该已经通过评估的文档:
dataToTag#primop应始终应用于已计算的参数.确保这一点的方法是通过GHC.Base中的'getTag'包装器调用它:
Run Code Online (Sandbox Code Playgroud)getTag :: a -> Int# getTag !x = dataToTag# x
对我来说,x在dataToTag#调用之前我们需要强制进行评估是完全合理的.我没有得到的是为什么爆炸模式就足够了.定义getTag只是语法糖:
getTag :: a -> Int#
getTag x = x `seq` dataToTag# x
Run Code Online (Sandbox Code Playgroud)
关于评估顺序的注释:表达式
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表达式.