我们在上一篇文章中,描述了一个通用的优化技术:

找到时间差,把数据分成已知跟未知,并且用代码表示后者。

但是,这种做法有两个问题:

0 - 你需要费时费力去整这个东东,而且不是所有时候都能刚刚好复用代码。

1 - 如果我们预先不知道应该怎么区分呢?甚至,如果这是动态的呢?假设我们先接受一半的数据,等一会,然后接受另一半,那我们如何用上述的技巧?难不成把所有可能的分法都预先写一遍?

所以,我们希望把上述技术,staging,自动化掉,使得程序员不再需要去关心之。

我们应该怎么做呢?

我们先从最基本的开始,先决定要操作的语言吧:

type expr =
| Var of string
| Int of int
| Add of (expr * expr)
| Mult of (expr * expr)

这个语言,简单到了极点,只剩下+跟*两个操作了。别担心,我们会一步步扩展这个语言。

module ENV = Map.Make(String)

let rec eval (e : int ENV.t) : expr -> int =
  function
  | Var v -> ENV.find v e
  | Int i -> i
  | Add (x, y) -> eval e x + eval e y
  | Mult (x, y) -> eval e x * eval e y

这是一个标准的definitional interpreter实现,中规中矩,并没任何出彩的地方。

既然我们的数据可能是已知(值),或者可能是未知(程序),我们可以用一个sum type表示之,然后根据运算两边是Static还是Dynamic,调用各自的方法。

type pValue =
| Static of int
| Dynamic of expr

let rec pEval (e : pValue ENV) : expr -> pValue =
  function
  | Var v -> ENV.find v e
  | Int i -> Static i
  | Add (x, y) ->
      (match (pEval e x, pEval e y) with
      | Static xi, Static yi -> Static (xi + yi)
      | Dynamic xe, Dynamic ye -> Dynamic (Add (xe, ye)))
  | Mult (x, y) ->
      (match (pEval e x, pEval e y) with
      | Static xi, Static yi -> Static (xi * yi)
      | Dynamic xe, Dynamic ye -> Dynamic (Mult (xe, ye)))

眼尖的小伙伴跟OCaml都发现了一个问题:我们没有处理Static跟Dynamic混合的case。这个时候,我们需要把Static转化成Dynamic(因为反过来不可能),然后输出Dynamic。

let asDyn : pValue -> expr =
  function
  | Static i -> Int i
  | Dynamic e -> e

let rec pEval (e : pValue env) : expr -> pValue =
  function
...
  | Add (x, y) ->
      (match (pEval e x, pEval e y) with
      | Static xi, Static yi -> Static (xi + yi)
      | xp, yp -> Dynamic (Add (asDyn xp, asDyn yp)))
...

值得注意的是,我们现在的pEval,刚刚好对应着常规编译器里面的constant folding pass - 我们会预先把常量组成的计算先算一遍,然后再生成剩下的代码。我们能不能让我们的pEval做更多的优化?

let rec pEval (e : pValue env) : expr -> pValue =
  function
  | Var v -> ENV.find v e
  | Int i -> Static i
  | Add (x, y) ->
      (match (pEval e x, pEval e y) with
      | Static xi, Static yi -> Static (xi + yi)
      | Static 0, y -> y
      | x, Static 0 -> x
      | xp, yp -> Dynamic (Add (asDyn xp, asDyn yp)))
  | Mult (x, y) ->
      (match (pEval e x, pEval e y) with
      | Static xi, Static yi -> Static (xi * yi)
      | Static 0, _ -> Static 0
      | _, Static 0 -> Static 0
      | Static 1, y -> y
      | x, Static 1 -> x
      | xp, yp -> Dynamic (Mult (asDyn xp, asDyn yp)))

我们可以利用+跟*的各种特性,使得当我们有一些是Static,有一些是Dynamic的时候,返回一些更优结果。这种优化对应着strength reduction。

这个语言能做的其实不多,我们可以继续利用+*的性质做更多优化(比如分配律),但是这跟上面的优化并没有什么不同。

是时候给语言增加更多的功能了!我们先从最简单的Let expression做起:

type expr =
...
| Let of (string * expr * expr)

let rec eval (e : int ENV) : expr -> int =
  function
...
  | Let (var, v, body) ->
      eval (ENV.add var (eval e v) e) body

