Haskell 测试某个值是否已评估为弱水头标准形式
在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中,可以检查那些不
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