2013-07-19 16 views
25

匿名関数の明示的なreturnステートメント(returnキーワードを使用する)は、匿名関数自体からではなく、囲んでいる名前付き関数から返されるのはなぜですか?匿名関数のスカラreturnステートメント

など。型エラーで次のプログラムの結果:

def foo: String = { 
    ((x: Integer) => return x) 
    "foo" 
} 

私はreturnキーワードを避けることをお勧めしますけど、私は明示的および暗黙のreturn文は、無名関数で別の意味を持っている理由に興味があります。

次の例では、mの実行が終了した後にreturnステートメントが "存続"し、プログラムの実行時に例外が発生します。匿名関数が囲み関数から返されなかった場合、そのコードをコンパイルすることはできません。

def main(args: Array[String]) { 
    m(3) 
} 

def m: (Integer => Unit) = 
    (x: Integer) => return (y: Integer) => 2 

答えて

16

正式に言えば、リターンは常にだから、ラムダで異なる意味を持っていない最も内側という名前のメソッド

A return expression return e must occur inside the body of some enclosing named method or function. The innermost enclosing named method or function in a source program, f , must have an explicitly declared result type, and the type of e must conform to it. The return expression evaluates the expression e and returns its value as the result of f . The evaluation of any statements or expressions following the return expression is omitted.

から戻ると定義されます。しわは、通常のメソッドとは異なり、ラムダから作成されたクロージャは、囲むメソッドへの呼び出しをエスケープすることができ、そのようなクロージャで戻り値があれば例外を得ることができます。

If the return expression is itself part of an anonymous function, it is possible that the enclosing instance of f has already returned before the return expression is executed. In that case, the thrown scala.runtime.NonLocalReturnException will not be caught, and will propagate up the call stack.

ここで、「理由」について説明します。ラムダは式であり、式とそのすべての部分式がどのような入れ子構造であっても同じ意味を持っているといいです。 Neal Gafterがこれについて語っています。http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

命令型プログラミングでよく使われる制御フローの形を簡単にシミュレートできますが、それでも高次関数に抽象化することができます。おもちゃの例として、Javaのforeach構造体(for (x : xs) { yada; })はループ内でのリターンを可能にします。 Scalaにはforeachという言語レベルはありません。代わりに、foreachをライブラリに入れます(foreachにちょうどdesugarであるので、 "表現のために"収穫なしで数えません)。非ローカルリターンを持つことは、Java foreachを使用して、直接Scala foreachに変換できることを意味します。

私の頭の上にあるRuby、Smalltalk、Common Lispも同様の「非ローカル」戻り値を持っています。

+0

セマンティクスの違いが必要な理由についてもっと深刻な例がありますか?あなたがリストしたものは、述語パラメータを持つ変更された 'foreach'で簡単にエミュレートできます。 – corazza

3

returnキーワードは(クラス)メソッド用に予約されていますが、関数では使用できません。あなたは簡単にそれをテストすることができます。

object Foo { 
    val bar = (i: Int) => return i + i 
} 

これは

<console>:42: error: return outside method definition 
     object Foo { val bar = (i: Int) => return i + i } 
             ^

ほとんどあなたが原因メソッドを呼び出すように構文的に振る舞う機能のapply方法で、同じような方法や機能を扱うことができ、いわゆるを与えますメソッドを関数の引数として渡すことができるようにします。

この場合、違いがあります。方法として定義するとき、それが合法である:要するに

object Foo { 
    def bar(i: Int): Int = return i + i 
} 

、あなただけの条件(初期の)リターンを許可する方法でreturnを使用する必要があります。 メソッド対機能の説明についてはthis postを参照してください。

+0

この回答は、実際にその理由、つまりその決定の背後にある言語設計の理由には答えていません。 – corazza

+0

@jczこれは非常にあいまいで直感的になりません。 lambdasを考えてください。 'xs.foreach {x => if(x == y)return x}'などです。 –