EoPL reading (65) 2.2 An Abstraction for Inductive Data Type

色々見てみるに、define-datatype の定義とかって全然関係なくって、ただのナチュラル君の大暴れな事が判明しつつあります。
しかも最初は構文木って 2 分木じゃないと駄目なの? って思ったら、それさえもナチュラルである事が判明しつつあります。やれやれ。
試験を書いてみた。以下。

(test* "(app-exp (var-exp a) (var-exp b)) is expression"
       #t
       (expression? '(app-exp (var-exp a) (var-exp b))))
(test* "(app-exp (var-exp a) (app-exp (var-exp b) (var-exp c))) is expression"
       #t
       (expression? '(app-exp (var-exp a) (app-exp (var-exp b) (var-exp c)))))
(test* "(app-exp (var-exp a) (var-exp b) (var-exp c)) is expression"
       #t
       (expression? '(app-exp (var-exp a) (var-exp b) (var-exp c))))

上記、いずれも expression 認定。別に 2 分木でなくても OK。define-datatype の実装は現時点で以下ですが

(define-datatype expression expression?
  (lit-exp
   (datum number?))
  (var-exp
   (id symbol?))
  (if-exp
   (test-exp expression?)
   (true-exp expression?)
   (false-exp expression?))
  (lambda-exp
   (ids (list-of symbol?))
   (body expression?))
  (app-exp
   (rator expression?) 
   (rand (list-of expression?))))

上記に構わず expression? って以下でもセイフなだけに。

(test* "(app-exp) is expression"
       #t
       (expression? '(app-exp)))

この件で問題なのは parse.scm の実装とか試験とかな模様。とりあえず、parse-expression 手続きの app-exp な部分を以下に修正。

	    (else
	     (app-exp
	      (parse-expression (car datum))
	      (parse-expression (if (null? (cddr datum))
				    (cadr datum)
				    (cdr datum)))))))

あと、lambda 式の body 部分は式一発な BNF なので面倒なのでそーゆー事にしときます。って上記だと二分木になるな。
else な部分を以下に修正。

	    (else
	      (let f ((rslt '(app-exp)) (datum datum))
		(if (null? datum)
		    rslt
		    (f (append rslt (list (parse-expression (car datum))))
		       (cdr datum)))))))

を、これで parse な以下の試験にパス。

(test-section "parse-expression")
(test* "parse (1)"
       '(var-exp a)
       (parse-expression 'a))
(test* "parse (2)"
       '(lambda-exp (x) (var-exp x))
       (parse-expression '(lambda (x) x)))
(test* "parse (3)"
       '(app-exp (lambda-exp (x) (var-exp x)) (var-exp a))
       (parse-expression '((lambda (x) x) a)))
(test* "parse (4)"
       '(lit-exp 5)
       (parse-expression 5))
(test* "parse (5)"
       *test-error*
       (parse-expression "a"))
(test* "parse (6)"
       '(app-exp (var-exp a) (var-exp b) (var-exp c))
       (parse-expression '(a b c)))

これで lambda body も cddr 取り出しでなんとかなる?

      (cond ((eqv? (car datum) 'lambda)
	     (lambda-exp (cadr datum)
			 (parse-expression (caddr datum))))

で、以下な試験を追加。

(test* "parse (7)"
       '(app-exp (lambda-exp (x) (app-exp (var-exp +) (var-exp x) (var-exp x))) (var-exp a))
       (parse-expression '((lambda (x) (+ x x)) a)))

試験パスしてます。いやはや。次はこの考え方を踏ませて unparse-expression の実装と試験を。あら? unparse はどう書けば良いのかな。ってか現時点での実装が以下で

(define unparse-expression
  (lambda (exp)
    (cases expression exp
	   (lit-exp (num) num)
	   (var-exp (id) id)
	   (if-exp (test-exp true-exp false-exp)
		   (list 'if test-exp true-exp false-exp))
	   (lambda-exp (ids body)
		       (list 'lambda ids
			     (unparse-expression body)))
	   (app-exp (rator rands)
		    (list (unparse-expression rator)
			  (unparse-expression rands))))))

パスしてない試験が以下。

(test* "unparse (4)"
       '(lambda (x) (f x y))
       (unparse-expression '(lambda-exp (x) (app-exp (var-exp f) (var-exp x) (var-exp y)))))
(test* "unparse (5)"
       '(lambda (x) (x))
       (unparse-expression '(lambda-exp (x) (app-exp (var-exp x)))))

試験の出力は以下なカンジ。成程。

test unparse (4), expects (lambda (x) (f x y)) ==> ERROR: GOT #<error "wrong number of arguments for #<closure (unparse-expression unparse-expression)> (required 2, got 3)">
test unparse (5), expects (lambda (x) (x)) ==> ERROR: GOT #<error "wrong number of arguments for #<closure (unparse-expression unparse-expression)> (required 2, got 1)">

うーむ。元に戻ったカンジ。こうしたら

	   (app-exp (rator . rand)
		    (list (unparse-expression rator)
			  (unparse-expression rand))))))

全部試験 NG。つい、expression な app-exp を以下にしたくなりますが、

  (app-exp
;;   (rator expression?) 
;;   (rand (list-of expression?))))
   (data (list-of expression?))))

あるいは unparse-expression 手続きが以下

	   (app-exp (data)
		    (list (unparse-expression (car data))
			  (unparse-expression (cdr data)))))))

この場合、以下が試験にパスしない。

(test* "unparse (3)"
       '((lambda (x) x) a)
       (unparse-expression '(app-exp (lambda-exp (x) (var-exp x)) (var-exp a))))
(test* "unparse (4)"
       '(lambda (x) (f x y))
       (unparse-expression '(lambda-exp (x) (app-exp (var-exp f) (var-exp x) (var-exp y)))))
(test* "unparse (5)"
       '(lambda (x) (x))
       (unparse-expression '(lambda-exp (x) (app-exp (var-exp x)))))

これはオシい、のだろうか。で、parse と同じ方式に変更してみた。

	   (app-exp (data)
		    (let f ((rslt '()) (data data))
		      (if (null? data)
			  rslt
			  (f (append rslt (list (unparse-expression (car data))))
			     (cdr data))))))))

うーん。なんとなく前に進んだ気もするんですが、そうでもなかったりします。とりあえずここで投入。

define-datatype.scm の define-datatype:case-helper なナニを以下で parse の試験が通りました。

    ((_ Variant (Purported-variant-name (Purported-field-name ...)
                  Body0 Body1 ...)
       Clause ...)
     (if (eq? (car Variant) 'Purported-variant-name)
;;         (apply (lambda (Purported-field-name ...) Body0 Body1 ...)
         (apply (lambda ((list Purported-field-name ...)) Body0 Body1 ...)
           (cdr Variant))
         (define-datatype:case-helper Variant Clause ...)))

ぬ。ちなみに現時点で unparse-expression の該当部分は以下。

	   (app-exp (data)
;;	   (app-exp (rator rand)
;;	   (app-exp (rator . rands)
		    (let f ((rslt '()) (data data))
		      (if (null? data)
			  rslt
			  (f (append rslt (list (unparse-expression (car data))))
			     (cdr data))))))))

なぜか unparse な試験がパスしなくなってます。微妙。この状態で止めた方が良さげ。ってかこの直前に戻せる状態かな。