想了想,最后还是觉得应该用这篇paper开头。
什么是Defunctionalization?
简单的来说,Defunctionalization是一个程序变换。更具体的说,Defunctionalization接受一个高阶编程语言的程序,输出一个一阶编程语言的程序。这通过一下变换完成。
- 创建一个代数数据类型,lam,跟创建一个函数apply: (lam * Any) -> Any(是的,naive的Defunctionalization并不类型安全)
- 对于所有a -> b类型作为一等公民(不是直接调用)出现的地方,把类型a -> b变换成lam。
- 对于所有构建闭包的地方,创建一个新的lam构造子。这个构造子的成员就是这个闭包捕获的变量。
- 同时,在apply函数内,给出该lam构造子的实现 - 而这实现照抄原closure的body。
- 对所有调用闭包f x的地方,改为apply (f, x)。
举个例子,假如我们有以下代码,
(* aux : int * (int -> int) -> int *)
fun aux (i, f)
= f i
(* main = fn : int * int list -> int list *)
fun main (i, js)
= let fun walk nil
= nil
| walk (j :: js)
= (aux (i, fn i => i + j)) :: (walk js)
in walk js
end
Defunctionalize后,会成为
datatype lam = LAM of int
(* apply : lam * int -> int *)
fun apply (LAM j, i)
= i + j
(* aux_def : int * lam -> int *)
fun aux_def (i, f)
= apply (f, i)
(* main_def : int * int list -> int list *)
fun main_def (i, js)
= let fun walk nil
= nil
| walk (j :: js)
= (aux_def (i, LAM j)) :: (walk js)
in walk js
end
这时候,我们可以看到
- aux的一个f类型从(int -> int)成为lam
- fn i => i + j成为了Lam j,而apply(Lam j, i)会返回i + j
- aux函数原先f i的地方,改为调用apply (f, i)
Defunctionalization的用处是什么?
一般来说,Defunctionalization可以把闭包编译上代数数据类型,于是可以通过实现代数数据类型来实现闭包。但是,我们一般不这样整,是因为闭包实现本来也不难,而defunctionalization以后诞生的巨大的pattern matching会带来性能问题。
尽管如此,Defunctionalization依然很有意思。这篇paper,就是在探究Defunctionalization跟各种常见PL概念之间的联系。
更具体的说,
- church encoding的defunctionalization是原数据结构
- 用DList数据结构实现reverse的程序,在defunctionalize下,就是最经典的reverse as foldl of cons
- 如果我们有一个CPS的程序,我们把continuation defunctionalize后可以玩一些有趣的东东。CPS变换+Defunctonalization是一个很经典的连招。
- 对于一个small step operational semantic interpreter, CPS + Defunctionalization = Contextual Semantic
- 对于一个Definitional Interpreter,这样整可以给出对应的Abstract Machine,比如Call By Name/Value对应着Krivine’s Machine跟CEK Machine
- 对于一个0^n1^n识别器(试图识别一个字符串,前面是一串0,后面是同等长度的一串1),CPS + Defunc = PushDown Automaton,而这时候continuation/stack刚好代表了peano natural,就是在数多吃了多少个0
- 对于一个regular expression matcher,CPS + Defunctionalization = stack based regular expression matcher
就这样。额外阅读:
- Defunctionalization of Typed Programs - 还记得上面说Defunctionalization不是type-safe的吗?这篇paper把这修复了。
- Cutting out continuations - 上面举出的很多工作,都是CPS变换后上Defunctionalization。这篇paper说这两个步骤可以合二为一。
- Defunctionalized interpreters for programming languages - 这篇paper把各种各样的operational semantic/denotational semantic联系在一起,属于‘semantic的大一统’paper。(不过吐槽一下,忘了axiomatic semantic了。不过dijkstra monad is nothing but state monad continuzed,按这个角度口胡几下?)