我们试试看对pEval如法炮制吧。

let rec pEval (e : pValue ENV.t) : expr -> pValue =
  function
...
  | Let (var, v, body) ->
      pEval (ENV.add var (pEval e v) e) body

...
let example_0 =
let program = (Let ("x2", Add (Var "x1", Var "x1"), Add (Var "x2", Var "x2"))) in
pEval (ENV.singleton "x1" (Dynamic (Var "x1"))) program

我们重新引入了表达式爆炸问题:

utop # example_0;;
- : pValue = Dynamic (Add (Add (Var "x1", Var "x1"), Add (Var "x1", Var "x1")))

我们需要再次引入letlist,然后let的时候,把Dynamic部分放上letlist。

let genCounter () =
  let cnt = ref 0 in
  let gen () =
    let ret = !cnt in
    cnt := ret + 1 ;
    ret
  in
  gen

let freshVar = genCounter ()

let freshName () = "x" ^ string_of_int (freshVar ())

type letlist = (string * expr) list ref

let new_letlist () : letlist = ref []

let push_letlist (ll : letlist) (x : expr) : expr =
  let name = freshName () in
  ll := (name, x) :: !ll;
  Var name

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
  function
...
  | Let (var, v, body) ->
      (match recurse v with
      | Static vi -> pEval (ENV.add var (Static vi) e) ll body
      | Dynamic vd -> pEval (ENV.add var (Dynamic (push_letlist ll vd)) e) ll body)

let example_1 =
let program = (Let ("x2", Add (Var "x1", Var "x1"), Add (Var "x2", Var "x2"))) in
with_letlist (fun ll -> asDyn (pEval (ENV.singleton "x1" (Dynamic (Var "x1"))) ll program))

问题解决!

utop # example_1;;
- : expr = Let ("x0", Add (Var "x1", Var "x1"), Add (Var "x0", Var "x0"))

我们接下来,加入一些控制流:

type expr =
...
| IfZero of (expr * expr * expr)

let rec eval (e : int ENV.t) : expr -> int =
  function
...
  | IfZero (c, x, y) ->
      if (eval e c) = 0 then eval e x else eval e y

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
...
  | IfZero (c, l, r) ->
      (match recurse c with
      | Static (SInt cv) -> if cv = 0 then recurse l else recurse r
      | Dynamic cd -> Dynamic (IfZero (cd, asDyn (recurse l), asDyn (recurse r)))
      | _ -> assert false)

...
let example_2 =
let program = IfZero (Var "x0", Let ("x1", Var "x0", Var "x1"), Int 1) in
with_letlist (fun ll -> asDyn (pEval (ENV.singleton "x0" (Dynamic (Var "x0"))) ll program))

可以看到,我们把If里的东东提出来了,坏坏!

utop # example_2;;
- : expr = Let ("x1", Var "x0", IfZero (Var "x0", Var "x1", Int 1))

当我们有新的scope的时候,我们不能复用letlist,而是要自己再造一个,否则运算会跑出scope。

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
  function
...
  | IfZero (c, l, r) ->
      (match recurse c with
      | Static cv -> if cv = 0 then recurse l else recurse r
      | Dynamic cd ->
          Dynamic (IfZero (cd,
                           with_letlist (fun ll -> asDyn (pEval e ll l)),
                           with_letlist (fun ll -> asDyn (pEval e ll r)))))

现在,就把问题修复了:

utop # example_2;;
- : expr = IfZero (Var "x0", Let ("x1", Var "x0", Var "x1"), Int 1)

搞完控制流后,是时候加入product type跟sum type了。

type expr =
...
| Pair of (expr * expr)
| Zro of expr
| Fst of expr
| Left of expr
| Right of expr
| Match of (expr * (string * expr) * (string * expr))

现在,eval不能再返回int了,必须返回自己的类型

type value =
| VInt of int
| VPair of (value * value)
| VLeft of value
| VRight of value

