Let's start Scheme

2012-12-02

R6RSとR7RSのライブラリ比較

この記事はLisp Advent Calendar 2012の3日目の記事として書かれました。

3日目は最近出たR7RSドラフトセミファイナル(とWG1が読んでいる)と、R6RSのライブラリシステムの文法的な違い(意味的なものも多少)を見ていこうと思います。

まずは、R6RSのライブラリのおさらいです。R6RSは制定されてすでに数年(2007年だったっけ?)経っているので、皆さんもうご存知ですよね。なので以下はごく簡単な例;
(library (foo)
   (export bar)
   (import (only (rnrs) define quote))
  (define bar 'bar))
#|
ライブラリ構文の定義
(library <library-name>
   (export <export-spec> ...)
   (import <import-spec> ...)
  <body> ...)
|#
R6RSのライブラリは暗黙もしくは明示的なマクロ展開フェーズを持ちます。これはマクロ内で使われる手続きと、ランタイムで使われる手続きを明示的に分ける必要があったから(らしい)です。(個人的にこのフェーズ分けは考えるのが面倒なので、暗黙的に解決してくれる処理系の方が好きです。好みの問題ですけど。)

次はR7RSのライブラリの例;
(define-library (foo)
  (import (scheme base) (scheme write))
  (begin (define foo 'foo)
         (define (print . args) (for-each display args)(newline)))
  (define (printw . args) (for-each write args)(newline))
  (export foo print)
  (export bar)
  (begin (define bar 'bar))
  (cond-expand
   ((library (srfi 1))
    (import (rename (only (srfi 1) alist-cons) (alist-cons acons))))
   ((library (srfi :1))
    (import (rename (only (srfi :1) alist-cons) (alist-cons acons))))
   (else
    (define (acons a b c) (cons (cons a b) c))))
  (export acons))
#|
ライブラリ構文の定義
(define-library <library-name>
   <library-declaration> ...)
|#
あえて違いを出すように書いてありますが、普通はexportとimport句はまとめると思います。
R7RSではR6RSと違い順番がライブラリ定義の順番及びその個数が規定されていません。なので、上記のコードは既存のR7RS処理系(ChibiとSagittariusしか知らない)で正しく動きます。(規定されていないので処理系依存になるかもしれません。)
R7RSのimport句はR6RSとは異なりforがありません。これはR7RSではマクロの展開フェーズが規定されていないからです。処理系は暗黙的にしかマクロ展開フェーズを持つことができません。(問題になる処理系の方が少ない気はしますが)
一方export句でもrenameのあり方がR6RSとは異なります。R6RSではrename句の中に複数の識別子を定義することができましたが、R7RSでは一つのrenameに対して一つの識別子しか定義することができません。
また、cond-expandはSRFI-0のものとは多少違い、featureとしてlibraryが追加されています。これによってライブラリが存在するかのチェックが可能になり、上記のように無ければ定義、みたいなことが可能(なはず)です。
また、以下の例はR7RSが既存のR5RSで書かれたプログラムをそのまま使用することが可能であるということを強く意識して作られたことを示す例です。
;; bazz.sls (for Sagittarius) or bazz.sld (for Chibi)
(define-library (bazz)
  (export bla)
  (import (scheme base))
  (include "bazz.scm"))

;;--
;; bazz.scm
(define bla 'bla)
ライブラリ内で使用されるinclude、include-ci(case insensitive)はライブラリ上にそのままファイルの内容が展開されるイメージです。処理系によっては以下のように解釈するかもしれませんが、まぁ無視できる程度の差異でしょう(Sagittariusはこのように解釈する)。
(define-library (bazz)
  (export bla)
  (import (scheme base))
  (begin (define bla 'bla)))
事実、Chibi-Schemeのほぼ全てのライブラリはincludeを用いて記述されています。また、今回のドラフトから追加されたinclude-library-delarationsは以下のように使えば処理系ごとの拡張子の違いを簡単に吸収することが可能です。(ただし、現状ではSagittariusのみがこの構文をサポートしている状態です)。
;; boo.sls or boo.sld
(define-library (boo)
   ;; もちろん、この上下にいろいろ書いてもOK
   (include-library-declarations "boo.decl"))

;;--
;; boo.decl
(export bah)
(import (scheme base))
(include "boo.scm")

;;--
;; boo.scm
(define bah 'bah)
include-library-declarationsはinclude、include-ciとほぼ同様の動作をしますが、include先のファイル(この場合はboo.decl)は<library-declarations>で構成されている必要があります。R7RSの議論の中ではこの構文は巨大なexport句を外出しにし共有化するために作られてのですが(*1)、このような使い方もできるのではと思っています。

R7RSはまだドラフトで上記の構文を試せる処理系はまだ少ないですが(2012年12月現在)、多くの処理系製作者がすでに追従するということを表明しています(*2)。個人的にはR7RSの構文の方がR6RSに比べて柔軟かつ処理系の差異も吸収しやすいのではと考えています。

3日目はR6RSとR7RSのライブラリ構文の比較を行いました。

*1: たとえば(srfi :1)と(srfi :1 lists)は同一のexport句を持ちますが、R6RSのライブラリではどちらのライブラリも同一のexport句を記述する必要があります。Sagittariusは:allキーワードがあるので、多少手が抜けたりしますが・・・
*2: Kawaはすでに可能な限り追従するとR7RSのメーリングリスト上で製作者が言っています。Larceny、Gauche、及びChickenも最初のドラフトの段階では追従するということを回答していました。参照

No comments:

Post a Comment