С небольшим количеством 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
), встроенные прагмы, очень небезопасные функции и т. Д. c.
РЕДАКТИРОВАТЬ : Карл указал, что я, возможно, недостаточно ясно понял ограничения того, что я искал. Требование состоит в том, чтобы функция , которая magic
задана как аргумент, не могла быть изменена (хотя можно предположить, что ее аргумент является ленивым). magic
будет частью библиотеки, которую вы можете вызывать с помощью своих собственных функций.
Тем не менее, GH C -specifi c хаки и вещи, которые работают только ненадежно, определенно остаются в игре.