先日、op: Unit => Unit と書かざるを得ない状況に直面した。それで、この関数を op()として呼び出したときに「多分あなたが意図していることと違うことしてますよ」みたいなwarningが出て気づいたこと。ScalaのUnitは値がないことを表す型なわけだが、「実際に値がない」のではなく、「値がないことを仮想的に表す値()」を持つ型であるということ。

具体的なwarningメッセージはこちら。

Warning:(140, 9) Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want.
        signature: Function1.apply(v1: T1): R
  given arguments: <none>
 after adaptation: Function1((): Unit)
      op()
        ^

したがって、Unit => Unit は実際には一つの引数を取って一つの値を返す関数であり、引数を取らない () => Unit とは別物である。そのため、op() のように引数が空っぽの呼び出しでは整合性が取れない。

=> Unit() => Unit については理解していたものの、() => UnitUnit => Unit の違いについてきちんと理解していなかった。何もない、という意味で ()Unitは同じものだと勘違いしており、落とし穴にハマってしまった。しかも、なんというか、引数リストの( )括弧と、値としての()と、型指定の特殊表記としての() =>の意味が重なりあって不思議な読みを引き起こしていて、話をややこしくしている。名前や記号に関する整合性を重視するScalaっぽくない仕様という感じがするが、仕方ないのかな…。

以下の解説がよくわかります。 () => Unit はFunction0が定義されるのに対して、 Unit => Unit はFunction1となるため、Unit型の値(つまり())を引数として取る必要がある。op: Unit => Unit に対してはop(())op(Unit)とすると警告が解消される。

stackoverflow.com

Unitについてはこちら。

www.scala-lang.org