如果遇到类型为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>,因此......)
我试图提出低优先级的暗示和磁铁模式,但尚未找到解决方案。
答案 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
(基于宏的版本将是静态的,我猜)