型修飾 (妄想パート)

Queenタグは妄想俺言語用。

とりあえずAdaベースで。

静的割付け

何度も、いくらstd::stringが頑張っても言語組み込みの文字列には敵わないという話をしていますが、それでももう少し頑張ろうという一環です。
To_Unbounded_Stringやstd::stringのコンストラクタの引数の文字列のアドレスをそのまま覚えておいて、書き換えられるまではそれを使うようにすれば、定数文字列を入れている分には動的なメモリ割り当てと実行時のコピーが不要になって嬉しいと思いませんか。

function To_Unbounded_String (S : String) return Unbounded_String is
begin
   return (Data => S'Unrestricted_Access, Flag => Static);
end To_Unbounded_String;

しかし、引数の文字列がずっと残っている保証なんてありませんから、残念!
というわけでそれなら保証してやろうというわけです。

function To_Unbounded_String (S : *static* String) return Unbounded_String;

S : Unbounded_String := To_Unbounded_String ("ABC"); -- OK
S : Unbounded_String := To_Unbounded_String (Get_Line); -- ERROR

D言語のimmutableはローカル変数にも使用できますが、これは静的割り当てができるグローバル定数/変数やリテラルとそのaggregate、及びaccess constantに対するnewのようなもののみが対象となります。

推移

例えばO'Camlでは、テーブル等の構築中はmutableで、構築が終ったらmutableを外すといったことができません。抽象型にしてアクセサを用意すればいいのですが、パターンマッチが使えなくなるので微妙です。*1
というわけでD言語の推移的constは素晴しいと思います。
で、推移的constがあればアクセサは取っ払って直拙アクセスで問題なくなるのですが、やはりアクセサを使いたいときもあります。で、アクセサでも推移的constが使いたくなると思います。C++のように大量のオーバーロードを作るのは嫌なので、ひとつのアクセサでconstと非constの両方をカバーできるのが理想です。
というわけで、どれから推移してくるかを明示するようなものがいいのかなと考えています。

type T is record
   F1 : access Integer; -- 常に書き換え可能
   F2 : access constant Integer; -- 常に読み取りのみ
   F3 : access Integer *of T*; -- constantかどうかをコンテナ((Adaでは型宣言中の型名はthisとかselfとかの意味です。))と同一視
end record;

-- アクセサ
function Get_F3 (Obj : access T) return access Integer *of Obj.all*;

declare
   Obj1 : aliased T;
   Obj2 : aliased constant T;
begin
   Obj1.Get_F3.all := 1; -- OK
   Obj2.Get_F3.all := 1; -- ERROR!
end;

型ではありませんが、オーバーロード

上記の例を、たとえばコピーオンライトを仕込もうとして、やっぱりオーバーロードしたくなったとします。

function Get_F3 (Obj : access T) return access Integer; -- 共有解除を仕込む
function Get_F3 (Obj : access constant T) return access constant Integer;

現行のAdaですとこのオーバーロードは曖昧になります。
C++はOK。

int * get_f3 (); //共有解除を仕込む
int const * get_f3 () const;

しかしC++ではconstでオーバーロードしても、オーバーロードの解決には引数しか関係しませんので、無駄が発生します。

T obj;
int const *p = obj.get_f3(); //objがconstではないので共有解除される

折角Adaでは返値の型でオーバーロードできますので、こういう場合は返値の型のconstの有無を見て解決できるよう、オーバーロードの解決ルールを拡張するべきです。
実は私が大昔に作っていたQueenというコンパイラではごにょごにょどうでもいいですねはいどうでもいいです。

Universal_型

  • O'Camlで"%d"と書いた場合、文脈によってstring/format/format4/format6のどれかになります。
  • Adaで"%d"と書いた場合、文脈によってString/Wide_String/Wide_Wide_Stringのどれかになります。
  • D言語で"%d"と書いた場合、文脈によってchar[]/wchar[]/dchar[]のどれかになります。
  • Delphiで'%d'と書いた場合、文脈によってShortString/AnsiString/WideString/UnicodeStringの(ry

……要するにこういうの(AdaではUniversal_Integer, Universal_Real, Universal_String等と用語が(だけ)あります)を維持したまま「必ずコンパイル時に評価される関数」を適用できないかと考えています。

pair!(universal_string, int) func(universal_string s)
{
  return pair!(universal_string, int)(s, s.length);
}

pair!(char[], int) s = func('あ'); //s.second = 3
pair!(wchar[], int) ws = func('あ'); //s.second = 1

具体的なアイデアはまだないです。

*1:追記:シグネチャにprivateを付ければモジュール外から書き込み不可になりますので実用上問題ないですね。camlspotterさんに教えていただきました。そういえばEiffelのフィールドもクラス外からはreadonlyでしたっけ。