マクロの使用例

マクロ定義の実例として、簡単なオブジェクトシステムを書くことを想定したケースが紹介されている。

ここで登場するオブジェクトは、以下のような銀行口座を表すオブジェクト。(以下のC#コードは動作確認してません)

using Sysmtem;
public class Account
{
  // 名義人
  string _owner;
  // 残高
  int _balance;

  // 口座開設
  public Account(string owner, int balance)
  {
    _owner = owner;
    _balance = balance;
  }

  // 名義人の氏名を取得
  public string Owner { get { return _owner; } }
  // 預金残高を取得
  public int Balance { get { return _balance; } }
  // 現在の状況を表示
  public void Show()
  {
    Console.WriteLine("{0}'s balance: {1}", _owner, _balance);
  }
  // 預け入れ
  public int Deposit(int amount)
  {
    _balance += amount;
    return _balance;
  }
  // 引き出す
  public int Withdraw(int amount)
  {
    _balance -= amount;
    return amount;
  }  
}

これをマクロ使わずにやると、例えば以下のようになるかもしれない。

(define make-account
  (lambda (owner balance)
    (lambda (message . args)
      (case message
        ;; 名義人を取得
        ((get-owner) owner)
        ;; 残高を取得
        ((get-balance) balance)
        ;; 現在の状況を表示
        ((show) (print owner "'s balance: " balance))
        ;; 預け入れ
        ((deposit) (inc! balance (car args)) balance)
        ;; 引き出し
        ((withdraw) (dec! balance (car args)) (car args))))))

口座開設して、一通りできることをやってみると

gosh> (define acc (make-account 'foo 10000))
acc
gosh> (acc 'get-owner)
foo
gosh> (acc 'get-balance)
10000
gosh> (acc 'show)
foo's balance: 10000
#<undef>
gosh> (acc 'deposit 2300)
12300
gosh> (acc 'show)
foo's balance: 12300
#<undef>
gosh> (acc 'withdraw 2500)
2500
gosh> (acc 'show)
foo's balance: 9800
#<undef>

こんな感じ。
この例では、(口座オブジェクトとしての)手続きを作成する様子が丸見えで、なんだか

手続きを使ってオブジェクトもどきを作って見せただけ

感が否めない。どうせなら単なる手続きじゃなくてオブジェクトを生成している感を前面に押し出したいところだ。
ってことで、「銀行口座を作成する」というまとまった概念を表すべく、マクロの出番。
マクロで同じことをやるとすると

(define-syntax object
  (syntax-rules ()
    ((object (ivar ...) (method (arg ...) body ...) ...)
     (lambda (ivar ...)
       (lambda (message . args)
         (case message
           ((method) (apply (lambda (arg ...) body ...) args))
           ...))))))

;; make-accountをマクロobjectを使って書き換え
(define make-account
  (object (owner balance)
          (get-owner () owner)
          (get-balance () balance)
          (show () (print owner "'s balance: " balance))
          (deposit (amount) (inc! balance amount) balance)
          (withdraw (amount) (dec! balance amount) amount)))

こうなる。
なんとなく、C#のクラス定義っぽく見えてくるようなこないような。
もちろん、accでの例と同じインターフェースが利用できる。


ownerやbalanceは、それを保持した環境フレームがずっと生きてるからフィールド(メンバ変数)としての役割を果たせるわけだな。
オブジェクトも手続きとして表現できるっていうのは、自分にとってはかなり興味深いことだ。

実は...

マクロobjectの定義がなかなか理解できなかった。
それは、パターン変数とそうでないものを区別して見れてなかったからだと思う。
argやbodyはパターン変数で、messageやargsはパターン変数ではない。
あと、apply使ってるところも少しトリッキーな気がする。