I'm trying to create some simple custom String interpolator, and I'm successful as long as I don't try to use a type parameter.
import scala.concurrent.Future
object StringImplicits {
implicit class FailureStringContext (val sc : StringContext) extends AnyVal {
// This WORKS, but it's specific to Future :(
def fail[T](args : Any*): Future[T] = {
val orig = sc.s (args : _*)
Future.exception[T](new Exception(orig))
}
// I want this to work for Option,Try,Future!!
def fail[M,T](args:Any*): M[T] = {
val orig = sc.s (args : _*)
// Obviously does not work..
M match {
case Future => Future.exception(new Exception(orig))
case Option => None
case Try => Failure(new Exception(orig))
case _ => ???
}
}
}
}
Can I get this to work? I can't use parametric polymorphism because I'm not the one defining those three types.
What's the equivalent in the type level for that pseudo-code pattern match?
LATEST ATTEMPT
My latest attempt was to use implicitly, but I don't have such implicit! I'd be actually interested to grab a hold of the type that the compiler wants me to return according to type inference.
def fail[T, M[T]](args:Any*): M[T] = {
val orig = sc.s(args: _*)
implicitly[M[T]] match {
case _:Future[T] => Future.exception(new Exception(orig))
case _ => ???
}
}
<console>:18: error: could not find implicit value for parameter e: M[T]
implicitly[M[T]] match {
^
<console>:19: error: value exception is not a member of object scala.concurrent.Future
case _: Future[T] => Future.exception(new Exception(orig))
^
def test[A,B](a:Any):A[B] = ???=> compilation error:Adoes not take type parameters.Ais generic and can beIntfor example. Int[String] does not exist. So compiler will always complain.def fail[T, M[T]](...)gets you part way. But I don't see how you will specify the M at the call site. Are you expecting the compiler to infer what to use for M based on the return type required?Future.failedwith a much less obviousfail"...", making it polymorphic on top of that... and having the string silently ignored in case of anOption)? Or is it just for kicks? Anyway, if you feel like you need it, what is wrong with good old overloading? Defining a distinct overload for each of the types you want to support seems like the sanest solution.