1. ホーム
  2. scala

[解決済み] と()=>、Unit=>の違いは何ですか?

2022-04-27 05:01:06

質問

引数を取らず、値を返さない関数を表現しようとしています(JavaScriptのsetTimeout関数をシミュレートしています。)

case class Scheduled(time : Int, callback :  => Unit)

は、 " `val' パラメータは call-by-name でない可能性があります" と言って、コンパイルされません。

case class Scheduled(time : Int, callback :  () => Unit)  

はコンパイルされますが、変に起動する必要があります。

Scheduled(40, { println("x") } )

私はこれをしなければならない

Scheduled(40, { () => println("x") } )      

また、次のようなものもあります。

class Scheduled(time : Int, callback :  Unit => Unit)

が、さらに意味不明な方法で呼び出されます。

 Scheduled(40, { x : Unit => println("x") } )

(Unit型の変数は何になるのか?)私の場合 欲しい もちろん、普通の関数と同じように呼び出すことができるコンストラクタが必要です。

 Scheduled(40, println("x") )

赤ちゃんに哺乳瓶をあげよう

解決方法は?

コール・バイ・ネーム:=> タイプ

=> Type という表記はコール・バイ・ネームの略であり、これは いろいろな方法 パラメータを渡すことができます。今はcall-by-valueやcall-by-referenceが主流ですが、もし馴染みがなければ、そのwikipediaの記事を時間をかけて読んでみることをお勧めします。

どういうことかというと、渡されるものは 代入 を関数内の値名として使用します。例えば、こんな関数がある。

def f(x: => Int) = x * x

このように呼び出すと

var y = 0
f { y += 1; y }

すると、コードは次のように実行されます。

{ y += 1; y } * { y += 1; y }

しかし、その場合、識別子の名前がぶつかったらどうするのか、という問題が出てきます。従来の call-by-name では、名前の衝突を避けるために capture-avoiding substitution と呼ばれるメカニズムが働きます。しかし Scala では、これは別の方法で実装されており、結果は同じです。パラメータ内の識別子名は、呼び出される関数内の識別子を参照したり、シャドウしたりすることはできません。

call-by-nameについては、他にもいくつかポイントがあるので、他の2つを説明した後にお話しします。

0-arity関数。() => タイプ

構文 () => Type の型を表します。 Function0 . つまり、パラメータを取らず、何かを返す関数です。これは、例えば、メソッド size() -- パラメータを取らず、数値を返す。

しかし、この構文が 無名関数リテラル これが混乱の原因になっている。例えば

() => println("I'm an anonymous function")

はアリティ 0 の無名関数リテラルで、その タイプ

() => Unit

だから、書けるんです。

val f: () => Unit = () => println("I'm an anonymous function")

ただし、型と値を混同しないようにすることが重要です。

単位 => タイプ

これは実際には単なる Function1 で、その最初のパラメータは Unit . 他の書き方としては、以下のようになります。 (Unit) => Type または Function1[Unit, Type] . しかし......これでは、人が望んでいるものになることはまずないでしょう。その Unit 型の主な目的は、興味のない値を示すことです。 受け取る という値です。

例えば、考えてみてください。

def f(x: Unit) = ...

で何ができるのでしょうか? x ? それは単一の値しか持つことができないので、それを受け取る必要はありません。一つの可能な使い方は Unit :

val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g

なぜなら andThen にのみ定義されます。 Function1 を返し、連鎖している関数は Unit の型であると定義する必要がありました。 Function1[Unit, Unit] を連鎖させることができます。

混乱の元となるもの

最初の混乱の原因は、0-arity関数に存在する型とリテラルの類似性が、call-by-nameにも存在すると考えることです。言い換えれば、以下のように考えてしまうことです。

() => { println("Hi!") }

はリテラルで () => Unit であれば

{ println("Hi!") }

はリテラルで => Unit . そうではありません。それは ブロックコード は、リテラルではありません。

もう一つの混乱の原因は Unit 型の が書かれています。 () これは,0-arityのパラメータリストのように見えます(しかし,そうではありません).