我们在上一篇文章中,描述了一个通用的优化技术:
找到时间差,把数据分成已知跟未知,并且用代码表示后者。
但是,这种做法有两个问题:
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。