Сошников Дмитрий Валерьевич к.ф.-м.н., доцент dmitryso@microsoft.com Факультет инноваций и высоких технологий Московский физико-технический институт Реализация функциональных языков: интерпретаторы и абстрактные машины 2 eval Let ("id",Lam ("x",Var "x"), Let ("sq",Lam ("z",App (App (PFunc "*",Var "z"),Var "z")), App (Var "sq",App (Var "id",Int 5)))) in [] eval Lam ("x",Var "x") in [] eval Let ("sq",Lam ("z",App (App (PFunc "*",Var "z"),Var "z")),App (Var "sq",App (Var "id",Int 5))) in [[id,Closure(Lam(“x”,Var(“x”)))]] eval Lam ("z",App (App (PFunc "*",Var "z"),Var "z")) in [[id,Closure(Lam(“x”,Var(“x”)))]] eval App (Var "sq",App (Var "id",Int 5)) in [[id,…]; [sq, …]] – вычисление внутреннего выражения sq(id 5) eval Var "sq" in [[id,...]; [sq, ...]] eval App (Var "id",Int 5) in [[id, …]; [sq, …]] - применение id 5 eval Var "id" in [[id,…]; [sq,…]] eval Int 5 in [[id, …]; [sq, …]] app (Closure (Lam ("x",Var "x"), [])) (Int 5) - применение (x.x)5 eval Var "x" in [[x,Int(5)]] app (Closure(Lam ("z",App (App (PFunc "*",Var "z"),Var "z")), [[id,…]])) (Int 5) – применение (z.z*z)5 eval App (App (PFunc "*",Var "z"),Var "z") in [[id, …]; [z, Int(5)]] – подстановка z=5 в окружение eval App (PFunc "*",Var "z") in [[id, …]; [z, Int(5)]] eval PFunc "*" in [[id, …]; [z, Int(5)]] eval Var "z" in [[id,…]; [z, Int(5)]] app (Op ("*",2,[])) (Int 5) - применение (*)5 eval Var "z" in [[id, …]; [z, Int(5)]] app (Op ("*",1,[Int 5])) (Int 5) – применение ((*)5)5 val it : expr = Int 25 3 ©2008 Сошников Д.В. let id = x.x in let sq = z.z*z in sq(id 5) letrec fact = fun x -> if x<=1 then 1 else x*fact(x-1) in fact 4;; 4 ©2008 Сошников Д.В. При вычислении fact 4 необходимо вычислять (eval) fact в контексте, содержащем само определение fact При этом будет происходить вычисление fact в последовательности контекстов с x=4, 3, 2, 1. Для реализации вводим тип выражения LetRec и Rclosure RClosure – вычисление замыкания, в контекст которого надо добавить определение самой функции ©2008 Сошников Д.В. let rec eval exp env = match exp with ... | Let(id,e1,e2) -> let r = eval e1 env in eval e2 (Map.add id r env) | LetRec(id,e1,e2) -> eval e2 (Map.add id (RClosure(e1,env,id)) env) ... and apply e1 e2 = match e1 with Closure(Lam(v,e),env) -> eval e (Map.add v e2 env) | RClosure(Lam(v,e),env,id) -> eval e (Map.add v e2 (Map.add id e1 env)) ... 5 ©2008 Сошников Д.В. При ленивых вычислениях f x мы сначала начинаем вычислять f, а вычисление x откладываем (задерживаем) Для «откладывания» вводим конструкцию Susp(expr,env) Похожа на Closure, теоретически можно моделировать отложенность с помощью замыканий, т.е. выражений вида (fun () -> expr) При аппликации задерживаем вычисление второго аргумента 6 ©2008 Сошников Д.В. let rec eval exp env = match exp with ... | App(e1,e2) -> apply (eval e1 env) (Susp(e2,env)) | Let(id,e1,e2) -> let r = Susp(e1,env) in eval e2 (Map.add id r env) | Susp(e,envr) -> eval e envr ... let rec funof = function "+" -> (function [a;b] -> let Int(x),Int(y)= eval a Map.empty,eval b Map.empty in Int(x+y)) ... 7 8 ©2008 Сошников Д.В. Прямая интерпретация дерева программы не всегда эффективна Рекурсивный алгоритм, в явном виде не контролируем расходы памяти Хочется получить более простой алгоритм в терминах низкоуровневых команд элементарного исполнителя Алгоритм, пригодный для реализации на более низкоуровневом языке Выход: использование абстрактных (стековых) машин 9 ©2008 Сошников Д.В. SECD-машина – это абстрактная машина, состояние которой состоит из 4-х стеков: S – Stack – стек объектов для вычисления выражений E – Environment – среда для означивания переменных, контекст C – Control – управляющая строка, оставшаяся часть вычисляемого выражения D – Dump – стек возвратов для обработки вложенных конткестов (вызовы функций) 10 ©2008 Сошников Д.В. 1+2 S E C D [] [] [App(App(+,1),2)] [] [] [] [2,App(+,1),@] [] [2] [] [App(+,1),@] [] [2] [] [1,+,@,@] [] [1,2] [] [+,@,@] [] [+,1,2] [] [@,@] [] [(+1),2] [] [@] [] [3] [] [] [] 11 ©2008 Сошников Д.В. (λf. λx.(f f x)) succ 0 S E C D [] [] [A(A(L(f,L(x,A(f,A(f,x))),succ),0)] [] [] [] [0, A(L(f,L(x,A(f,A(f,x))),succ),@] [] [0] [] [A(L(f,L(x,A(f,A(f,x))),succ),@] [] [0] [] [succ, L(f,L(x,A(f,A(f,x)))),@,@] [] [succ,0] [] [L(f,L(x,A(f,A(f,x)))),@,@] [] [C(f,L(x,A(f,A(f,x))),[]), succ,0] [] [@,@] [] [] [(f,succ)] [L(x,A(f,A(f,x)))] [([0],[],[@])] [C(x, A(f,A(f,x)), (f,succ))] [(f,succ)] [] [([0],[],[@])] Вызов функции 12 E C ©2008 Сошников Д.В. S D [C(x, A(f,A(f,x)), (f,succ))] [(f,succ)] [] [([0],[],[@])] [C(x, A(f,A(f,x)), (f,succ)),0] [] [@] [] [] [(f,succ), (x,0)] [A(f,A(f,x))] [([],[],[])] [] [(f,succ), (x,0)] [A(f,x),f,@] [([],[],[])] [] [(f,succ), (x,0)] [x,f,f,@,@] [([],[],[])] [0] [(f,succ), (x,0)] [f,f,@,@] [([],[],[])] [0,succ,succ] [(f,succ), (x,0)] [@,@] [([],[],[])] [1,succ] [(f,succ), (x,0)] [@] [([],[],[])] [2] [(f,succ), (x,0)] [] [([],[],[])] [2] [] [] [] 13 ©2008 Сошников Д.В. type id = string;; type expr = Var of id | Lam of id*expr | App of expr*expr | PFunc of (expr->expr) | AT | Int of int | Closure of id*expr*env and env = Map<id,expr>;; ;; type type type type stack = expr list;; control = expr list;; dump = (stack*env*control) list;; state = stack*env*control*dump;; 14 ©2008 Сошников Д.В. let rec eval SECD = match SECD with (Res::S, E, C, D) when C=[] && D=[] -> Res | (x::S, E, C, (S1,E1,C1)::D) when C=[] -> eval (x::S1,E1,C1,D) | (S,E,Var(x)::C,D) -> eval ((Map.find x E)::S,E,C,D) | (S,E,Lam(x,b)::C,D) -> eval (Closure(x,b,E)::S,E,C,D) | (S,E,Int(x)::C,D) -> eval(Int(x)::S,E,C,D) | (S,E,PFunc(x)::C,D) -> eval(PFunc(x)::S,E,C,D) | (Closure(x,b,E1)::(arg::S),E,AT::C,D) -> eval ([],Map.add x arg E1,[b],(S,E,C)::D) | (PFunc(f)::(arg::S),E,AT::C,D) -> eval (f(arg)::S,E,C,D) | (S,E,App(f,a)::C,D) -> eval (S,E,a::(f::(AT::C)),D) ;; Обратите внимание на хвостовую рекурсию! 15 ©2008 Сошников Д.В. Условный оператор Реализация рекурсии При помощи комбинатора неподвижной точки Y ▪ Вариант для энергичной стратегии вычислений Y’=λh.(λx.h(λy.xxy))(λx.h(λy.xxy))) При помощи помеченных выражений Ленивая SECD-машина Доказательство эквивалентности конверсии и вычислений SECD-машины (см. Филд, Харрисон) 16