let rec eval (e : value ENV.t) : expr -> value =
  function
  | Var v -> ENV.find v e
  | Int i -> VInt i
  | Add (x, y) -> 
      (match (eval e x, eval e y) with
             (VInt x, VInt y) -> VInt (x + y) | _ -> assert false)
  | Mult (x, y) -> 
      (match (eval e x, eval e y) with 
             (VInt x, VInt y) -> VInt (x * y) | _ -> assert false)
  | Let (var, v, body) ->
      eval (ENV.add var (eval e v) e) body
  | IfZero (c, x, y) ->
      (match eval e c with 
             VInt ci -> if ci = 0 then eval e x else eval e y | _ -> assert false)
  | Pair (x, y) -> VPair (eval e x, eval e y)
  | Zro x -> (match eval e x with VPair (l, r) -> l | _ -> assert false)
  | Fst x -> (match eval e x with VPair (l, r) -> r | _ -> assert false)
  | Left x -> VLeft (eval e x)
  | Right x -> VRight (eval e x)
  | Match (x, (lv, lb), (rv, rb)) ->
      (match eval e x with
      | VLeft l -> eval (ENV.add lv l e) lb
      | VRight r -> eval (ENV.add lv r e) rb
      | _ -> assert false)

那pEval的类型该怎么设计?我们试试看这样:

type sValue =
| SInt of int
| SPair of (sValue * sValue)
| SLeft of sValue
| SRight of sValue

type pValue =
| Static of sValue
| Dynamic of expr

不太对的样子 - 我们的一个Pair (Int 0, Var x)只能被当成Dynamic value处理,尽管我们知道左边是0.

type sValue =
| SInt of int
| SPair of (pValue * pValue)
| SLeft of pValue
| SRight of pValue
and
pValue =
| Static of sValue
| Dynamic of expr

这样就对多了 - 我们需要使partialness递归。

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
  function
  | Var v -> ENV.find v e
  | Int i -> pInt i
  | Add (x, y) ->
      (match (recurse x, recurse y) with
      | Static (SInt xi), Static (SInt yi) -> pInt (xi + yi)
      | Static (SInt 0), y -> y
      | x, Static (SInt 0) -> x
      | xp, yp -> Dynamic (Add (asDyn xp, asDyn yp)))
  | Mult (x, y) ->
      (match (recurse x, recurse y) with
      | Static (SInt xi), Static (SInt yi) -> pInt (xi * yi)
      | Static (SInt 0), _ -> pInt 0
      | _, Static (SInt 0) -> pInt 0
      | Static (SInt 1), y -> y
      | x, Static (SInt 1) -> x
      | xp, yp -> Dynamic (Mult (asDyn xp, asDyn yp)))
  | Let (var, v, body) ->
      (match recurse v with
      | Static vi -> pEval (ENV.add var (Static vi) e) ll body
      | Dynamic vd -> pEval (ENV.add var (Dynamic (push_letlist ll vd)) e) ll body)
  | IfZero (c, l, r) ->
      (match recurse c with
      | Static (SInt cv) -> if cv = 0 then recurse l else recurse r
      | Dynamic cd -> Dynamic (IfZero (cd,
                                       with_letlist (fun ll -> asDyn (pEval e ll l)),
                                       with_letlist (fun ll -> asDyn (pEval e ll r))))
      | _ -> assert false)
  | Pair (x, y) -> pPair (recurse x) (recurse y)
  | Zro x ->
      (match recurse x with
      | Static (SPair (l, r)) -> l
      | Dynamic p -> Dynamic (Zro p)
      | _ -> assert false)
  | Fst x ->
      (match recurse x with
      | Static (SPair (l, r)) -> r
      | Dynamic p -> Dynamic (Fst p)
      | _ -> assert false)
  | Left x -> pLeft (recurse x)
  | Right x -> pRight (recurse x)
  | Match (x, (lv, lb), (rv, rb)) ->
      (match recurse x with
      | Static (SLeft l) -> pEval (ENV.add lv l e) ll lb
      | Static (SRight r) -> pEval (ENV.add lv r e) ll rb
      | Dynamic s ->
          Dynamic 
            (Match (s,
              (lv, with_letlist (fun ll -> 
                     asDyn (pEval (ENV.add lv (Dynamic (Var lv)) e) ll lb))),
              (rv, with_letlist (fun ll ->
                     asDyn (pEval (ENV.add rv (Dynamic (Var rv)) e) ll rb)))))
      | _ -> assert false)

let example_3 =
let program = Match (Left (Add (Var "x", Var "x")), ("a", Add (Var "a", Var "a")), ("y", Int 0)) in
with_letlist (fun ll -> asDyn (pEval (ENV.singleton "x" (Dynamic (Var "x"))) ll program))

