3

如果在 type 存在的情况下遇到一个有趣的情况,即 thunk 与函数Nothing

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[_,_],因此......)

我试图提出低优先级的隐含和磁铁模式,但还没有找到解决方案。

4

3 回答 3

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)

您也许可以简化一点,添加差异等。

于 2013-06-07T16:04:55.753 回答
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

(我猜这个基于宏的版本是静态的)

于 2013-06-07T15:37:38.150 回答
1

利用:

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

这按预期工作。(创建时无异常,调用时异常res3)。

于 2013-06-07T15:10:28.857 回答