効果(Effects)¶
Onion は、呼び出しがプログラムの外の世界に何をしうるかを追跡します。すべてのメソッドは
効果集合 を持ち、それは何も実行せずに計算されます。--effects で表示できます:
$ onion --effects report.on
Report#readCfg(java.lang.String): read
Report#main(java.lang.String[]): console,read
このページは、語彙・事実の出どころ・解析が約束すること/しないことのリファレンスです。
この上に載る capability 層(tool 宣言、requires、--plan)はガイド側で解説します。
このデータ層が先に出荷されました。
語彙¶
効果は「外の世界への触れ方」に一つずつ名前を付けたものです。呼び出しはその集合を持ち、
空集合は pure と表示されます。
read はファイルシステムの読み取り、write は書き込みです。Files::copy のように両方を
行う操作は両方を持ちます。net は方向を問わないネットワーク通信です。exec はプロセスの
起動・置換・終了を指し、だから Proc::run だけでなく System::exit もこれを持ちます。
env はプロセス環境の読み取り(環境変数、システムプロパティ、作業ディレクトリ、JVM 統計)
です。clock は時計を読むことと時計に対してブロックすることの両方を含むので、
DateTime::now と Timing::sleep が同じ効果を共有します。rand は乱数源からの取得、
console は接続された stdin / stdout / stderr の使用です。
9 つ目の unknown だけは種類が違います。これは表がこの呼び出しを保証できないことを
意味します。表に載っていない Java メソッドは「純粋だと分かっている」わけではなく、そう
装えばこの語彙の上に築かれる保証すべてが嘘になります。だから unknown は他の効果と同じ
ように伝播する効果であり、capability 層はこれを「明示的な許可が要る」と扱います。
「無害」とは決して扱いません。
事実の出どころ¶
このコンパイラでは効果をアノテーションに載せられません — Java メソッドのアノテーションは
型検査器に届かず、バイトコードにも出力されず、最適化器はメソッドを再構築するときに捨てて
しまいます。だから事実は帯域外、コンパイラに同梱されるリソース(onion/effect-table.txt)
にあります。各行が 1 メソッド(またはワイルドカードでクラス全体)を分類し、個別エントリは
クラスのワイルドカードに勝ちます。ほぼ純粋なクラスが例外だけ申告する書き方です:
onion.DateTime#*=pure
onion.DateTime#now=clock
onion.Files#copy=read,write
onion.Cli#parse=console,exec
表には標準ライブラリの効果を持つ全表面 — Files、FileResource、Proc、Http、
HttpResource、Config、IO、Cli、DateTime、Timing、Rand、OnionMath — の
分類に加えて、純粋な残りの stdlib、よく使う JDK の値型・コレクション型の純粋ベースライン、
JDK の既知の効果点(System::getenv、System::exit、Runtime::exec、Thread::sleep、
System::out の型としての PrintStream)が入っています。
粒度は型単位であり、インスタンス単位ではありません。ファイルの上の PrintStream は本来
write ですが、Onion コードで PrintStream といえば System::out / System::err なので、
この型は console に分類されています。この区別が重要な場面では、stdlib 自身の入り口
(Files、IO)が正確な経路です。
メソッドの集合はどう計算されるか¶
プログラム内で定義されたメソッドについては、コンパイラが型付きのボディを走査し、中の
すべての呼び出しについて和集合を取ります。外部呼び出しは表の判定を、プログラム内の他の
メソッドへの呼び出しはそのメソッド自身の推論結果を、推移的に合流させます。再帰・相互再帰は
固定点で収束します。コンストラクタも同じように参加します — new Loud() は Loud の
コンストラクタが行うこと(スーパークラス連鎖を含む)を運びます。
2 つの規則は意図的な過大近似で、最大の精度よりも境界検査に対する健全性を選んでいます。
クロージャのボディは、それを最終的に呼び出すメソッドではなく生成するメソッドに課金
されます(関数値の呼び出し自体は表で pure)。生成箇所こそ検査器が常に見える場所だから
です。そして表もコンパイル対象ソースも説明できない呼び出しは、無言で落とされるのではなく
unknown を寄与します。
結果はもう一方の方向にも過大近似です。解析が報告するのはボディがなしうることであり、 ある実行で実際にすることではありません。一度も実行されない分岐も効果を寄与します。 これはこの語彙が答えるべき問い —「これを実行するとき、私は何を許可するのか」— に対して 正しい方向です。効果の見落としは穴になりますが、余分に載るのは単に保守的なだけです。
約束しないこと¶
リフレクション、ClassLoader の細工、ネイティブコードはモデルの外です。それらに到達する
呼び出しの箇所で unknown として現れ、その先を透視することはありません。表は出荷された
stdlib を分類したものであり、onion.Files という名前のユーザー提供クラスをソースと突き
合わせることはしません。そして効果集合はセキュリティサンドボックスではありません。何も
プログラムの実行を止めません。集合は「実行してよいか」を決める上位の層への、正直な入力
です。