match と cut
長いこと「match なんて酸っぱい葡萄だ(=match なんかなくてもプログラムは書ける)」と思っていたが、やっと分かったかもしれない。
;; S式を変換する例。(ret) は "ret void", (ret '(i32 8)) は "ret i32 8", ;; (ret '(i32 8) '(i1 1)) は "ret i32 8, i 1" にしたい。 ;; (ret) => ret void ;; (ret (type value)) => ret <type> <value> ;; (ret (type value) (type value)) => ret <type> <value>, <type> <value> (define (ret-inst . args) (match args (() "ret void") (((type value) ...) (format #f "ret ~a" (string-join (map (lambda (ty va) (format #f "~a ~a" ty va)) type value) ", "))) (_ (errorf "mulformed: ~a" args))))
ほとんど仕様通りにプログラムにできる。どうりで compile.scm で多用されているわけだ。式をひたすら変形していくのに便利すぎ。ただし、(当然ながら)簡単なパターンマッチでは、簡単な判定しかできない(例えば上の例では (ret '(i32 ()) のようなものを許してしまう)。複雑なパターンマッチ用には ? predicate というのが使えて、これまた多用されている。少し改良したのが以下。
(define (ret-inst . args) (match args (() "ret void") ;; (ret) にマッチ ((((? symbol? type) (? (lambda (x) (and (not (pair? x)) (or (symbol? x) (number? x)))) value)) ...) ;; (ret '(i32 1)) とか (format #f "ret ~a" (string-join (map (cut format #f "~a ~a" <> <>) type value) ", "))) (_ ;; それ以外はエラー (errorf "mulformed: ~a" args))))
ついでにこれも自分にとっては「酸っぱい葡萄」だった cut を使った。
- cut は「手続きを簡潔に書ける便利なマクロ」なので lambda に近いレベルの存在。
- 良い点は簡潔に書けること。
- 悪い点は良い点の裏返しで(簡潔すぎて)引数に名前が付かないこと、だと思う。ただし実例を見ると1個の<>がほとんどで、長所のほうが大きい。上は下手な例か。また lambda よりカッコが少なすぎて、かえって見難い気がする。CL の#' も無いから、カッコの中にべたーと要素が並んでいるのはなんとなく気持ち悪い。慣れか。