Подтвердить что ты не робот

Проверьте, было ли значение оценено на слабую головную нормальную форму

В Haskell, можно ли проверить, оценивается ли значение для нормальной нормальной формы головы? Если функция уже существует, я ожидаю, что она будет иметь подпись типа

evaluated :: a -> IO Bool

Есть несколько мест, в которых работает аналогичная функциональность.

A предыдущий ответ познакомил меня с командой :sprint ghci, который будет печатать только часть значения, которое уже было вынуждено для нормальной нормальной формы головы. :sprint может наблюдать, было ли оценено значение:

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

В IO возможно изучить свойства, которые в противном случае были бы запрещены. Например, можно сравнить в IO, чтобы увидеть, были ли два значения взяты из одной и той же декларации. Это обеспечивается StableName в System.Mem.StableName и используется для решения проблемы наблюдаемого обмена в data-reify. Связанный StablePtr не предоставляет механизм проверки, находится ли ссылочное значение в слабой нормальной форме головы.

4b9b3361

Ответ 1

Я не уверен, что для этого есть что-то готовое. Однако его можно закодировать:

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-and-then-return-the-original-value thunk WHNF, а не передается ли значение, переданное в track, в WHNF, так что вы будете хотите поставить это как можно ближе к интересующему вас блоку - например он не сможет сказать вам, был ли кто-то другой, который был сделан кем-то другим, уже был оценен кем-то еще до начала отслеживания. И, конечно, используйте MVar вместо IORef, если вам нужна безопасность потоков.

Ответ 2

реализация ghci для :sprint в конечном итоге использует unpackClosure# из ghc-prim для проверки закрытие. Это можно объединить со знанием формата объектов кучи, чтобы определить, была ли оценка закрыта полностью до нормальной нормальной формы головы.

Существует несколько способов воспроизвести проверку, выполненную реализацией ghci, для :sprint. GHC api предоставляет getClosureData :: DynFlags -> a -> IO Closure в RtClosureInspect. Пакет vacuum, который зависит только от ghc-prim, воспроизводит код из RtClosureInspect и предоставляет getClosure :: a -> IO Closure. Не сразу очевидно, как рассматривать любое из этих представлений Closure, например, для обозначения косвенности. Пакет ghc-heap-view проверяет закрытие и предоставляет как getClosureData :: a -> IO Closure, так и подробный вид Closure. ghc-heap-view зависит от Apache GHC.

Мы можем написать evaluated в терминах getBoxedClosureData из 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, выборочно заставляя оценивать. Следующее строит поток пар thunks, в котором мы можем выбрать принуждение одной или другой пары.

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 читает наш бит-поток, проверяя, был ли каждый из thunks в паре evaluated.

readBs :: [(x, y)] -> IO ()
readBs [email protected]((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

Ответ 3

Отрицательный ответ для записи: не представляется возможным повторно использовать механизм sprint, потому что он сильно привязан к интерпретируемой интерактивной оценке, а не к примитивным структурам времени исполнения - насколько я могу судить; Я никогда раньше не смотрел на внутренности GHC.

Я начал с поиска "sprint" в источнике GHC на GitHub, который, как оказалось, использует реализацию с командой "print", но для Bool с именем force и последующими определениями, пока я не доберусь до RtClosureInspect.cvObtainTerm, который, как представляется, является специализированным оценщиком.