Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making Scala choose less specific overloaded method in presence of argument of type Nothing

If come across an interesting case with thunks versus functions in the presence of type 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!

Now the tricky bit is the last case. Is it possible to modify the apply method to make Scala choose the thunk version of it, instead of the Function1 version (which is more specific and thus preferred, and Nothing <: Function1[_,_], therefore...)

I tried to come up with low-high priority implicits and magnet pattern, but haven't found a solution yet.

like image 361
0__ Avatar asked Dec 14 '25 05:12

0__


2 Answers

Here's a quick draft of a type-safe approach based on type classes:

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)
}

And then:

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)

You might be able to simplify a bit, add variance, etc.

like image 55
Travis Brown Avatar answered Dec 15 '25 22:12

Travis Brown


Use:

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

This works as expected. (No exception on creation, exception when calling res3).

like image 20
gzm0 Avatar answered Dec 15 '25 23:12

gzm0



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!