2011-12-20 5 views
4

次のコードでは暗黙の変換がprintln(2)行に適用されます。私は愚かにブロック{ println(1); println(2) }全体に適用することを期待していました。コンパイラが暗黙的に配置する場所について、どうすればよいですか?ブロックを使用した暗黙的変換の場所の選択

object Executor { 

    private var runnable: Runnable = _ 

    def setRunnable(runnable: Runnable) { 
    this.runnable = runnable 
    } 

    def execute() { runnable.run() } 

} 

object Run extends App { 

    implicit def blockToRunnable(p: ⇒ Any): Runnable = 
    new Runnable { def run() = p } 

    Executor.setRunnable { 
    println(1) 
    println(2) 
    } 

    println("Before execute") 
    Executor.execute() 

} 
+1

!私はそれが_neither_ステートメントに適用され、 'println(2)'の '()'(単位)戻り値だけをラップすることを期待していました。 2行目を名前で呼ぶようにしても、私にとっては直感にはなりません。 –

答えて

3

仕様によると、暗黙的な変換が適用される。これは、この小さな例でscala -Xprint:typerで確認され

{ s1; s2; ... sn; convert(e) } 

:だから、これをしたいと思います式のタイプが予想タイプと一致しません。主要な観察は、ブロックをタイプするときに期待されるタイプがスレッド化される方法です。

表現eはタイプTのものであり、Tは、式の期待タイプptに準拠していない場合。この場合、暗黙のvが検索され、これはeに適用可能であり、結果の型はptに準拠します。第6.11ブロック

、ブロックの最後の式の予想されるタイプを

最終発現eの期待される型として定義されるブロックの予期される型です。このスペックを考えると

、それはこのように動作するを持ってコンパイラと思われます。ブロックの予想されるタイプはRunnableであり、予想タイプprintln(2)Runnableになります。

提案:どのようなインプライシットが適用されているか知りたい場合は、スカラIDE for Eclipseの夜間ビルドを使用できます。それは「含意」を強調することができます。

編集:範囲内に暗黙の名前のコールがあると驚きです。

+0

私はこのScala IDEのことを行く必要があります;-) –

+0

ここには、ハイライト暗黙のスクリーンショットのある[download](http://scala-ide.org/download/nightly.html)サイトがあります。 :) –

4

私はこのように、この動作を合理化:仕様によると、ブロック{s1; s2; ...; sn; e }の種類は、最後の式eのタイプです。

したがって、コンパイラはeを受け取り、タイプはRunnableとなります。それは失敗するので、eRunnableに変換する暗黙的な変換を検索します。

class A 
implicit def convert(a: A): String = a.toString 
def f(s: String) { println(s) } 
f{ println(1); new A } 

プリント:

private[this] val res0: Unit = $line3.$read.$iw.$iw.f({ 
    scala.this.Predef.println(1); 
    $line2.$read.$iw.$iw.convert(new $line1.$read.$iw.$iw.A()) 
}); 
+0

そうですね、私はそれが何をしているのかを理解しています...しかし、コンパイラがどのように{s1; s2; ... e}ブロックを使用します。 –

0

問題は、ブロックをあたかもあたかもあたかもコードのように思っていることです。彼らはそうではありません。 { a; b; c }ではありません。は渡すことのできるコードです。

だから、どうやって含意について考えなければなりませんか?実際には、暗黙の変換であるビューについて、どのように推論すればよいでしょうか。ビューは、変更が必要な値に適用されます。あなたの例では、値

{ 
    println(1) 
    println(2) 
} 

setRunnableに渡されています。ブロックの値は最後の式の値なので、println(2)の結果をsetRunnableに渡しています。それはUnitであり、setRunnableRunnableを必要とするため、暗黙的に検索されて見つかったので、println(2)が大雑把に間違った名前のblockToRunnableに渡されます。

ボトムラインであり、これは私がスタックオーバーフローで、すでに何回も与えたアドバイスです(多くの人が同じことをやろう)あなたの頭の中で次のことを得ることです:そこ

THERE ARE NO BLOCKS IN SCALA. 

関数であるがブロックではない。

技術的には、このステートメントは間違っています - Scalaにはブロックがありますが、それらはあなたが思っているものではありません。スカラのどのブロックが後者であるかは、きれいなスレートから知ることができます。そうでなければ、あなたは彼らがそうでない方法で働くように努めることに縛られている、あるいは彼らが異なったやり方で働いていることが一定の方法で働くと推測する。

+0

私はあなたが私が何か考えないと思うと思います。 ;-) Scalaは構文上の柔軟性を提供するので、コードのブロックを実際に渡すことができるコードのように扱うことができます。これは、名前付きパラメータの大きな利点の1つです。たとえば、質問のように構文でうまくいく "setRunnable(p:=> Any)"メソッドを簡単に書くことができます。私はちょうど驚くべきケースを打った後にコンパイラが暗黙のうちに配置することを選択する仕組みに興味があります。 –

+0

@MattRああ、そうだよ!名前パラメータは、「コードブロック」とは全く関係ありません。ところで、私が考えていたことの1つは、あなたがそうでない方法で仕事を推論するかもしれないと言ったときです。 –

+0

"問題は、ブロックをサンクスのように考えていることです"。 Thunksは、名前による呼び出しの評価(http://en.wikipedia.org/wiki/Thunk_(functional_programming)#Call_by_name)について考える正しい方法です。 –

0

first scala puzzleに記載されている説明が大好きです。言い換えれば

、何の出力になります:奇妙な

List(1, 2).map { i => println("Hi"); i + 1 } 
List(1, 2).map { println("Hi"); _ + 1 }