При внедрении ленивого функционального языка необходимо хранить значения как неоцененные thunks, которые будут оцениваться только при необходимости.
Одна из задач эффективной реализации, рассмотренная, например, The Spineless Tagless G-machine состоит в том, что эта оценка должна выполняться только один раз для каждого thunk, а последующие обращения должны повторно использовать вычисленное значение - отказ сделать это приведет к по меньшей мере квадратичному замедлению (возможно, экспоненциальному? Я не уверен от верхней части головы.)
Я ищу простой пример реализации, чья работа легко понятна (в отличие от реализации промышленной силы, такой как GHC, которая предназначена для производительности по простоте). Я встретил minihaskell в http://www.andrej.com/plzoo/, который содержит следующий код.
Поскольку он называется "эффективным интерпретатором", я бы предположил, что он действительно проводит каждую оценку только один раз и сохраняет рассчитанное значение для повторного использования, но мне трудно видеть, где и как; Я могу видеть только один оператор присваивания в самом интерпретаторе, и это не похоже на перезапись части записи thunk.
Итак, мой вопрос: действительно ли этот интерпретатор делает такое кэширование, и если да, то где и как? (А если нет, то какая простейшая существующая реализация делает это?)
Код http://www.andrej.com/plzoo/html/minihaskell.html
let rec interp env = function
| Var x ->
(try
let r = List.assoc x env in
match !r with
VClosure (env', e) -> let v = interp env' e in r := v ; v
| v -> v
with
Not_found -> runtime_error ("Unknown variable " ^ x))
... snipping the easy stuff ...
| Fun _ as e -> VClosure (env, e)
| Apply (e1, e2) ->
(match interp env e1 with
VClosure (env', Fun (x, _, e)) ->
interp ((x, ref (VClosure (env, e2)))::env') e
| _ -> runtime_error "Function expected in application")
| Pair _ as e -> VClosure (env, e)
| Fst e ->
(match interp env e with
VClosure (env', Pair (e1, e2)) -> interp env' e1
| _ -> runtime_error "Pair expected in fst")
| Snd e ->
(match interp env e with
VClosure (env', Pair (e1, e2)) -> interp env' e2
| _ -> runtime_error "Pair expected in snd")
| Rec (x, _, e) ->
let rec env' = (x,ref (VClosure (env',e))) :: env in
interp env' e
| Nil ty -> VNil ty
| Cons _ as e -> VClosure (env, e)
| Match (e1, _, e2, x, y, e3) ->
(match interp env e1 with
VNil _ -> interp env e2
| VClosure (env', Cons (d1, d2)) ->
interp ((x,ref (VClosure(env',d1)))::(y,ref (VClosure(env',d2)))::env) e3
| _ -> runtime_error "List expected in match")