几乎所有修改都是mechanical的,除了Match。Match由于创建了自己的scope,所以需要操控letlist。

要注意 - 那怕我们操控了letlist,其实我们又陷入duplication problem了。这是因为我们的Match也会绑定数据到变量上,而我们并没有把这放在letlist上面。

utop # example_3;;
- : expr = Add (Add (Var "x", Var "x"), Add (Var "x", Var "x"))

我们在Match以前,先把被match的‘正规化’ - 里面的所有Dynamic都要上letlist。Let也需要这样的修改。

let rec push_pValue (ll : letlist) : pValue -> pValue = function
| Static (SInt i) -> pInt i
| Static (SPair (x, y)) -> pPair (push_pValue ll x) (push_pValue ll y)
| Static (SLeft x) -> pLeft (push_pValue ll x)
| Static (SRight x) -> pRight (push_pValue ll x)
| Dynamic d -> Dynamic (push_letlist ll d)

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
  function
...
  | Let (var, v, body) ->
      (match push_pValue ll (recurse v) with
      | Static vi -> pEval (ENV.add var (Static vi) e) ll body
      | Dynamic vd -> pEval (ENV.add var (Dynamic (push_letlist ll vd)) e) ll body)
...
  | Match (x, (lv, lb), (rv, rb)) ->
      (match push_pValue ll (recurse x) with
...

现在对了。

utop # example_3;;
- : expr = Let ("x2", Add (Var "x", Var "x"), Add (Var "x2", Var "x2"))

那么,古尔丹,代价是什么呢?

代价是,我们必须十分小心地控制push_pValue,并且如果我们对一个列表进行length计算,我们每次递归的Match都需要重新traverse一次列表,把(编译时)复杂度从O(n)变成O(n^2)!

菩提本无树,明镜亦非台,本来无一物,何处惹尘埃。我们有这个问题,是因为我们要把可能有duplication的任意Dynamic转换成安全的Dynamic。如果我们强制所有Dynamic都只能是Variable,就没有这个问题了。

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
  function
...
  | Add (x, y) ->
      (match (recurse x, recurse y) with
      | Static (SInt xi), Static (SInt yi) -> pInt (xi + yi)
      | Static (SInt 0), y -> y
      | x, Static (SInt 0) -> x
      | xp, yp -> Dynamic (push_letlist ll (Add (asDyn xp, asDyn yp))))
...
  | Match (x, (lv, lb), (rv, rb)) ->
      (match recurse x with
      | Static (SLeft l) -> pEval (ENV.add lv l e) ll lb
      | Static (SRight r) -> pEval (ENV.add lv r e) ll rb
      | Dynamic s ->
          Dynamic (push_letlist ll
            (Match (s,
              (lv, with_letlist (fun ll -> asDyn (pEval (ENV.add lv (Dynamic (Var lv)) e) ll lb))),
              (rv, with_letlist (fun ll -> asDyn (pEval (ENV.add rv (Dynamic (Var rv)) e) ll rb))))))
      | _ -> assert false)

有了前面这么多练习,我们试试看加入Lambda Expression吧:

type expr =
...
| Abs of (string * expr)
| App of (expr * expr)

type value =
...
| VFunc of (value -> value)

let rec eval (e : value ENV.t) : expr -> value =
  function
...
  | Abs (v, b) ->
      VFunc (fun p -> eval (ENV.add v p e) b)
  | App (f, x) ->
      (match eval e f with VFunc f_ -> f_ (eval e x) | _ -> assert false)

跟以前一样,我们的partial evaluator可以尽量接近definitional interpreter:

type sValue =
...
| SFunc of (letlist -> pValue -> pValue)

要注意,我们需要多一个letlist,因为pEval需要letlist才能运行。

let rec asDyn : pValue -> expr =
  function
...
  | Static (SFunc f) ->
      let name = freshName () in
      Abs (name, with_letlist(fun ll -> asDyn (f ll (Dynamic (Var name)))))

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
...
  | Abs (v, b) ->
      pFunc (fun ll p -> pEval (ENV.add v p e) ll b)
  | App (f, x) ->
      (match recurse f with
      | Static (SFunc f_) -> f_ ll (recurse x)
      | Dynamic f_ -> Dynamic (push_letlist ll (App (f_, (asDyn (recurse x)))))
      | _ -> assert false)

这时候,我们有跟以前push_pValue一样的瑕疵:我们可能多次对同一Static Value调用asDyn,进行多次转换。

解决方法也跟以前一样 - 我们强制所有pValue都有一个Dynamic部分,就不需要调用asDyn了。

这就代表,我们的所有操作都会在letlist上记录下来 - 换句话说,我们重新发明了ANF。

type sValue =
| SInt of int
| SPair of (pValue * pValue)
| SLeft of pValue
| SRight of pValue
| SFunc of (letlist -> pValue -> pValue)
and pValue = {static: sValue option; dyn: string}

let push_letlist (ll : letlist) (x : expr) : string =
  let name = freshName () in
  ll := (name, x) :: !ll;
  name

let static ll s d = {static = Some s; dyn = push_letlist ll d}

let dynamic ll d = {static = None; dyn = push_letlist ll d}

let pInt ll i = static ll (SInt i) (Int i)

一不做二不休,我们限制所有的dyn都只能是变量名,这样可以从根源上防止复制。

这些改动很机械化,就只贴一小段:

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> pValue =
  let recurse = fun expr -> pEval e ll expr in
  function
  | Var v -> ENV.find v e
  | Int i -> pInt ll i
  | Add (x, y) ->
      let px = recurse x in
      let py = recurse y in
      (match (px.static, py.static) with
      | Some (SInt xi), Some (SInt yi) -> pInt ll (xi + yi)
      | Some (SInt 0), y -> py
      | x, Some (SInt 0) -> px
      | _ -> dynamic ll (Add (Var px.dyn, Var py.dyn)))

现在,我们离一个‘真正’的编程语言,还差一样东东 - Effect。

type expr =
...
| Count

let rec eval (e : value ENV.t) (c : int ref) : expr -> value =
  let recurse = fun expr -> eval e c expr in
  function
...
  | Count -> let v = !c in c := (v + 1); VInt v

这会是partial evaluator跟definitional interpreter相差最远的地方。

我们会把effect转换成非effect(reify成value),因为effect难以操控。

在这个情况下,我们可以用一个int的state monad。

let rec pEval (e : pValue ENV.t) (ll : letlist) : expr -> int -> (pValue * int) =

好像不太对。

if x then count else 0应该返回什么样的int?我们并不知道。所以,我们要改用int option。

let rec pEval (e : pValue ENV.t) (ll : letlist) :
  expr -> int option -> (pValue * int option) =
  let recurse = fun expr -> pEval e ll expr in
  let return x = fun s -> (x, s) in
  let ( >>= ) x f = fun s -> let (x', s') = x s in f x' s' in
  let ( >> ) a b = a >>= (fun _ -> b) in
  let get = fun s -> (s, s) in
  let put s = fun _ -> ((), s) in
  function
  | Var v -> return (ENV.find v e)
  | Int i -> return (pInt ll i)
  | Add (x, y) ->
      recurse x >>= (fun px ->
      recurse y >>= (fun py ->
      return
        (match (px.static, py.static) with
        | Some (SInt xi), Some (SInt yi) -> pInt ll (xi + yi)
        | Some (SInt 0), y -> py
        | x, Some (SInt 0) -> px
        | _ -> dynamic ll (Add (Var px.dyn, Var py.dyn)))))

我们新增了一些基本的State Monad函数,然后靠着这些我们可以轻松改写跟State无关的部分。

let with_letlist' (f : letlist -> (expr * 'a)) : (expr * 'a) =
  let ll = new_letlist () in
  let (e, a) = f ll in
  (let_letlist ll e, a)
...
  let dynS = fun (a, s) -> (Var a.dyn, s) in
  let merge a b =
    match (a, b) with
    | Some a_, Some b_ -> if a_ == b_ then Some a_ else None
    | _ -> None in
...
  | IfZero (c, l, r) ->
      recurse c >>= (fun pc ->
      (match pc.static with
      | Some (SInt cv) -> if cv = 0 then recurse l else recurse r
      | None ->
          get >>= (fun c ->
          let (le, lc) = with_letlist' (fun ll -> dynS (pEval e ll l c)) in
          let (re, rc) = with_letlist' (fun ll -> dynS (pEval e ll r c)) in
          put (merge lc rc) >>
          return (dynamic ll (IfZero (Var pc.dyn, le, re))))
      | _ -> assert false))

if的时候,如果是动态的,我们要把两边的状态合并 - 如果两边不一样,我们只能返回‘不知道’。

Match也有一样的处理,就不写了。

...
  | App (f, x) ->
      recurse f >>= (fun pf ->
      recurse x >>= (fun px ->
      (match pf.static with
      | Some (SFunc f_) -> f_ ll px
      | None ->
          put None >>
          return (dynamic ll (App (Var pf.dyn, Var px.dyn)))
      | _ -> assert false)))

十分容易忘掉的case - 调用未知函数,也需要清空状态,因为我们不知道这个函数里面会搞什么东东。

... 
  | Count ->
      get >>= (fun s ->
      (match s with
      | Some i -> put (i + 1) >> return (pInt ll i)
      | None -> return (dynamic ll Count)))

Count自身却十分简单。在这,困难都是effect对其他特性的破坏带来的。

Counter基本上是最简单的effect了,我们试试看更难的,比如Reference。

type expr =
...
| Unit
| MkRef of expr
| GetRef of expr
| SetRef of (expr * expr)

type value =
...
| VRef of value ref
| VUnit

let rec eval (e : value ENV.t) (c : int ref) : expr -> value =
  let recurse = fun expr -> eval e c expr in
  function
...
  | Unit -> VUnit
  | MkRef x -> VRef (ref (recurse x))
  | GetRef r -> 
      (match recurse r with VRef r_ -> !r_ | _ -> assert false)
  | SetRef (r, x) ->
      (match recurse r with VRef r_ -> r_ := recurse x; VUnit | _ -> assert false)

let freshCounter = genCounter ()

我们需要把effect reify成值。State Monad依旧适用,只不过int option不够用了,需要一个Store。

同时,我们可以把Store跟Counter放一起,这样更好管理。

module STORE = Map.Make(Int)

type sValue =
...
| SUnit
| SRef of int
and pValue = {static: sValue option; dyn: string}
and world = {counter: int option; store: pValue STORE.t}

let mergeCounter a b =
  match (a, b) with
  | Some a_, Some b_ -> if a_ == b_ then Some a_ else None
  | _ -> None

let mergeStore a b =
  STORE.merge (fun _ av bv ->
    match (av, bv) with
    | (Some apv, Some bpv) -> if apv.dyn == bpv.dyn then av else None
    | _ -> None) a b

let mergeWorld a b = 
  {counter = mergeCounter a.counter b.counter; store = mergeStore a.store b.store}

let emptyWorld = {counter = None; store = STORE.empty}

let rec pEval (e : pValue ENV.t) (ll : letlist) :
  expr -> world -> (pValue * world) =
...
  | Unit -> return (pUnit ll)
  | MkRef x ->
      recurse x >>= (fun px ->
      let storeId = freshVar () in
      get >>= (fun s ->
      put {counter = s.counter; store = STORE.add storeId px s.store} >>
      return (static ll (SRef storeId) (MkRef (Var px.dyn)))))
  | GetRef r ->
      recurse r >>= (fun pr ->
      (match pr.static with
      | Some (SRef storeId) ->
          get >>= (fun s ->
          return
            (match STORE.find_opt storeId s.store with
            | Some x -> x 
            | None -> dynamic ll (GetRef (Var pr.dyn))))
      | None -> return (dynamic ll (GetRef (Var pr.dyn)))
      | _ -> assert false))
  | SetRef (r, x) ->
      recurse r >>= (fun pr ->
      recurse x >>= (fun px ->
      (match pr.static with
      | Some (SRef storeId) ->
          get >>= (fun s ->
          put {counter = s.counter; store = STORE.add storeId px s.store} >>
          return (pUnit ll))
      | None ->
          get >>= (fun s ->
          put {counter = s.counter; store = STORE.empty} >>
          return (pUnit ll))
      | _ -> assert false)))

呼。终于搞定了。

跟上节一样,我们总结出了一个写任意partial evaluator的方案:

0:写一个defintional interpreter

1:把value变成partially static value

2:用ANF防止复制

3:reify effect

4:记得处理scope跟dynamic control。