R6RSとR7RSライブラリの比較 - trivialに同じ(と見做す)もの

というわけで、今度は追加実装の必要が無さそうなものを消していく。ここでは、R6RSとR7RSで等価なものと、R7RSの方がスーパーセットになっているもの。

portやunicodeはもうちょっと真面目に見る必要が有りそうなので一旦保留。number→stringがR6RSとR7RSで互換性が無いとか知らなかった。

(rnrs base) : 等価なもの

baseは流石に被る機能が多い。
R6RSとR7RSで機能が(ほぼ)同じものは:

  • list, length, append, reverse, list-ref, list-tail - 同じ
  • string-append - 同じ
  • min, max - 同じ。R6RSではinfのケースの例が有る。
  • apply - 同じ
  • equal? - R7RSでも、equal?は循環構造の比較であっても停止しなければならないことになっている。
  • procedure?, vector?, char?, string?, symbol?, list? - 同じ。list?ってどちらでも停止性の規定は無いのか。
  • quote, quasiquote, unquote, unquote-splicing - R7RSでのquasiquote類はR6RSセマンティクスに揃えている。
  • make-vector, vector, vector-length, vector-ref, vector-set!, list→vector - 同じ。
  • make-string, string, string-length, string-ref,string-set!, list→string - 同じ。R6RSではstring-set!はmutable-stringライブラリに分離されている。でもmake-stringはbase。
  • string→symbol, symbol→string - 同じ。R7RSでもsymbol→stringの返す文字列はimmutableということになっている。
  • char→integer, integer→char - 同じ。R7RSでもUnicode値。
  • char=?, ... - 同じ。R7RSでは、char→integerの結果の比較ということになっている。R6RSではUnicode番号比較。
  • string=?, ... - ほぼ同じ。R7RSでは処理系依存の順序比較を許可している。R6RSでは文字比較の拡張であることを要求している。
  • symbol=? - 同じ。R7RSには追加のメモが有る。
  • not - 同じ
  • substring - 同じ
  • define, define-syntax - 事実上同じもの。ただし、R7RSのdefine-syntaxで定義される構文は、必ずその定義よりも後に表われる必要がある。
  • lambda, let, let*, letrec, letrec*, let-values, let*-values - これらも同じ。ループ構文をサポートしているのも一緒。
  • let-syntax, letrec-syntax - 同じ。
  • set! - R6RSのset!はidentifier-syntaxのための補助構文でもある。それが無ければ機能性は一緒。いわゆるgeneralized set!(SRFI-17)はR7RSでも採用されていない。
  • begin - 同じ。
  • if, cond, when, unless, and, or, =>, else - 同じ。when、unlessは戻値の扱いが微妙に違う。Racketでは禁止だがR6RS/R7RSはelse部の無いifが許可されている。
  • exact, inexact - R6RSからの輸入。同じ。R5RS語彙ではexact→inexactのような明示的変換だった。
  • (+ のような演算やプレディケート) - R6RSと異なり、R7RSでは完全なnumeric towerを実装する必要は無くなった。このため厳密にはサブセットと言える。ここでは同じと見做す。
  • number?, complex?, real?, rational?, integer?, exact?, inexact?, zero? positive?, negative?, odd?, even?, finite?, infinite?, nan? - 同じ。
  • boolean?, boolean=? - 同じ。
  • abs, gcd, lcm, exp, log, sin, cos, tan, asin, acos, atan, sqrt, exact-integer-sqrt,expt - 同じ。
  • numerator, denominator - 同じ。
  • floor, ceiling, truncate, round, rationalize - 同じ。rationalizeはR7RSではInf/NaNケースが例から落ちている。
  • null?, pair?, cons, car, cdr, caar, ... - 同じ。caddrのような3つ以上入るものはR7RSでは何故かオプショナルになっている。
  • make-rectangular, make-polar, real-part, imag-part, magnitude, angle - 同じ。R7RSでは、(scheme complex)ライブラリが導入されこれらは分離された。
  • _, ... - syntax-rules用の補助構文。R7RSでは一時期束縛しない方向だったが、結局束縛する方向で規格化された。
  • call/cc, call-with-current-continuation, values, call-with-values, dynamic-wind - 同じ。

