[Anthy-dev 2888] Re: [r5rs] storage-compact.h 再々編

Back to archive index

YamaKen yamak****@bp*****
2006年 4月 20日 (木) 07:35:53 JST


At Tue, 18 Apr 2006 16:52:37 -0700,
jun.l****@gmail***** wrote:
> 
> YamaKen <yamak****@bp*****> writes:
> >> (2) 関連部分が散らばっている
> >> 
> >> これは fatty にも言えることですが、小さな変更をするにも変更箇所が沢山
> >> あり、全てを把握 & 確実に変更 + coherency を確保するのは大変です。実際、
> >> macro を実装するにあたって型を二つ追加しようとしただけなのに、
> >> 
> >> -構造体を追加
> >> -検索+移動
> >> -MAKE_* を追加
> >> -検索+移動
> >> -Accessor を追加
> >
> > 実験段階ならここらあたりで適当に端折っとくのがいいと思います。
> > こんなんでどうでしょう。
> >
> > storage-fatty.h:
> > inline ScmObj
> > MAKE_HOGE(fuga, hoga)
> > {
> >     return hoge(fuga, hoga);
> > }
> 
> んー、どういう意味かよくわかりません。私が問題にしてるのは、移動が必要
> 以上に多い (一緒に変更する部分が離れてる→見通しが悪い+面倒) という点
> であって、MAKE_* とか個別の interface がどう実装されてるかという話では
> 無いんですが。

その問題はインタフェイス自体を新設する試行錯誤の段階で起こるのだ
から、以下のような4層にまたがった変更を律義に行う代わりに、

内部エイリアス     MAKE_HYGIENIC_MACRO           sigschemeinternal.h
公開インタフェイス SCM_MAKE_HYGIENIC_MACRO       sigscheme.h
SAL                SCM_SAL_MAKE_HYGIENIC_MACRO   storage-foo.h
実装               scm_make_hygienic_macro他     storage-foo.h, storage*.c

一番外側のインタフェイスに合わせて直接実装を書いて実験を進めれば
ファイル間の移動を無くせるんじゃないかという話です。

実装               MAKE_HYGIENIC_MACRO           storage-foo.h

inline関数を例に出したのは、プロトタイプと関数本体を分ける事なく
他のマクロ定義と同じ場所に実装を集約できる事と、インタフェイスが
確定してレイヤを分ける時にscm_make_hygienic_macro()等に実装をそ
のまま流用できるという事を示したかっただけです。当然マクロで書い
てあっても構いません。

SAL APIが確定した後は、新たなstorageを実装する際にいじる必要のあ
るファイルはstorage-foo.hとstorage*.cに限定されます。

> > SALの目的は今後も新たな実装が現れる予定になっているストレージ層
> > の安全な分離と挿し替えにあるので、インタフェイスを明確に規定する
> > 必要があります。SAL_プリフィクス付きのマクロを無くしたとしてもこ
> > の規定は文書等によって為される必要があり、さらに機械的なチェック
> > ができなくなるのでストレージ実装者もlibsscmユーザも間違いを起こ
> > す可能性が高まります。これらの点から見て、現状のSAL_プリフィクス
> > 付きのマクロによるAPI規定層を一段噛ませた方が開発・保守コストが
> > 低くなると考えています。
> 
> あれ? Interface は MAKE_* + accessors + INIT() [ + FIN()?] +
> MARK/UNMARK ぐらいでほぼ固定だと思ったんですが。それに変更を加えること
> になったとしても、機械的なチェックが活躍する…その、まあ、機会、…がど
> ういうものか思いつきません。

a storageが実装するべきインタフェイスのsetとその引数の名前・数を
正確に規定できます。全て_SALプリフィクス付きなので各種スクリプト
加工(例えばドキュメント生成)にも便利です。ちょっと面倒だけど静的
引数型チェックも仕込めるでしょう。各種デバッグ・計測用コードを埋
め込むエントリポイントとして利用する事もできます。それから後述の
grepによる境界面の検出も捨て難いです。

