28-chainedpkg-a

チェインされたパッケージ節 (Chained Package Clauses)

Martin_Odersky
2010 年 9 月 7 日

もしあなたが既にいくつかの大規模な Scala 2.8 プログラムを見たことがあるなら、ソースファイルが時々、次のような複数のパッケージ節の列から始まるのを見て当惑したかもしれません。:

   package org.myproject
   package tests
   ...

この意味は何でしょうか? 実はこれは、次のような 2 段にネストしたパッケージ節を書くこととまったく同じです。:

   package org.myproject {
     package tests {
       ...
     }
   }

ネストしたパッケージ節は全く正しいのですが、それほど頻繁には Scala で使われません。記法が単一のあるいはチェインされたパッケージ節よりもずっとヘビーだからです。その利点は、パッケージのネスト構造を明示するということです。しかし Scala 2.8 以前は、たいていのプログラマーは、次のような単一のパッケージ節を選んでいました。:

   package org.myproject.tests
   ...


==========================================

変わったこと (What Has Changed)


以前の単一のパッケージ節

   package org.myproject.tests
   ...

は、次の 2 段にネストする節に等価でした。

   package org.myproject {
     package tests {
       ...
     }
   }

そして両方とも、次の 3 段にネストする節に等価でした。

   package org {
     package myproject {
       package tests {
         ...
       }
     }
   }

それらはすべて同じ意味でした。 Scala 2.8 で変わりました。

違いは、パッケージ節

   package org.myproject.tests

は、今度は、スコープに org.myproject.tests のメンバーだけを入れ、外側の 2 つのパッケージ org.myproject、org のメンバーを入れないことです。もしスコープに test と org 両方のメンバーを入れたいなら、上記のように 2 つのネストしたパッケージ節か、あるいはチェインされた等価物を使う必要があります。

   package org.myproject // myproject メンバーは可視だが、org members は不可視
   package tests         // test メンバーは可視

==========================================

なぜ変えたか? (Why the Change?)


Scala と Java では、パッケージの意味はいくつかの点で異なります。Java には 4 つの異なる名前解決メカニズムがあります。:メソッド、フィールド、クラスとパッケージのそれぞれにあります。パッケージについては、パッケージ名の解決を常にルートから始めます。ですから、パッケージ名は常に絶対指定であり、決して相対ではありません。Scala は、よりシンプルで規則的です。コンパイラが検索する名前の種類に関係なく、単純名の解決にはただ 1 つの方法しかありません。

When it encounters a simple_name x, the compiler will look from the outwards until it finds a declaration of x and that is the declaration that's chosen.
コンパイラは単純名 x に遭遇すると、x の宣言を外側にあるものから検索し、あればその宣言が選ばれます。

これはブロック構造スコープの普通の考え方です。その結果、ブロック中の宣言は何らかのブロック中の宣言を隠します。

他のすべてのエンティティと同様、Scala ではパッケージをネストできます。これにより、しばしばより簡潔なコードが書けます。例えば、パッケージ org.myproject.tests 内でアクセスしたい、もう 1 つのパッケージ org.myproject.web があるとします。それは、次のように書くだけです。

   import web._ 

パス全体をインポートする必要はありません。

   import org.myprojec.web._ 

パッケージ名が長くなるにつれ、このスタイルで乱雑度をかなり減らせます。ですから、このネスト規約は役に立ちます。また、Java のようなパッケージ種に応じた特定の規則が必要ないので、非常に明解でもあります。

残念なことに実際には、ネスト規約が歓迎されない副作用を持つことが分かります。人は時々、ルートパッケージと同じ名前のネストしたパッケージに遭遇します。例えば、彼はクラスパス上のどこかでパッケージ org.java を持っているかもしれません。バージョン 2.7 までの Scala オリジナルのネスト規約では、パッケージ org.myproject 内部では、名前 java はルートパッケージ java ではなく、org.java へ解決されると規定していました。これは、org ドメインが、アクセスしたい(ルートの java を隠す) Java パッケージを含むことを知りさえしないユーザーの間に混乱と驚きをもたらしました。さらに悪いことに、ネストした java の利用可能性は、クラスパスの定義の仕方、何の jars か(what jars were on it)、その他に依存します。クラスパス上に何があるかなど、いつもわかっている人がいるでしょうか?

もちろん、逃げ道はありました。Scala には、他のすべてのトップレベルのパッケージを含む「ルート」パッケージに対する名前があります。それは _root_ と名付けられています。ですから、標準的な Java パッケージを _root_.java として参照できます。しかし、防御的であるために、_root_ を用いるすべてのトップレベルパッケージへの参照に修飾名を付けて、ネストしたパッケージに似た名前によって偶然隠されないことを確実にすべきです。

手短に言うと、パッケージ節を体系化する最も簡単で最も直接的な方法は脆弱である、という状況に直面しました。偶然クラスパスになるネストしたパッケージがトップレベルのパッケージを隠すことがあるからです。問題を避けるためのより防御的な手法は、いくぶん冗長で見苦しいものでした。

==========================================


変更のまとめ (Change Summary)


問題の解決は、次のような修飾されたパッケージ節の意味を変えることでした。

   package org.myproject.tests

これは、今は、スコープに tests のメンバーだけを入れ、それを含む外側の 2 つのパッケージのメンバーを入れません。これは Scala パッケージのネスト方法を何も変えません。パッケージ test は依然、パッケージ org.myproject のメンバーです。唯一変わったのは、上のような修飾されたパッケージ節によって見えるスコープ範囲です。

