複数の手続きで状態を共有する


Tags: R6RS, 手続き, ライブラリ, マクロ

いちばん簡単なのはライブラリを使うことである。

(library (counter)
  (export counter-value reset! inc! dec!)
  (import (rnrs))
  
  (define v 0)

  (define (counter-value)
    v)

  (define (reset!)
    (set! v 0))

  (define (inc!)
    (set! v (+ v 1)))

  (define (dec!)
    (set! v (- v 1)))
  )

あるいは、 let で閉じ込まれた環境を参照する。

(import (rnrs))

(define counter-value)
(define reset!)
(define inc!)
(define dec!)

(let ((v 0))
  (set! counter-value (lambda () v))
  (set! reset! (lambda () (set! v 0)))
  (set! inc! (lambda () (set! v (+ v 1))))
  (set! dec! (lambda () (set! v (1 v 1)))))

もしくは次のようなマクロをつくったうえで、字句環境を閉じ込む。

(import (rnrs))

(define-syntax define-values
  (lambda (x)
    (syntax-case x ()
      ((_ (v vs ...) expr)
       (for-all identifier? #'(v vs ...))
       (with-syntax (((t ts ...) (generate-temporaries #'(v vs ...))))
         #'(begin
             (define v)
             (define vs)
             ...
             (define tmp
               (let-values (([t ts ...] expr))
                 (set! v t)
                 (set! vs ts)
                 ...
                 #f))))))))

(define-values (counter-value reset! inc! dec!)
  (let ((v 0))
    (values
     (lambda () v)
     (lambda () (set! v 0))
     (lambda () (set! v (+ v 1)))
     (lambda () (set! v (- v 1))))))

define-values の定義の最後の define は define-values を定義として扱うためのダミーである。これにより define-values は次のような使い方ができる。

(let ()
  (define foo 10)
  (define-values (counter-value reset! inc! dec!) ...)
  (define bar 100) ; <= syntax error if define-values transcribe expression
  (reset!)
  (inc!) 
  ...
  (counter-value))