以下の例はstorage-fatty.hで定義されたINT_VALUE()の引数の個数が間
違っていた場合のエラーです。

  - SALあり
    macro "SCM_SAL_INT_VALUE" requires 2 arguments, but only 1 given

  - SALなし
    macro "SCM_INT_VALUE" requires 2 arguments, but only 1 given

SALありの場合は、原因がstorage層の実装にあると機械的に特定できま
すが、なしの場合は以下のいずれが原因かを脳味噌を使って考える必要
があります。まあ開発コンテキストから自明だと思うんで屁理屈に近い
ですが。

  - ユーザがSCM_INT_VALUE()の引数指定をミスった
  - storage層の実装が誤っている

storageの実装にマクロ定義そのものが欠けている場合は以下のように
なります。これもSALありの場合の方が原因がstorage実装にあるという
事が明確に分かって好ましいです。combined-souce mode用のヘッダで
SCM_INT_VALUEがundefされてしまった等の可能性を排除できるので。

  - SALあり
    warning: implicit declaration of function `SCM_SAL_INT_VALUE'

  - SALなし
    warning: implicit declaration of function `SCM_INT_VALUE'

> 例えばある user が storage-a にある非公開 
> API の FOO() を使ってたとして、FOO() を実装してない storage-b に 
> config option で変更したとしましょう。SAL_ が噛ませてあろうがなかろう
> が、cpp は文句を言います。逆に、SAL_ がある事によって FOO() の使用を阻
> 止することもできません。BAR() が SAL_BAR() と定義されてても BAR() と定
> 義されてても FOO() は外から見えますし。

非公開インタフェイスの隠蔽についてはその通り。これはSAL API以外
定義してないstorage-lintとか作ってチェックするしかないと思います。

しかし、libsscmユーザに利用可能APIの情報を提供するために
storage-*.hを閲覧させるのには賛成できません。sigscheme.hに集約さ
れているべきです。

> 冗長な prefixing について言ったのはこのためです。FOO() が MTAG_FOO()、
> ましてや OTHERS_CAR_FOO() だったら明らかに scheme level の概念ではない
> ものが混じってるので、公開でないことはわかりそうなものです。

それは脳味噌を使った判別ですね。確実性に欠けます。例えば
SCM_ENTYPE()はfatty固有のマクロですが、公開APIに見えてもおかしく
ありません。やはり機械的に判別できる必要があると思います。

> 一方 SAL_ があると試行錯誤の自由が縛られます (こっちは OK ですよね?)。

上述の通り、ここは無問題と認識してます。

> SAL_ を設けることが文書での API 定義をサボる理由になるわけでもないし、
> 新しい設計を書くにしてもどうせ文書を基本に書くことになるので、特に 
> SAL_ をつけとく merit が見えんのです。

それはちょっと現実からかけ離れてるんじゃないかと。SAL APIは今後
もlibsscmユーザにあまり影響が及ばない範囲でちょくちょく気軽に改
訂されてゆくと思うし(例: INT_SET_VALUE廃止)、その過程で文書との
不整合や記述ミスが全く発生しないというのは考え難いです。

さらに言えば、ごく限られたstorage実装者のためだけに全SALインタフェ
イスを網羅した文書を用意するのは、工数がかかるだけでなく余計な誤
解やトラブルの元になり誰も幸せにならないと思います。コンセプトの
解説と参考にすべき例へのポインタを示した簡単な文書で済ませて、後
は一次情報であるコードを参照させるべきだと思います。

SAL APIは一般のSigScheme APIからは機械的に分別できないと不便です。
SAL_プリフィクス無しだと、例えばSCM_CONSP()やSCM_NULLがSALを構成
するインタフェイスだという事がコードからは分かりません。かと言っ
てそれらをsigscheme{,internal}-sal.hに分離すると、今度はlibsscm 
ユーザに向けた公開APIの一覧性(sigscheme.h)が失われます。
SCM_CONSP()とSCM_LISTP()は同じファイルで定義されていて欲しいです。

以下のように簡単に境界面を一覧してジャンプできる利便性を失いたく
ありません。

API:  M-x grep _SAL_ sigscheme*.h
実装: M-x grep _SAL_ storage-fatty.h

