ghci для :sprint
в конечном итоге использует unpackClosure#
из ghc-prim для проверки закрытия. Это можно объединить со знанием формата объектов кучи, чтобы определить, закрытие оценивалось полностью до нормальной формы слабой головы.
Есть несколько способов воспроизвести проверку, выполненную реализацией ghci для :sprint
. API GHC предоставляет доступ к getClosureData :: DynFlags -> a -> IO Closure
в RtClosureInspect
. Пакет вакуум, который зависит только от ghc-prim, воспроизводит код из RtClosureInspect
и предоставляет getClosure :: a -> IO Closure
. Не сразу очевидно, как исследовать любое из этих Closure
представлений, чтобы, например, проследить косвенное обращение. Пакет ghc-heap-view проверяет закрытие и предоставляет как getClosureData :: a -> IO Closure
и подробный вид Closure
. ghc-heap-view зависит от API GHC.
Мы можем написать evaluated
в терминах _12 _ из ghc-heap-view.
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
, путем выборочного принудительного вычисления. Следующее создает поток пар преобразователей, в котором мы можем выбрать принудительное включение одного или другого из пары.
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
читает наш битовый поток, проверяя, был ли каждый из преобразователей в паре evaluated
.
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
abc123
abc123
1
1
1
0
0
0
person
Cirdec
schedule
24.02.2015