С небольшим количеством unsafe
вы можете увидеть, сколько много ленивого значения было вычислено в Haskell.
import Data.IORef
import System.IO.Unsafe
data Nat = Z | S Nat
deriving (Eq, Show, Read, Ord)
natTester :: IORef Nat -> Nat
natTester ref =
let inf = natTester ref
in unsafePerformIO $ do
modifyIORef ref S
pure $ S inf
newNatTester :: IO (Nat, IORef Nat)
newNatTester = do
ref <- newIORef Z
pure (natTester ref, ref)
howMuchWasEvaled :: (Nat -> b) -> IO Nat
howMuchWasEvaled f = do
(inf, infRef) <- newNatTester
f inf `seq` readIORef infRef
С участием:
ghci> howMuchWasEvaled $ \x -> x > S (S Z)
S (S (S Z))
Указывает, что были оценены только первые четыре конструктора infinity :: Nat
.
Если x
используется дважды, мы все равно получим требуемую общую оценку:
> howMuchWasEvaled $ \x -> x > Z && x > S (S Z)
S (S (S Z))
В этом есть смысл — как только мы оценим x
до определенного момента, нам не нужно начинать все сначала. Преобразователь уже был принудительно.
Но есть ли способ проверить, сколько раз оценивались конструкторы? То есть функция magic
, которая ведет себя так:
> magic $ \x -> x > Z
S Z
> magic $ \x -> x > Z && x > Z
S (S Z)
...
Я понимаю, что это может включать флаги компилятора (возможно, no-cse
), встроенные прагмы, очень небезопасные функции и т. д.
РЕДАКТИРОВАТЬ: Карл указал, что я, возможно, недостаточно четко изложил ограничения того, что искал. Требование состоит в том, что функция, которую magic
задает в качестве аргумента, не может быть изменена (хотя можно предположить, что ее аргумент ленив). magic
будет частью библиотеки, которую вы можете вызывать со своими собственными функциями.
Тем не менее, хаки, специфичные для GHC, и вещи, которые работают только ненадежно, определенно все еще в игре.