在存在类型为Nothing的参数时,使Scala选择不太具体的重载方法

时间:2013-06-07 14:56:31

标签: scala overloading thunk bottom-type

如果遇到类型为Nothing时存在thunks与函数的有趣案例:

object Test {
  def apply(thunk: => Any     ): String => Any = _ => thunk
  def apply(fun: String => Any): String => Any = fun
}

val res0 = Test { println("hello") }
res0("foo") // --> hello

val res1 = Test { x: String => println(s"hello $x") }
res1("foo") // --> hello foo

val res2 = Test { x: String => throw new RuntimeException("ex") }
util.Try(res2("foo")) // --> Failure

val res3 = Test { throw new RuntimeException("ex") } // boom!

现在棘手的一点是最后一个案例。是否可以修改apply方法以使Scala选择它的thunk版本,而不是Function1版本(更具体,因此首选, Nothing <: Function1[_,_] < / strong>,因此......)

我试图提出低优先级的暗示和磁铁模式,但尚未找到解决方案。

3 个答案:

答案 0 :(得分:4)

以下是基于类型类的类型安全方法的快速草案:

object Test {
  trait Wrap[A, B] { def apply(a: => A): String => B }

  trait LowWrap {
    implicit def thunkWrap[A] = new Wrap[A, A] { def apply(a: => A) = _ => a }
  }

  trait MidWrap extends LowWrap {
    implicit def funcWrap[A] = new Wrap[String => A, A] {
      def apply(f: => String => A) = f
    }
  }

  object Wrap extends MidWrap {
    implicit object nothingWrap extends Wrap[Nothing, Nothing] {
      def apply(f: => Nothing) = _ => f
    }
  }

  def apply[A, B](a: => A)(implicit w: Wrap[A, B]) = w(a)
}

然后:

scala> Test { println("hello") }
res0: String => Unit = <function1>

scala> res0("foo")
hello

scala> Test { x: String => println(s"hello $x") }
res2: String => Unit = <function1>

scala> res2("foo")
hello foo

scala> Test { x: String => throw new RuntimeException("ex") }
res4: String => Nothing = <function1>

scala> util.Try(res4("foo"))
res5: scala.util.Try[Nothing] = Failure(java.lang.RuntimeException: ex)

scala> Test { throw new RuntimeException("ex") }
res6: String => Nothing = <function1>

scala> util.Try(res6("foo"))
res7: scala.util.Try[Nothing] = Failure(java.lang.RuntimeException: ex)

您可以简化一下,添加差异等等。

答案 1 :(得分:1)

使用:

val res3 = Test { (throw new RuntimeException("ex")): Any }

这可以按预期工作。 (创建时没有例外,调用res3时例外)。

答案 2 :(得分:1)

使用反射的解决方案。 (仍然好奇:可以写一个静态解决方案吗?)

import reflect.runtime.universe._

object Test {
  def apply[A: TypeTag](thunk: => A): String => Any =
    if (typeOf[A] =:= typeOf[Nothing]) // hah, caught you
      _ => thunk
    else if (typeOf[A] <:< typeOf[String => Any])
      thunk.asInstanceOf[String => Any]
    else
      _ => thunk
}

val res0 = Test { println("hello") }
res0("foo") // --> hello

val res1 = Test { x: String => println(s"hello $x") }
res1("foo") // --> hello foo

val res2 = Test { x: String => throw new RuntimeException("ex") }
util.Try(res2("foo")) // --> Failure

val res3 = Test { throw new RuntimeException("ex") }
util.Try(res3("foo")) // --> Failure

(基于宏的版本将是静态的,我猜)