変更の第 2 は、ネストしたパッケージに代わる、よりコンパクトな表現としてチェインされたパッケージ節という新しい構文を使えることです。もしコード中でorg.myproject と org.myproject.tests の両方のメンバーにアクセスしたいなら、2 つのパッケージ節を使うことができます。:

   package org.myproject
   package tests

一般に、もしプロジェクトが複数のサブパッケージから成るなら、最初にプロジェクト全体の名前がついたパッケージ節を使い、続いて 2 番目には、現在のサブパッケージの名前がついたパッケージ節を使うと良いでしょう。このようにすれば、プロジェクトを指すしばしば長くなりがちな前置子を使わずに、プロジェクトの他のサブパッケージを参照できます。

==========================================


新スキームへの移行 (Migrating to the New Scheme)


Because of the change, previous Scala code that is spread out over multiple subpackages of a common base package will most likely no longer compile correctly.
この変更によって、共通の基本パッケージを複数のサブパッケージにわたって広げた以前の Scala コードは、十中八九正しくコンパイルされないでしょう。

同じプロジェクト中の異なるサブパッケージへの参照は正しく拾い上げられません(いつも絶対パスを使わなかったなら、問題はありません)。

幸いなことに修正は簡単です。それぞれ主要なパッケージ節を、2 つあるいは 1 つの、そのプロジェクト名のついたパッケージ節と現在のサブパッケージで置き換えるだけです。これは複数ファイルの正規表現検索と置換で簡単にできます。

==========================================


慣習の力 (The Force of Convention)


ついでに言えば、古い Scala 2.7 規則は、C# の規則や他の .NET 言語と同じであることが分かります。ではなぜ、.NET 上で明らかに動作することが JVM 上でこのような問題を起こすのか?それは期待(expectations)と慣習(conventions)の問題です。Scala パッケージと似たネストした名前空間をもつ .Net では、名前空間 org.System を定義すると、よく知られているトップレベルの System 名前空間が隠れてしまいますから、正気なら誰もそんなことはしません。JVM 上では人はその類のことをし、そして Java の絶対指定のパッケージ名規約のおかげで、うまく動作します。ですから、この経験は、設計はしばしば技術的な基準のみで正誤を判断できず、既にある慣習とそのユーザーの期待にいかに沿うかが問題となることを示しています。Scala 2.7 のネストしたパッケージは .NET 上でも動作する単純な設計です。そこでの慣習が異なっていたため、JVM 上ではそれほどうまく機能しませんでした。2.8 では、パッケージ節の解釈に新しい展開を与えて問題を解消しました。

==========================================

バージョン番号 (Version Numbers)


実は、厳密に言えば、パッケージ節の変更は言語の主版数を上げることを我々に強います。Scala の非公式の版数管理方式は次の通りです。

1. 言語中の後方非互換な変更は、2005 年 3 月の Scala1 から Scala2 へのように、新しい主版数を要求します。もし Scala を事実上異なる言語に変えるような十分な追加があるなら、主版数も変えなくてはなりません。ですから scala3 は scala2 と大きく異なることを意味すべきです。

2. 新しい言語フィーチャーとライブラリ中の後方非互換な変更は、Scala 2.7 から 2.8 へのように、ポイント番号の増加を要求します。ライブラリの後方非互換な変更は -deprecation 警告でゆっくりと段階的に導入するか、あるいは、それができなければ -Xmigration 警告を伴うべきです。

3. 後方互換なライブラリの追加とバグ・フィックスは、たとえば Scala 2.8.0 から 2.8.1 のように、3 番目の版数番号の変更を要求するだけです。これまでのところ、この版数管理方式に単純に従おうとしてきました。;これはいかなる公式のものでもなく、また、いつもこのようになることは前提とされるべきではありません。パッケージ節の意味変更は後方互換ではなく、したがって主要なバージョンが 3.0 にジャンプすることを要求します。しかし、変更は、2.8 に向けての準備段階遅くにもち上がり、ユーザーからの緊急のリクエストによって引き起こされたものでした。その時我々は、次の 3 つの可能性から選択しなければなりませんでした。:1 つめの選択肢は 2.7 から 3.0 へ直接行くことでした。しかし、すでに Scala 2.8 に関する本が出版されていました。それらの本の読者に不必要な困惑を与えるでしょう。そのうえ、人々がそれについて多くの話をしたにもかかわらず、日の目を見なかった幻の言語バージョンにするのはよくありません。2 つめの選択肢は、 2.8 の後までパッケージ節の変更を延ばすことでした。変更は既存コードの修正を要求とするので、これは全くよくありませんでした。変更する必要があるなら、早ければ早いほどよいのです。長く待てば、それだけ修正すべきコードが多くなります。3 つめの選択肢は、命番方式に例外を設けることで、我々が最終的に選んだものです。

==========================================


謝辞 (Acknowledgements)


修飾されたパッケージ節の意味変更は、Jorge Ortiz によって繰り返し提案されました。私は彼の忍耐力に感謝します。新しいチェインされたパッケージ節構文は、当初 David MacIver によって提案されました。

==========================================

目次 (Contents)


==========================================

ナビゲーション (Navigation)

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2011年04月07日 09:09
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。