[英]Test if a value has been evaluated to weak head normal form
在Haskell中,是否可以测试一个值是否已被评估为弱头正常形式? 如果一个函数已经存在,我希望它有一个像这样的签名
evaluated :: a -> IO Bool
有一些类似功能的地方。
之前的回答向我介绍了:sprint
ghci命令,该命令仅打印已被强制为弱头正常形式的值的一部分。 :sprint
可以观察是否已经评估了一个值:
> let l = ['a'..]
> :sprint l
l = _
> head l
'a'
> :sprint l
l = 'a' : _
在IO
可以检查否则将是禁止的属性。 例如,可以在IO
进行比较,以查看两个值是否来自同一声明。 这是由System.Mem.StableName
的StableName
提供的,并且用于解决data- StableName
中的可观察共享问题。 相关的StablePtr
没有提供检查引用值是否处于弱头正常形式的机制。
我不确定是否有预先包装好的东西。 但是,可以编写代码:
import Data.IORef
import System.IO.Unsafe
track :: a -> IO (a, IO Bool)
track val = do
ref <- newIORef False
return
( unsafePerformIO (writeIORef ref True) `seq` val
, readIORef ref
)
这是ghci中的一个示例用法:
*NFTrack> (value, isEvaluated) <- track (undefined:undefined)
*NFTrack> isEvaluated
False
*NFTrack> case value of _:_ -> "neat!"
"neat!"
*NFTrack> isEvaluated
True
当然,这将跟踪包装的 write-then-then-return-the-original-value thunk是否被评估为WHNF,而不是传递给track
的东西是否被评估为WHNF,所以你要将它作为尽可能接近您感兴趣的thunk - 例如,在跟踪开始之前,它无法告诉您其他人制作的thunk是否已被其他人评估过。 当然,如果你需要线程安全,可以考虑使用MVar
而不是IORef
。
unpackClosure#
实现:sprint
最终使用来自ghc-prim的unpackClosure#
来检查一个闭包。 这可以与堆对象格式的知识结合起来,以确定是否已经将闭包一直评估为弱头正常形式。
有几种方法可以重现ghci实现的检查:sprint
。 该GHC API公开getClosureData :: DynFlags -> a -> IO Closure
在RtClosureInspect
。 真空包只依赖于ghc-prim,从RtClosureInspect
再现代码并暴露getClosure :: a -> IO Closure
。 如何检查这些Closure
表示中的任何一个,例如,遵循间接性,并不是很明显。 所述GHC堆视图包检查封闭和暴露两者getClosureData :: a -> IO Closure
和所述的详细视图Closure
。 ghc-heap-view取决于GHC api。
我们可以根据ghc-heap-view中的getBoxedClosureData
来编写evaluated
。
import GHC.HeapView
evaluated :: a -> IO Bool
evaluated = go . asBox
where
go box = do
c <- getBoxedClosureData box
case c of
ThunkClosure {} -> return False
SelectorClosure {} -> return False
APClosure {} -> return False
APStackClosure {} -> return False
IndClosure {indirectee = b'} -> go b'
BlackholeClosure {indirectee = b'} -> go b'
_ -> return True
在评估黑洞时,这种黑洞封闭处理可能是不正确的。 选择器闭包的处理可能不正确。 AP闭合不是弱头正常形式的假设可能是不正确的。 所有其他闭包都在WHNF中的假设几乎肯定是不正确的。
我们的示例将需要两个并发线程在一个线程中观察另一个线程正在评估表达式。
import Data.Char
import Control.Concurrent
我们可以通过有选择地强制评估来避免任何unsafe
的事情,从而将信息横向传递出功能。 下面构建了一对thunk的流,我们可以选择强制其中一个或另一个。
mkBitStream :: Integer -> [(Integer, Integer)]
mkBitStream a = (a+2, a+3) : mkBitStream (a+1)
zero
强制第一个, one
强制第二个。
zero :: [(x, y)] -> [(x, y)]
zero ((x, _):t) = x `seq` t
one :: [(x, y)] -> [(x, y)]
one ((_, y):t) = y `seq` t
copy
是一种邪恶的身份函数,它具有基于检查数据强制流中的位的副作用。
copy :: (a -> Bool) -> [(x, y)] -> [a] -> [a]
copy f bs [] = []
copy f bs (x:xs) = let bs' = if f x then one bs else zero bs
in bs' `seq` (x:copy f bs' xs)
readBs
通过检查一对中的每个readBs
是否已被evaluated
readBs
读取我们的位流。
readBs :: [(x, y)] -> IO ()
readBs bs@((f, t):bs') = do
f' <- evaluated f
if f'
then putStrLn "0" >> readBs bs'
else do
t' <- evaluated t
if t'
then putStrLn "1" >> readBs bs'
else readBs bs
在打印时强制copy
具有打印关于读取字符串的信息的副作用。
main = do
let bs = mkBitStream 0
forkIO (readBs bs)
text <- getLine
putStrLn (copy isAlpha bs text)
getLine
如果我们运行程序并提供输入abc123
我们会观察到与检查每个字符是否为isAlpha
相对应的isAlpha
abc123
abc123
1
1
1
0
0
0
对于记录的否定答案:重用sprint
机制似乎不可行,因为它与解释的交互式评估紧密相关,而不是原始的运行时结构 - 据我所知; 我以前从未看过GHC内部。
我开始在GitHub上的GHC源代码中搜索“sprint”,结果是使用“print”命令共享一个实现,但是对于一个名为force
的Bool
标志,并按照定义直到我得到RtClosureInspect.cvObtainTerm ,这似乎是专业评估员。
最近有一个提案,也许已经实现了某个地方https://mail.haskell.org/pipermail/libraries/2015-February/024917.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.