A-Normalization / A正規化 - Scheme VM を書く

ここに書いてある内容は古いです。A正規形まとめをご参照ください。
Gauche VM が採用している IForm という中間表現は A-Normal Form(A正規形)を拡張したもの。A-Normal Form を学んでみる。
The Essence of Compiling with Continuations という論文を読む。

A-Normalization(A正規化)とは計算の途中経過に対して変数を割り当てる変換。

(+ (+ 2 2) (let ((x 1)) (f x)))
=>
(let ((t1 (+ 2 2)))
  (let ((x 1))
    (let ((t2 (f t1)))
      (+ t1 t2))))

もう少し一般的には

(let1 x (let1 y M1 M2) M3)
=>
(let1 y M1
  (let1 x M2
    M3))

M3 に y はあらわれない。(事前にα-reduction)。


メリットや意味は?

  • コンパイラの中間表現として適している
  • コードが単純な計算の繰り返しに変換されて扱いやすくなる
    • 多くの最適化が β-reduction η-reduction として表現できるようになる
    • ブロックや分岐をまたいでコードをマージすることで最適化が可能


論文の A linear-time A-normalization algorithm を Gauchde で書いてみたが動作が怪しい。

#!/usr/bin/env gosh
(use util.match)

;; normalize
(define (normalize-term M) (normalize M (lambda (x) x)))

(define (normalize M k)
  (match M
    [('lambda params body)
     (k `(lambda ,params ,(normalize-term body)))]
    [('let (x M1) M2)
     (normalize M1 (lambda (N1) `(let (,x ,N1) ,(normalize M2 k))))]
    [('if0 M1 M2 M3)
     (normalize-name M1 (lambda (t) (k `(if0 ,t ,(normalize-term M2) ,(normalize-term M3)))))]
    [(Fn . M*)
     (if (eq? Fn '+)
         (normalize-name* M* (lambda (t*) (k `(,Fn . ,t*))))
         (normalize-name Fn (lambda (t) (normalize-name* M* (lambda (t*) (k `(,t . ,t*)))))))]
    [else (k M)]))

(define (normalize-name M k)
  (normalize M (lambda (N)
                 (if (symbol? N)
                     (let ([t (gensym)])
                       `(let (,t ,N) ,(k t)))
                     (k N)))))

(define (normalize-name* M* k)
  (if (null? M*)
      (k '())
      (normalize-name (car M*) (lambda (t) (normalize-name* (cdr M*) (lambda (t*) (k `(,t . ,t*))))))))

;; (normalize-term '(func 1 (let (a 3) (+ a b))))
;; => (let (G67 func) (let (a 3) (let (G68 a) (let (G69 b) (G67 1 (+ G68 G69))))))
;; (normalize-term '(lambda (a) (add1 (let (x (f 5)) 0))))
;; (normalize-term '(+ (+ 2 2) 3))

まとめ

A-normalization した中間形式を採用すると最適化(主にβ簡約?)しやすいらしい。

もやもやすること

  • 自分の処理系で A-normalization する方法
  • let/lambda など分かるが、set!/named letは?
  • display closure と A-normalization の関係
  • box と A-normalization の関係
  • call/cc と A-normalization の関係
  • そもそも CPS を完全には理解できていない
  • K正規化とA正規化の違い

次の一手

S式に登場する要素を A-normalization した場合の想定される結果の対応表的なものを作る?。そうすればどこがよく分からないか分かるだろう。
また処理系の instruction セットが要求する情報と A-normal form の構造を考えるとか。
まだまだ闇だ。