LoginSignup
1
0

More than 5 years have passed since last update.

`def toOption(name: String): Option[name.type]= Some(name)`を、`toOption("hoge").get`で呼び出せない

Last updated at Posted at 2018-06-23

環境

  • IntelliJ IDEA Community 2018.1
  • Scala 2.12.5
    • sbt 1.1.3
    • ScalaTest 3.0.5
build.sbt
# build.sbtの一部
scalaVersion := "2.12.5"
libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.5"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test"

やりたいこと

以下のメソッドに対応するテストコードを、ScalaTestで書きたいです。

Sample.scala
object Sample {

  def toOption(name: String): Option[name.type] = {
    println("toOption is called.")
    Some(name)
  }

  def toOption2(name: String): Option[String] = {
    println("toOption is called.")
    Some(name)
  }
}

toOptiontoOption2の違いは、戻り値の型です。
toOptionの戻り値の型は、「引数の型と同じ型パラメータを持つOption型」という意味を表すため、Option[name.type]にしています。

テストコードは、以下の通りです。

SampleTest.scala
import org.scalatest.FreeSpec
import org.scalatest.Matchers._

class SampleTest extends FreeSpec {

  "toOption Test" in {
    println("[toOption Test]")
    Sample.toOption("hoge") shouldBe Some("hoge")
    Sample.toOption("hoge").get shouldBe "hoge" // toOption IS NOT CALLED
  }

  "toOption2 Test" in {
    println("[toOption2 Test]")
    Sample.toOption2("hoge") shouldBe Some("hoge") 
    Sample.toOption2("hoge").get shouldBe "hoge"
  }
}

問題

上記のテストコードを実行すると、コンソールには以下の内容が出力されます。

[toOption Test]
toOption is called.
[toOption2 Test]
toOption2 is called.
toOption2 is called.

Sample.toOption("hoge").get shouldBe "hoge"というコードでは、** Sample.toOptionメソッドが実行されませんでした!!**

原因調査

@xuwei_k さんからコメントをいただきました。
ScalaTestは関係ありませんでした。

Scala_Console
Welcome to Scala 2.12.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_172).
Type in expressions for evaluation. Or try :help.

scala> def toOption(name: String): Option[name.type] = {
     |   println("toOption is called.")
     |   Some(name)
     | }
toOption: (name: String)Option[name.type]

scala> toOption("hoge").get
res0: String = hoge

scala> toOption("hoge")
toOption is called.
res1: Option[String("hoge")] = Some(hoge)

@xuwei_k さんのコメントから引用

javapで逆アセンブル

javapで逆アセンブルしてみます。

Sample.scala
object Sample {
  //追加
  def toString(name: String): name.type = {
    println("toString is called.")
    name
  }
}
CallTest.scala
object CallTest {
  def test = {
    Sample.toOption("aaa")
    Sample.toOption("bbb").get
    Sample.toOption2("ccc").get
    Sample.toString("ddd")
  }
}
console
$ javap -verbose CallTest$.class > CallTest$.txt
CallTest$.txt
  public java.lang.String test();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #19                 // Field jp/co/scalablescala/Sample$.MODULE$:Ljp/co/scalablescala/Sample$;
         3: ldc           #21                 // String aaa
         5: invokevirtual #25                 // Method jp/co/scalablescala/Sample$.toOption:(Ljava/lang/String;)Lscala/Option;
         8: pop
         9: ldc           #27                 // String bbb
        11: pop
        12: getstatic     #19                 // Field jp/co/scalablescala/Sample$.MODULE$:Ljp/co/scalablescala/Sample$;
        15: ldc           #29                 // String ccc
        17: invokevirtual #32                 // Method jp/co/scalablescala/Sample$.toOption2:(Ljava/lang/String;)Lscala/Option;
        20: invokevirtual #38                 // Method scala/Option.get:()Ljava/lang/Object;
        23: pop
        24: ldc           #40                 // String ddd
        26: areturn
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      27     0  this   Ljp/co/scala/invest/CallTest$;
      LineNumberTable:
        line 8: 0
        line 9: 9
        line 10: 12
        line 11: 24

Sample.toOption("bbb").get, Sample.toString("ddd")のときは、invokevirtual(インスタンスのメソッド呼び出し)が実行されていませんでした。

まとめ

関数の戻りの型が引数の型を参照しているとき、その関数は呼び出されない場合があります。

関数の定義 関数の呼び出し 関数が呼び出されるか
def hoge(arg:String) : Option[arg.type] hoge("fuga") OK
def hoge(arg:String) : Option[arg.type] hoge("fuga").get NG
def hoge(arg:String) : Option[String] hoge("fuga").get OK
def hoge(arg:String) : arg.type hoge("fuga") NG

Scalaコンパイラのバグですかね?

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0