ここでは無視しているが、未定義値の扱いは微妙に違う。R7RSでは、未定義値を返すときは1つの未定義値だが、R6RSでは特に規定していない。nmoshのライブラリは未定義値ではなくゼロ個の値を返すものがあるのでこの辺は微妙なところ。
R7RSではdefine-syntaxの実装が多少緩和されている。R7RSではR6RSの例にある

(let ()
  (define even?
    (lambda (x)
      (or (= x 0) (odd? (- x 1))))) ;; ← ★ ここでodd?が先に出ている
  (define-syntax odd?
    (syntax-rules ()
      ((odd? x) (not (even? x)))))
       (even? 10))

はダメということになった。こればっかりはどうしようもないので、yuniではR7RSの基準で書くことになる。

(rnrs base) : R7RSの方が高機能なもの

今やろうとしていること(R7RS実装のためのR6RS語彙を用意する)の逆は既に実装しているので、そちらを見れば良いことになる。

                 (rename
                   (except (rnrs)
                         case
                         syntax-rules
                         error
                         define-record-type
                         ;; SRFI-1
                         map for-each member assoc

                         vector-map)
                   (vector-fill! r6:vector-fill!)
                   (string->list r6:string->list)
                   (vector->list r6:vector->list)
                   (string-copy r6:string-copy)
                   (bytevector-copy r6:bytevector-copy)
                   (bytevector-copy! r6:bytevector-copy!)
                   (utf8->string r6:utf8->string)
                   (string->utf8 r6:string->utf8))

ここでexceptないしrenameされているものが該当する。

  • case - =>による手続きの起動がcond同様にサポートされている。
  • syntax-rules - SRFI-46になった。
  • map, for-each, member, assoc - これらはSRFI-1同様複数のリストを与えた場合は最短のリストに対して動作する。R6RSからは拡張されていると言える。R6RSではmemberとassocは基本ライブラリではなく(rnrs lists)の方にある。
  • vector-for-each, vector-map, string-for-each - これらもSRFI-1風に、最短のベクタに対して動作するようにR7RSでは規定されている。(ここに挙がっていないのはバグ)
  • vector-fill!, vector→list, string→list, string-copy, bytevector-copy はrangeを取るように拡張された。

errorは特に拡張というわけではなく単純に非互換だな。。非互換リスト行き。R7RSのerrorはSRFI-23。define-record-typeも非互換。R7RSはSRFI-9。
string-copyはR7RSではsubstringのスーパーセット。なので直交性のためにsubvectorとかsubbytevectorが用意されることはない。
bytevector-copy!も非互換。これはstring-copy等がrangeを取るようになった変更と揃えるために変更されている。

(rnrs base) : 微妙

eqv?はR7RSでは同じストレージを持つ手続きは真を返すという規定が追加されている。eq?についてはベクタも同様。yuniでは同じものと見做すことにしている。procedureとかbytevectorの等価性に依存したコードは書かない。
number→stringは、R6RSでは精度パラメタが有る。これは保留ということにした。

その他のライブラリ

以下のライブラリはライブラリごと等価またはR7RSの方がスーパーセット。

  • (rnrs control) - case-lambdaは(scheme case-lambda)に分離されている。他はbase。
  • (rnrs exceptions) - R6RSから輸入。
  • (rnrs programs) - R7RSでは、command-lineの戻値はimmutable stringのリストということになった。また、exitの扱いは逆転し、R7RSでは#tが正常終了でその他が適当に変換されることになった。R6RSでは#fで異常終了。引数無しはどちらでも同じ。
  • (rnrs mutable-pairs) - R7RSでは基本ライブラリ。
  • (rnrs mutable-strings) - ditto
  • (rnrs files) - 実はR6RSではファイル名は文字列以外も許可しているがそれは一旦忘れる。
  • (rnrs lists) - mempはR7RSには存在しないが、member?の拡張で同じ機能を持たせている(SRFI-1)
  • (rnrs bytevectors) - 多倍長の操作はR7RSには無い。bytevector-copy!は例外。

whenとunlessはちょっと微妙で、R6RSではbeginと等価であるとされている。R7RSでは戻値は未定義値。でもwhenやunlessの戻値を消費することはほぼ無いので、ここでは無視。
すごいどうでも良いけどR6RSでは(rnrs files)のように複数形を使うライブラリ名がR7RSでは(scheme file)のように単数形なのはなぜだろう。