識別子マクロとパラメタによる大域変数エミュレート

Scheme では R6RS になってモジュール化の仕組み (ライブラリ) が導入された。
ライブラリから import した変数へのアクセスには制約があり、破壊的に変更 (set!) することは出来ない。 例えばこんな場合にはエラーになる。

;; ライブラリ
(library (test-lib)
  (export *variable*)
  (import (rnrs))

  (define *variable* 1)
  )
;; メインプログラム
(import (rnrs) (test-lib))

(set! *variable* 2) ;;←ここでエラー
(display *variable*)

一般的にはパラメタで代用するのだが、メインプログラムの側ではパラメタを意識せずに使えるような方法は無いだろうかと考えてこんなマクロが出来上がった。

(library (identifier-parameter)
  (export define-identifier-parameter)
  (import (rnrs) (srfi :39))
  
  (define-syntax define-identifier-parameter
    (syntax-rules ()
      ((_ var val)
       (begin
         (define t (make-parameter val))
         (define-syntax var
           (make-variable-transformer
            (lambda(x)
              (syntax-case x (set!)
                ((set! _ a) #'(t a))
                (_ #'(t))))))))))
  )

使い方としては大域変数にしたい変数を定義するときに define ではなく define-identifier-parameter を使うだけだ。 このマクロを使うように先程のライブラリを修正しよう。

(library (test-lib)
  (export *variable*)
  (import (identifier-parameter) (rnrs))

  (define-identifier-parameter *variable* 1)
  )

メインプログラムの側には修正を加えていないが、これであたかも import した変数に set! できるかのように見える。 実体はパラメタだが。
Document ID: 5207508033146144b0840cfbed654857