Haskell 测试某个值是否已评估为弱水头标准形式

Haskell 测试某个值是否已评估为弱水头标准形式,haskell,lazy-evaluation,thunk,weak-head-normal-form,Haskell,Lazy Evaluation,Thunk,Weak Head Normal Form,在Haskell中,是否有可能测试一个值是否已被评估为弱头标准形式?如果函数已经存在,我希望它有一个签名,如 evaluated :: a -> IO Bool 有几个地方存在类似的功能 A向我介绍了ghci命令,该命令将仅打印已强制为弱头标准形式的值的部分:sprint可以观察值是否已计算: > let l = ['a'..] > :sprint l l = _ > head l 'a' > :sprint l l = 'a' : _ 在IO中,可以检查那些不

在Haskell中,是否有可能测试一个值是否已被评估为弱头标准形式?如果函数已经存在,我希望它有一个签名,如

evaluated :: a -> IO Bool
有几个地方存在类似的功能

A向我介绍了ghci命令,该命令将仅打印已强制为弱头标准形式的值的部分<代码>:sprint可以观察值是否已计算:

> let l = ['a'..]
> :sprint l
l = _
> head l
'a'
> :sprint l
l = 'a' : _

IO
中,可以检查那些不受限制的属性。例如,可以在
IO
中进行比较,查看两个值是否来自同一声明。这是由System.Mem.StableName中的s提供的,用于解决中的可观察共享问题。相关文件没有提供一种机制来检查引用值是否为弱头正常形式。

我不确定是否有任何预先打包的内容。但是,可以对其进行编码:

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
        )
import Data.IORef
导入System.IO不安全
轨迹::a->IO(a,IO布尔)
跟踪val=do

ref一个否定的答案,作为记录:重复使用
sprint
的机制似乎不可行,因为它与解释的交互评估紧密相连,而不是原始的运行时结构——据我所知;我以前从未看过GHC的内部结构

我首先在中搜索“sprint”,结果发现它与“print”命令共享一个实现,但有一个名为
force
Bool
标志,并遵循定义,直到找到了一个似乎是专门的评估器。

最终使用了来自ghc prim的命令来检查关闭。这可以与相关知识相结合,以确定闭合是否一直被评估为弱头部标准形式

有几种方法可以重现ghci实现对
:sprint
执行的检查。GHC api在
RtClosureInspect
中公开。该包仅依赖于ghc prim,从
RTCLOSUREENSPECT
复制代码,并公开
getClosure::a->IO Closure
。如何检查这两种
闭包
表示(例如,跟随一个间接方向)都不是很明显。该包检查闭包并同时暴露a和a。ghc堆视图取决于ghc api

我们可以从ghc堆视图编写
评估的

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
通过有选择地强制求值,我们可以在函数外横向传递信息,而无需诉诸任何不安全的方法。下面构建了一个成对的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
通过检查一对thunk中的每个thunk是否已被
评估来读取我们的位流

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
如果我们运行程序并提供输入
abc123
,我们将观察与检查每个字符
isAlpha

abc123
abc123
1
1
1
0
0
0

最近有一个建议,也许它已经在某处实现了。

有趣的是,这已经是谷歌关于“haskell check if whnf”的第二个结果,至少对我来说:)。令人充满希望的是,建议每个堆对象信息表包括闭包类型,看起来它应该给你想要的东西。不太乐观的是,我觉得GHC的拆箱转换将使整个想法变得相当棘手。@dfeuer谢谢。GHC的评论对编写一份报告非常有帮助。我认为
NOINLINE
通常需要
unsafePerformIO
(以及石棉内衣)来防止它被优化。AFAICS,这里
False
表示该值不在whnf中,而
True
表示它要么在whnf中,要么正在评估中(尚未生成whnf)。可能使用
val`seq`unsafePerformIO(writeIORef-True)`seq`val
会导致相反的保证(
True
保证whnf,和
False
非whnf/正在进行的评估)。对于三态状态,可以更精确地表示这一点:未评估/进行中/whnf。谢谢。这有助于撰写一份报告。
RtClosureInspect
中使用的闭包检查代码是。我认为您可以通过添加至少最相关闭包类型的简要说明来改进此答案。
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
main = do
    let bs = mkBitStream 0
    forkIO (readBs bs)
    text <- getLine
    putStrLn (copy isAlpha bs text)
    getLine
abc123
abc123
1
1
1
0
0
0