LoginSignup
1
1

More than 5 years have passed since last update.

REPLでコンパイルエラーと実行時のエラーを勘違いしていた

Last updated at Posted at 2015-09-02

追記 2

まさに@xuwei_kさんからコメントしていただいた通りで、2点勘違いしていました。

  • 型としてmatchする 可能性がある ことと、値が実際にmatchするのを勘違い
  • コンパイルエラーと実行時のエラーを勘違い

同じようなコードをREPLじゃなくてファイルで書いたとしても、

import scala.util.{Failure, Success, Try}

object MatchTest {
  def main(args: Array[String]) {
    val e = Try {
      Option(null)
    } match {
      case Success(Some(n)) => Right(n.toLowerCase)
      case Success(None) => Left("returned None")
      case Failure(e) => Left(e.getMessage)
    }
    println(e)
  }

  /** [値, null, 例外]という3種類の戻りがあるメソッド
    * 実際にはJavaのライブラリの呼び出し */
  def doorSecurity(name: String): String = {
    name match {
      case "Bob" => null
      case "Dave" => throw new RuntimeException("Dave is not allowed")
      case n => n
    }
  }
}

コンパイル時にエラーを出してくれます。

$ scalac MatchTest.scala
MatchTest.scala:9: error: value toLowerCase is not a member of Null
      case Success(Some(n)) => Right(n.toLowerCase)
                                       ^
one error found

Try { Option(null) }と書いたら戻りの型はTry[Option[Null]]になるというのは理解できます。
ただ、以下のように書いたら、Success(None)がmatchするのはなぜか?まだ腑に落ちていません。まだ考えが足りない。

import scala.util.{Try, Success, Failure}


object TryOptionTest {
  def main(args: Array[String]): Unit = {
    val people = List("Alice", "Bob", "Carol", "Dave")
    people map entrance foreach {
      case Right(n) => println(s"Hello $n")
      case Left(m) => println(m)
    }
  }

  def entrance(name: String): Either[String, String] = {
    Try {
      Option(doorSecurity(name))
    } match {
      case Success(Some(n)) => Right(n)
      case Success(None) => Left("do not pass null")
      case Failure(e) => Left(e.getMessage)
    }
  }

  /** [値, null, 例外]という3種類の戻りがあるメソッド
    * 実際にはJavaのライブラリの呼び出し */
  def doorSecurity(name: String): String = {
    name match {
      case "Bob" => null

  case "Dave" => throw new RuntimeException("Dave is not allowed")
      case n => n
    }
  }
}

コンパイル時には、Try{ Option(doorSecurity(name)) }の戻り値の型がTry[Option[String]]と推論されて、実行時にdoorSecuritynullを返したときには、実際にSuccess(None)になるから、という感じ、でしょうか。


旧タイトル: Try(None)からのパターンマッチがSuccess(Some(n))にマッチしてしまう?というのはなかった

追記: 以下の挙動はsbt consoleだけだった。普通にファイル作ってやれば期待通り動いた。

Scala version: 2.10.2

import scala.util.{Try, Success, Failure}

object TryOptionTest {
  def main(args: Array[String]): Unit = {
    val l1 = List(1, "abc", null)
    l1 foreach f1
  }

  def f1(v: Any): Unit = {
    Try {
      Option(v)
    } match {
      case Success(Some(n)) => println(2)
      case Success(None) => println(1)
      case Failure(e) => println(0)
    }
  }
}
$ scalac TryOptionTest.scala
$ scala TryOptionTest
2
2
1

よかったです。


例えばこんなコードを書いたときに、Success(None)にマッチするのを期待してたけど、Success(Some(n))にマッチしてしまう。

scala> Try {
     | Option(null)  // 本当はなんかOptionに包まれた値が返る処理
     | } match {
     | case Success(Some(n)) => Right(n.doSomething)
     | case Success(None) => Left("returned None")
     | case Failure(e) => Left(e.getMessage)
     | }
<console>:14: error: value getLines is not a member of Null
              case Success(Some(n)) => Right(n.doSomething)
                                               ^

入れ子にしてやれば動くかと思ったけど、そういうことでもないようだ。

scala> Try {
     | Option(null)
     | } match {
     |   case Success(n) => n match {
     |     case Some(g) => Right(g.doSomething)
     |     case None => Left("returned None")
     |     }
     |   case Failure(e) => Left(e.getMessage)
     | }
<console>:15: error: value doSomething is not a member of Null
                  case Some(g) => Right(g.doSomething)
                                          ^

なぜこんな挙動になっているのか、どうやって書くのがよいのか、どなたか教えていただけるととてもうれしいです。

ただのSuccess(n)なら期待した通りに動く。

scala> Try {
     | Option(null)
     | } match {
     | case Success(n) => n
     | case Failure(e) => e.getMessage
     | }
res31: java.io.Serializable = None
scala> Try {
     | Option(123)
     | } match {
     | case Success(n) => n
     | case Failure(e) => e.getMessage
     | }
res32: java.io.Serializable = Some(123)
1
1
3

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
1