DJ180
DJ180

Reputation: 19854

Scala: implicitly convert to a generic subtype

My code heavily uses Akka and untyped actors.

An example of typical logic is as follows:

val myFuture: Future[Any] = akka.pattern.AskSupport.ask(myActorRef, myMessage)

val completedLogic: Future[Unit] = myFuture.map(myFunction)

myFunction then contains a strongly typed signature as follows:

def myFunction(): (Option[MyStronglyTypedClass]) => Unit = {
    (myOption: Option[MyStronglyTypedClass]) => myOption foreach (myObject => // do some logic
}

I know that myFuture will always contain an instance of MyStronglyTypedClass or null for this particular actor. I will also know this for other actor/future/function combinations.

My problem comes when I look to create an implicit conversion from the Future[Any] to the Option[MyStronglyTypedClass] or Option[MyOtherStronglyTypedClass]

The implicit conversion will just do a null check and one other piece of logic before creating the Option

How do I go about performing this implicit conversion from Any to a subtype, or is it even possible?

Upvotes: 1

Views: 335

Answers (2)

Roland Kuhn
Roland Kuhn

Reputation: 15472

Impliciy converting from Any to something else is something that you should never do because it effectively switches off Scala's type system. Converting the type of a future in a safe fashion can be done using

myFuture.mapTo[Option[MyStronglyTypedClass]] 

Upvotes: 1

Alexey Romanov
Alexey Romanov

Reputation: 170713

You should, instead, convert to Future[Option[MyStronglyTypedClass]]:

def asMyStronglyTypedClass(x: Any): Option[MyStronglyTypedClass] = x match {
  case null => None
  case ...
}

// this will fail if myFuture fails or asMyStronglyTypedClass throws
val typedFuture = myFuture.map(asMyStronglyTypedClass)

and do what you want with this future. E.g.

typedFuture.onSuccess(myFunction)

EDIT: I missed you already have a map. In this case the issue is that you don't need to convert Future[Any] to Option, but its result (i.e. Any). You can write e.g. myFuture.map(asMyStronglyTypedClass).map(myFunction) or myFuture.map(x => myFunction(asMyStronglyTypedClass(x))). You could also make asMyStronglyTypedClass implicit and write myFuture.map(x => myFunction(x)). I still think it isn't a good idea, as it could get applied somewhere you don't expect.

If you really want to write myFuture.map(myFunction), you'll need a different implicit conversion to make the compiler understand:

implicit def contraMap(f: Option[MyStronglyTypedClass] => Unit): Any => Unit = 
  x => f(asMyStronglyTypedClass(x))

Of course, these can be made generic over your types, as mentioned in the comments.

Upvotes: 3

Related Questions