Continuation Passing Style and Tail Recursion

之前在"Essentials of Programming Languages"中学习过CPS(Continuation Passing Style), 而笔记在blog改版后被丢弃, 故在这篇文章中重新详细的探讨下CPS以及尾递归, 就当是温故而知新. Continuation 在理解什么是"Continuation Passing Style"之前, 我们首先需要定义Continuation(惭愧的是我都不知道中文叫啥, 查了下好像是"续延"). Continuation是计算机程序控制状态的抽象表示, 换言之, 其就是一种表示程序执行中间某一计算步骤的控制上下文的数据结构, 即表示某一计算的未来. 一个形象的例子是阶乘函数: 1 2 3 4 let rec fact n = if n = 0 then 1 else n * (fact (n - 1)) 当我们计算(fact 4)时, 其执行过程为(用lisp的形式是因为更加形象): 1 2 3 4 5 6 7 8 9 10 (fact 4) => (* 4 (fact 3)) => (* 4 (* 3 (fact 2))) => (* 4 (* 3 (* 2 (fact 1)))) => (* 4 (* 3 (* 2 (* 1 (fact 0))))) => (* 4 (* 3 (* 2 (* 1 1)))) => (* 4 (* 3 (* 2 1))) => (* 4 (* 3 2)) => (* 4 6) => 24 这过程中, (* 4 #)就是一个continuation, 是(fact 3)的控制上下文, 等待着(fact 3)的计算结果传入我们的"#", (* 4 (* 3 #))也是一个continuation. 容易看到的是在这样一个计算过程中, continuation在不断增长, 因为调用栈在不断增长, 程序不得不保存*的左操作数, 计算右操作数. 接下来, 我们来看fact的尾递归版本, 所谓的尾递归(tail recursion)指的是递归函数的递归调用部分只有函数调用, 即: ...

Value restriction,从OCaml到F#

Value Restriction是什么? Value restriction是用于控制类型推断能否对值声明进行多态泛化的规则(MLton原文:“The value restriction is a rule that governs when type inference is allowed to polymorphically generalize a value declaration.”)。常出现在ML系的语言中,如SML,OCaml,F#中,其实value restriction产生的本质原因是为了保证类型系统在结合参数多态与命令式特性(imperative feature,如ref)时候的可靠性(soundness)。一个典型的例子就是: 1 2 3 4 5 6 // 如果没有value restriction let x = ref None // 'a option ref let y: int option ref = x // type checked let z: string option ref = x // type checked let () = y := Some 2 // type checked let v: string = !z // 破坏了类型安全 限制了什么? 简单来讲,value restriction限制了类型泛化只能发生在表达式的右边是句法意义上的值。那么什么是句法意义上的值呢,SML的语言规范上明确给出了什么样的表达式是句法意义上的值(准确来说是non-expansive): 常量,如13,"string" 变量,如x,y 函数,如fn x => e 除了ref以外的构造函数在值上的调用,如Foo v 类型上受约束的值,如v: t 每一个元素都是值的tuple, 如(v1, v2, v3) 每一个字段都是值的record, 如{l1 = v1, l2 = v2} 每一个元素都是值的list, 如[v1, v2, v3] 确切的来讲,只要是协变(covariant)的类型并且不和可变的特性相结合,那么它总是可以类型安全的泛化(OCaml manual原文:“As a corollary, covariant variables will never denote mutable locations and can be safely generalized.”)。即: ...