特徴


minilang のユーザ向けの機能について。

Rubyとの互換性

実装済み

この辺りは, だいたい互換にできたように思います。

  • ... end で終わる構文
  • 変数に型はない
  • 演算子の優先順位は変更できない.
  • クラスベースのオブジェクト指向機能
  • 多重継承のサブセットとしての, モジュールによるmix-in. (内部では普通に多重継承です。)
  • 特異クラス (singleton class) -- 特定のオブジェクトのみに適用されるクラス. ただし, class << obj 記法は不可。特異メソッド記法 (def foo.test, EQL specializer form) のみサポート。
  • メソッド/演算子オーバーライド.
  • メソッドのオーバーロードはできない
  • 例外機構
  • λ (ラムダ) 式とクロージャ
  • ローカル変数とメソッドの名前空間は別. (Common Lisp的. Lisp-2)

不足している機能

Ruby にあって, minilang v0.7で不足している機能のうち主なものは,

  • ヒアドキュメント 実装した。まだ完全ではない (行頭の空白が余計).
  • =begin ... =end コメント 実装した。
  • % 表記 -- %{, %Q{ (いずれも文字列) だけ実装した。

不足しているライブラリ

  • ほとんどすべて!

v0.7は, まったく実用品のレベルには達していません。

開発に協力してくれる方が必要です! 開発に参加 を見てください。

省略 omit する機能

何から何まで Ruby と同じにするわけではありません。意図して実装していないものもあります。

機能選定には Crystal が参考になります。Crystal は better Ruby として優れています。[Crystal for Rubyists - Crystal] とはいえ Crystal は Ruby のサブセットではないし、スーパセットでもない。両方を通せるスクリプトがほぼ書けないので、それとも違うようにします。

キーワード

  • and -- && を使え
  • or -- ||
  • not -- !
  • defined?
  • redo 実行中の関数/クロージャの実行部の先頭に跳ぶ。仮引数は再評価しない。利用されているのを見たことがない。
  • undef

大域 (グローバル) 変数, クラス変数

定数を使えば同じようなことができます。クラス変数は実装した。

TODO: 特殊変数 $: (= $LOAD_PATH) だけは必要。

(フルの) 継続 continuation

継続がない, というよりは, call-with-current-continuation (call/cc) がダメ。一級継続は Scheme っぽい。Smalltalk も一級継続を持つのでそっちか。

機能検討 - 継続の除去 retry も不可.

Oracle の Ruby 実装である TruffleRuby も, 継続を削除しています。

セーフモード

内と外との境界線が, 昔のCGIスクリプトを書いていた頃ははよかったのですが, フレームワークやDBMSを駆使する現在の使い方にマッチしていないように思います。

証明書を要求する方式が妥当なように思います。

CRuby でも, v2.1 で $SAFE = 4 が廃止, v2.3 で SAFE レベル 2 と 3 が廃止された。SAFEレベル 4 は、安全な sandbox 環境のために利用できると考えられたが、実際には脆弱で、依拠できなかった。

ついに, CRuby v3.0.0 で $SAFE はただのグローバル変数になった。SAFE モードという考え方がなくなった。

eval()

文字列を評価する、というのがエラー避けの観点からまずい。マクロはほしい。

instance_eval / class_eval (module_eval() の別名)

eval() の難点に加えて, self を差し替える, というのは, 容易にプログラミングを誤らせやすいと思います。

とはいえ, 動的なメソッド定義, ゴーストメソッド BasicObject#method_missing, Module#define_method, 付随して Object#respond_to?, Object#respond_to_missing? が Ruby のキモになっている。DRY 原則の基礎。

動的ディスパッチ send() も多用される。

例えば、activerecord は, 次のコードで、メソッドを生やすようになっている。文字列を eval している。これが ruby 流。

Ruby
[RAW]
  1. class GeneratedRelationMethods < Module # :nodoc:
  2. include Mutex_m
  3. def generate_method(method)
  4. synchronize do
  5. return if method_defined?(method)
  6. if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
  7. module_eval <<-RUBY, __FILE__, __LINE__ + 1
  8. def #{method}(...)
  9. scoping { klass.#{method}(...) }
  10. end
  11. RUBY
  12. else
  13. define_method(method) do |*args, &block|
  14. scoping { klass.public_send(method, *args, &block) }
  15. end
  16. ruby2_keywords(method)
  17. end
  18. end
  19. end
  20. end
  21. private_constant :GeneratedRelationMethods

まず, method_missing() は, 非常に発見しにくいバグの温床なので、よくない。これと, 同時に利用される respond_to_missing?() は削除でよい。

Crystal は別のやり方を採る: Metaprogramming Help - Crystal

国際化 Internationalization (i18n)

Ruby 1.9 は CSI (Character Set Independence) と主張していますが, 誤って文字集合ではなく文字コードを用いており, とてもその水準ではありません。2013年の今, たとえ本物の CSI であっても, あまりいい考えとはいえません。

文字列はUnicodeの並び

See 機能検討 - 国際化

時刻とタイムゾーン

見掛けの時刻と真の時刻を区別する必要があります。例えば 2月1日9:00 JSTは 2月1日 0:00 GMTと同じ時刻です。

見掛けの時刻を扱うのか, 真の時刻を扱うのか, APIを整理する必要があります。

カレンダー

グレゴリオ歴ではない暦を扱えるように。

ネイティブスレッド

Giant lock はありません。開発者 (minilang のユーザ) がスレッドの同期をしなければなりません。

minilang v0.7 では, まだまだ実用には達していません。

その他

[]{} も偽 ヤメ. 今さら変えられない

Common Lisp では空リスト ()nil と同値. 他方, Scheme では空リストは真.

GNU Smalltalk では true のみが真, 例えば数値の 1 への #ifTrue: メッセージは error: "did not understand #ifTrue:". nil も同様のエラーで, 偽でもない。

Ruby の [] は真で、Scheme っぽい。Ruby は何由来?

Container のサブクラスで, empty?() が真の場合は, 偽にします。 これはヤメ。

ただし, "" と 0 は真のまま (今さら変えれない.)

[]{} も今さら変えられない。Common Lisp でも (vector)(make-array 0) 配列は nil と同じではない。

変数に型がある (型を付けてもよい), 型推論

まだ計画段階. [[未実装]]

機能検討 - 型推論・契約プログラミング を見てください。

Crystal は型を必須にしたが、それでは両方で動かせるスクリプトを書くことができず, Ruby から徐々に移行することができない。

初期化していないインスタンス変数はエラー, ローカル変数も

初期化していないインスタンス変数は nil ではなくエラーになります。initialize() 内でインスタンス変数を初期化し, 原則として super を呼び出してください。

Module#attr_accessor (なぜモジュールクラス?), #attr_reader, #attr_writer で宣言されたものは、エラーにしなくてよい。宣言と定義が分かれていないのでややこしいが、未宣言のものをエラーにする。

未初期化のローカル変数の使用もエラー。

定数の再代入もエラー

定数への再代入もエラーになります。