haskell lazy-evaluation thunk weak-head-normal-form

haskell - Probar si un valor ha sido evaluado en forma normal de cabeza débil



lazy-evaluation thunk (4)

La implementación de ghci para :sprint finalmente usa unpackClosure# de ghc-prim para inspeccionar un cierre. Esto se puede combinar con el conocimiento del formato de los objetos del montón para determinar si un cierre se ha evaluado hasta el final a la forma normal de cabeza débil.

Hay algunas formas de reproducir la inspección realizada por la implementación de ghci para :sprint . La api de GHC expone getClosureData :: DynFlags -> a -> IO Closure en RtClosureInspect . El paquete de vacuum , que solo depende de ghc-prim, reproduce el código de RtClosureInspect y expone getClosure :: a -> IO Closure . No es inmediatamente obvio cómo examinar cualquiera de estas representaciones de Closure para, por ejemplo, seguir una indirección. El ghc-heap-view inspecciona los cierres y expone tanto el getClosureData :: a -> IO Closure como una vista detallada del Closure . ghc-heap-view depende de la API de GHC.

Podemos escribir evaluated en términos de getBoxedClosureData de 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

Este manejo de los cierres de agujero negro puede ser incorrecto mientras se está evaluando el agujero negro. El manejo de los cierres de selector puede ser incorrecto. La suposición de que los cierres AP no están en forma normal de cabeza débil puede ser incorrecta. La suposición de que todos los demás cierres están en WHNF es casi ciertamente incorrecta.

Ejemplo

Nuestro ejemplo requerirá dos subprocesos simultáneos para observar en un subproceso que el otro está evaluando expresiones.

import Data.Char import Control.Concurrent

Podemos comunicar información fuera de una función sin recurrir a nada unsafe forzando selectivamente la evaluación. Lo siguiente construye un flujo de pares de thunks en los que podemos elegir forzar uno u otro del par.

mkBitStream :: Integer -> [(Integer, Integer)] mkBitStream a = (a+2, a+3) : mkBitStream (a+1)

zero obliga al primero y one obliga al segundo.

zero :: [(x, y)] -> [(x, y)] zero ((x, _):t) = x `seq` t one :: [(x, y)] -> [(x, y)] one ((_, y):t) = y `seq` t

copy es una función de identidad malvada que tiene el efecto secundario de forzar bits en una secuencia basada en la inspección de los datos.

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 lee nuestro flujo de bits al verificar si se ha evaluated cada uno de los thunks en un par.

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

Forzar la copy cuando se imprime tiene el efecto secundario de imprimir la información observada sobre la cadena de lectura.

main = do let bs = mkBitStream 0 forkIO (readBs bs) text <- getLine putStrLn (copy isAlpha bs text) getLine

Si ejecutamos el programa y proporcionamos la entrada abc123 , observamos el efecto secundario correspondiente a verificar si cada uno de los caracteres es isAlpha

abc123 abc123 1 1 1 0 0 0

En Haskell, ¿es posible probar si un valor se ha evaluado como una forma normal de cabeza débil? Si una función ya existe, esperaría que tuviera una firma como

evaluated :: a -> IO Bool

Hay algunos lugares donde vive una funcionalidad similar.

Una respuesta anterior me presentó al comando :sprint ghci, que imprimirá solo la parte de un valor que ya ha sido forzado a tener una forma normal de cabeza débil. :sprint puede observar si un valor ha sido evaluado o no:

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

Es posible en IO examinar propiedades que de otro modo estarían fuera de los límites. Por ejemplo, es posible comparar en IO para ver si dos valores provienen de la misma declaración. Esto es proporcionado por los StableName s en System.Mem.StableName y se usa de manera famosa para resolver el problema de intercambio observable en data-reify . El StablePtr relacionado no proporciona un mecanismo para verificar si el valor de referencia tiene una forma normal de cabeza débil.


No estoy seguro de que haya algo preempaquetado para esto. Sin embargo, uno puede codificarlo:

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 )

Aquí hay un ejemplo de uso en ghci:

*NFTrack> (value, isEvaluated) <- track (undefined:undefined) *NFTrack> isEvaluated False *NFTrack> case value of _:_ -> "neat!" "neat!" *NFTrack> isEvaluated True

Por supuesto, esto hará un seguimiento de si el valor de escritura envuelto y luego devuelto el valor original se evalúa como WHNF, no si la cosa que se pasa a track se evalúa como WHNF, por lo que querrá poner esto como cerca del thunk en el que está interesado, por ejemplo, no podrá decirle si un thunk hecho por alguien más ya ha sido evaluado por alguien antes de que comience el rastreo. Y, por supuesto, considere usar MVar lugar de IORef si necesita seguridad para subprocesos.



Una respuesta negativa, para que quede constancia: no parece ser factible reutilizar el mecanismo del sprint , porque está estrechamente ligado a la evaluación interactiva interpretada en oposición a las estructuras de tiempo de ejecución primitivas, por lo que puedo decir; Nunca he mirado los internos de GHC antes.

Comencé buscando "sprint" en la fuente de GHC en GitHub , que resultó compartir una implementación con el comando "imprimir" pero para un indicador Bool llamado force , y seguí las definiciones hasta que llegué a RtClosureInspect.cvObtainTerm que parece ser Un evaluador especializado.