この質問と同様の質問が、前方参照などで以前に尋ねられたことに注意してください - なぜこのコードはコンパイルされるのですか? 、しかし、まだいくつかの質問を開いたままにする答えが見つかったので、この問題にもう一度取り組んでいます。
メソッドと関数内では、val
キーワードの効果は語彙的であるように見えます。
def foo {
println(bar)
val bar = 42
}
降伏
error: forward reference extends over definition of value bar
ただし、クラス内では、スコープ ルールがval
変更されているようです。
object Foo {
def foo = bar
println(bar)
val bar = 42
}
これはコンパイルされるだけでなくprintln
、コンストラクター内のも0
出力として生成されますが、インスタンスが完全に構築された後に呼び出すとfoo
、期待される値が返されます42
。
したがって、メソッドがインスタンス値を前方参照することは可能であるように思われます。これは、最終的に、メソッドが呼び出される前に初期化されます (もちろん、コンストラクターから呼び出している場合を除きます)。コンストラクター内のステートメントについても同様です。同じ方法で値を前方参照し、初期化される前に値にアクセスすると、ばかげた任意の値になります。
このことから、いくつかの疑問が生じます。
val
コンストラクタ内で字句コンパイル時の効果を使用するのはなぜですか?
コンストラクターが実際には単なるメソッドであることを考えるとval
、通常の実行時の効果のみを与えて、 のコンパイル時の効果を完全に削除するのはかなり矛盾しているようです。
- が、事実上、不変値
val
を宣言する効果を失うのはなぜですか?
異なるタイミングで値にアクセスすると、異なる結果になる可能性があります。私には、コンパイラの実装の詳細が漏れているように思えます。
- これの正当なユースケースはどのようなものでしょうか?
コンストラクター内の現在のセマンティクスを絶対に必要とし、おそらく と組み合わせてval
、適切なレキシカル で簡単に実装できない例を思いつくのに苦労しています。val
lazy
- のこの動作をどのように回避し
val
、他のメソッド内での使用に慣れているすべての保証を取り戻すでしょうか?
おそらく、すべてval
のインスタンスを宣言lazy
して、val
不変であり、アクセス方法に関係なく同じ結果が得られるようにし、通常のメソッド内で観察されるコンパイル時の影響をあまり関連性のないものにすることができますが、それはそうですこの種のことについては、私にとって非常にひどいハックのようです。
この動作が実際の言語内で変更される可能性が低いことを考えると、コンパイラ プラグインはこの問題を修正する適切な場所でしょうか、またはval
-alike キーワードを実装することは可能でしょうか?この奇妙で、言語内のより賢明なセマンティクス?