繁体   English   中英

测试值是否已评估为弱头正常形式

[英]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.StableNameStableName提供的,并且用于解决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 ClosureRtClosureInspect 真空包只依赖于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”命令共享一个实现,但是对于一个名为forceBool标志,并按照定义直到我得到RtClosureInspect.cvObtainTerm ,这似乎是专业评估员。

最近有一个提案,也许已经实现了某个地方https://mail.haskell.org/pipermail/libraries/2015-February/024917.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM