Scheme でローカル定数

Common Lisp でローカルな定数を導入する構文を作ろうとする記事を読んだ。

Common Lispでローカル定数の構文 — #:g1

私は以前に似たようなことをやっていたことを思い出した。

以前に作ったマクロ define-constant は最終的に define に展開されるので、そのまま internal define に使っても機能はするのだが、あらためて let 系の形式でローカル定数を定義するマクロを書いてみることにした。

#!r6rs
(import (rnrs))

(define-syntax let-constant
  (lambda(stx)
    (syntax-case stx ()
      ((k ((var form) ...) body body* ...)
       (with-syntax
           (((t ...) (generate-temporaries #'(var ...))))
         #'(let ((t form) ...)
             (let-syntax
                 ((var
                   (make-variable-transformer
                    (lambda(stx2)
                      (syntax-case stx2 ()
                        ((set! v _)
                         (syntax-violation 'set!
                                           "attempt to assign constant"
                                           stx2
                                           #'v))
                        (_ #'t))))) ...)
               body body* ...)))))))

(let-constant ((x 1))
  ;;(set! x 2) ;; ← もし set! を試みたらエラーになる
  (display x)))

例として書いた定数 x は識別子マクロで、展開後には隠れた変数に置き換えられる。 識別子マクロは set! で代入されるときの展開形を特別に定義することも出来て、ここではそういう形になっているときは syntax-violation を使って構文エラーになるように定義した。

Document ID: d5f61355b4e55961f2af8517178d2c53