- OCaml   (most mature)
- Haskell (mostly works)
- Scheme  (a bit out of date)
| 1
 | Extraction "imp1.ml" ceval_step.
 | 
When Coq processes this command:
| 1
2
 | The file imp1.ml has been created by extraction.
The file imp1.mli has been created by extraction.
 | 
如果不做任何处理的话…生成的 ml 里的 nat 则都会是 Church Numeral…
  We can tell Coq how to extract certain Inductive definitions to specific OCaml types.
we must say:
  
    - how the Coq type itself should be represented in OCaml
- how each constructor should be translated
| 1
 | Extract Inductive bool ⇒ "bool" [ "true" "false" ].
 | 
  also, for non-enumeration types (where the constructors take arguments), 
we give an OCaml expression that can be used as a “recursor” over elements of the type. (Think Church numerals.)
| 1
2
3
4
 | Extract Inductive nat ⇒ "int"
  [ "0" "(fun x → x + 1)" ]
  "(fun zero succ n →
      if n=0 then zero () else succ (n-1))".
 | 
| 1
2
3
 | Extract Constant plus ⇒ "( + )".
Extract Constant mult ⇒ "( * )".
Extract Constant eqb ⇒ "( = )".
 | 
  注意:保证提取结果的合理性是你的责任。
| 1
 | Extract Constant minus ⇒ "( - )".
 | 
比如这么做很诱人……但是我们 Coq 的定义里 0 - 1 = 0, OCaml 的 int 则会有负数…
Recursor 的理论与实现 - a “encoding” of case expression and sum type
| 1
2
3
4
5
6
 | Fixpoint ceval_step (st : state) (c : com) (i : nat)
                    : option state :=
  match i with
  | O => None
  | S i' =>
    match c with
 | 
| 1
2
3
4
 | let rec ceval_step st c = function
  | O -> None
  | S i' ->
    (match c with
 | 
| 1
2
3
4
5
 | let rec ceval_step st c i =
  (fun zero succ n -> if n=0 then zero () else succ (n-1))
    (fun _ -> None)     (* zero *)
    (fun i' ->          (* succ *)
    match c with
 | 
注意我们是如何使用 “recursor” 来替代 case, match, pattern matching 得。
recall sum type 在 PLT 中的语法与语义:
| 1
2
3
4
5
6
7
8
 | T ::= 
  T + T
e ::=
  case e of
    | L(e) => e
    | R(e) => e
 | 
| 1
2
3
4
5
6
7
8
9
10
11
12
13
 |                       e → e' 
                  ------------- (work inside constructor)
                  C(e) -> C(e')
                      e → e' 
          -------------------------------   (work on the expr match against)
          case e of ... →  case e' of ...
     -----------------------------------------------  (match Left constructor, substitute)
     case L(v) of L(x) => e1 | R(y) => e2 → e1 [v/x]
     -----------------------------------------------  (match Right constructor, substitute)
     case R(v) of L(x) => e1 | R(y) => e2 → e1 [v/x]
 | 
可以发现 case 表达式可以理解为一种特殊的 application,会将其 argument 根据某种 tag (这里为构造函数) apply 到对应的 case body 上,
每个 case body 都是和 lambda abstraction 同构的一种 binder:
| 1
2
3
4
 |  L(x) => e1     ===   λx.e1
 R(x) => e2     ===   λx.e2 
 case v e1|e2   ===   (λx.e1|e2) v      -- `e1` or `e2` depends on the _tag_ wrapped on `v`
 | 
这个角度也解释了 Haskell/SML 在申明函数时直接对参数写 pattern match 的理论合理性.
根据经验几乎所有的 binding 都可以被 desugar 成函数(即 lambda expression).
难点在于我们如何 re-implement 这个 tag 的 switch 机制?
对于 Inductive nat 翻译到 OCaml int 时,这个机制可以用 v =? 0 来判断,因此我们的 recursor 实现为
| 1
2
3
4
 | fun zero succ                (* partial application  *)
  n -> if n=0                (* 判断 tag ... *)
       then zero ()          (* 0   case =>  (λx.e1) v *)
       else succ (n-1)       (* S n case =>  (λx.e2) v *)
 |