実際、今APIを一覧してSCM_MAKE_HYGIENIC_MACRO()等の引数仕様の規定
が欠けている事を発見できました。sigscheme.hを見た時には気付いて
いませんでしたが、マクロ定義部分だけを一覧する事によって他のマク
ロとの差異が目に留まりました。

-#define SCM_MAKE_HYGIENIC_MACRO           SCM_SAL_MAKE_HYGIENIC_MACRO
+#define SCM_MAKE_HYGIENIC_MACRO(rules, defenv) \
+    SCM_SAL_MAKE_HYGIENIC_MACRO(rules, defenv)

> > しかしfinalizationの分離は嬉しいですか? 現状のfree_cell()を見る
> > 限り、GC視点で集約してある方がコンパクトで読みやすいように思うし、
> > ほとんど追加される機会のない新オブジェクトタイプのコードの書きや
> > すさより、GC全体の切口での保守しやすさの方が重要と思います。
> 
> 私も aesthetically は別に嬉しかないです。でも面倒事がありまして。
> Compile が通るまで修正していくのが嫌なので確認取ってませんが、accessor
> assert を有効にして今の storage-compact.h で GC を走らせると assertion
> faiure が出ませんか? Sweep 時には ptag のついてない pointer しかないの
> で、assert を諦めるか別の accessor を使うことになります。それの為だけ
> に ad-hoc な accessor をバラバラと置くよりは目的がわかりやすい fin() 
> にまとめた方がいいかと判断したんです。その結果 BY_PTR のような気持ち悪
> い層ができあがってしまってるわけですが。
> ;; 「失敗」箇所の一つ。何か良い方法無いすかね。

なるほど。動機はわかりました。でもその目的ならFIN()をSALのインタ
フェイスにまで格上げする必要はないと思います。compact独自の内部
インタフェイスとしてFIN()を作るか、compact用のfree_cell()では内
部表現に直接触るかすればいいと思います。

将来的にGCのstorage実装非依存性をもっと高める必要が出てきたらSAL 
に格上げした方がいいかもしれませんが、その時はその時で未知の要求
が出てくるような気がするので、現時点では無理に標準化せず経験を積
むに留めた方がいいような気がします。

> > 個々の操作ではピンとこない名前もありましたが、デザインの根幹では
> > ないので当面は井上さんが分かりやすければそれでいいと思います。
> 
> どのへんでしょうか。とりあえず今のところわかってもらう気が薄いものは 
> _BY_PTR() と CELL_ の関係ぐらいなんですが。あ、↓STRIP_TAG() とかの事
> かな。

_BY_PTR()とか_SPLITX_AT()とかですけど、ピンとこないだけで別に論
理的におかしいわけではないです。私も代案思い付かないし。

> > 私からの希望としては、[Anthy-dev 2532]とstorage-compact.hの
> > SCM_INT_MSB付近のコメントに書いたように、元の実装ではstringや
> > vectorのlengthがintegerより無駄に広かったり符号ビットが未使用だっ
> > たりすところを最適化して欲しいです。
> 
> えーっと、MSB を mutable flag にするという話ですか。(Integer より広い
> というのはよくわかりませんが…。Integer は 32-4 bits, string /
> vector-length も mutable flag を考えると 32-4 bits でピッタリ合います。)

lengthは常に非負なので、integerをそのまま格納可能なビット幅を
length向けに確保すると符号ビット分が無駄になります。

この1ビットを削ればtype encodingにだいぶ余裕がでるんじゃないかと
思っての提案です。

> MSB を mut(ry flag にすると mutability test は速くなりますが、長さを取
> 得する度に unmask が入るので、LEN() に 0x7FFF... の load が追加されま
> す。
> 
> それに対して下の方に置いておくと、mut(ry test した後 >> 1 で長さが取得
> できます。長さだけを取るときの penalty は 0 です。Mut(ry は test する
> けど長さは取得しない場面というのをちょっと思いつかないので、下の方に置
> いた方が得かなーと思いました。

あ、確かに。compactだと値の取り出し時に元々必ずshiftが入ってるん
でしたね。これは取り下げます。

-------------------------------
ヤマケン yamak****@bp*****



Anthy-dev メーリングリストの案内
Back to archive index