Reputation: 1512
I have a couple of methods on a trait as so:
trait ResourceFactory[+R] {
def using[T](work: R => T): T
def usingAsync[T](work: R => Future[T]): Future[T]
}
Unfortunately there's nothing in the type checker to stop you calling the first using
method with a function returning a Future
. I'd like the compiler to insist that the type of T
in the first method be anything other than Future
, to prevent that mistake - is that possible?
Thanks
Upvotes: 1
Views: 261
Reputation: 1512
Here's an alternative I've stolen shamelessly from https://github.com/japgolly/scalajs-react/blob/cb75721e3bbd0033ad63d380bcaddc96fbe906e3/core/src/main/scala/japgolly/scalajs/react/Callback.scala#L21-L31:
@implicitNotFound("You're returning a ${A}, which is asynchronous, which means the resource may be closed before you try and use it. Instead use usingAsync.")
final class NotFuture[A] private[ResourceFactoryTests]()
object NotFuture {
final class Proof[A] private[ResourceFactory]()
object Proof {
implicit def preventFuture1[A]: Proof[Future[A]] = ???
implicit def preventFuture2[A]: Proof[Future[A]] = ???
@inline implicit def allowAnythingElse[A]: Proof[A] = null
}
@inline implicit def apply[A: Proof]: NotFuture[A] = null
}
which can be used as:
trait ResourceFactory[+R] {
def using[T: ResourceGuard](work: R => T): T
def usingAsync[T](work: R => Future[T]): Future[T]
}
This has the advantage of not having to add the implicit arg every time you implement the method, but the disadvantage that I'm pure cargo culting - I don't understand why it works, though it seems to use similar principles to shapeless.
Upvotes: 1
Reputation: 24812
You can use shapeless' <:!<
:
import scala.concurrent.Future
import shapeless._
trait ResourceFactory[+R] {
def using[T](work: R => T)(implicit ev: T <:!< Future[_]): T = ???
def usingAsync[T](work: R => Future[T]): Future[T] = ???
}
Then:
scala> val r = new ResourceFactory[Int] {}
r: ResourceFactory[Int] = $anon$1@effe6ad
// Compiles (the error is due to the use of ???)
scala> r.using(_.toString)
scala.NotImplementedError: an implementation is missing
// Doesn't compile
scala> r.using(Future.successful(_))
<console>:17: error: ambiguous implicit values:
both method nsubAmbig1 in package shapeless of type [A, B >: A]=> shapeless.<:!<[A,B]
and method nsubAmbig2 in package shapeless of type [A, B >: A]=> shapeless.<:!<[A,B]
match expected type shapeless.<:!<[scala.concurrent.Future[Int],scala.concurrent.Future[_]]
r.using(Future.successful(_))
Upvotes: 3