Matching against Value Classes in Akka - scala

I've created the Value Class
final class Feature(val value: Vector[Double]) extends AnyVal
To match against that class in scala:
val d = new Feature(Vector(1.1))
s match {
case a:Feature => println(s"a:feature, $a")
case _ => println("_")
}
This works correctly, but in Akka, with the same class above, in the receive method this is not working:
def receive = LoggingReceive {
case a:Feature =>
log.info("Got Feature: {}", a)
case _ => println("_")
}
When I execute the code, although I am sending a Feature, the case statement that is being executed is case _ => println("_"), but, If I change the code to this:
def receive = LoggingReceive {
case a:Feature =>
log.info("Got Feature: {}", a)
case b:Vector[_] =>
log.info("GOT FEATURE")
case _ => println("_")
}
case b:Vector[_] is executed.
Akka documentation mentions:
The recommended way to instantiate actor props uses reflection at runtime to determine the correct actor construc-
tor to be invoked and due to technical limitations is not supported when said constructor takes arguments that are
value classes. In these cases you should either unpack the arguments or create the props by calling the constructor
manually:
But do not mention nothing about matching against Value classes
Update
Thanks to YuvalItzchakov for the help. The Actor's code is as below:
Actor that receive the message:
def receive = LoggingReceive {
case Feature(a) =>
log.info("Got feature {}", a)
// ....
}
Actor sending the message:
def receive = LoggingReceive {
// ..
case json: JValue =>
log.info("Getting json response, computing features...")
val features = Feature(FeatureExtractor.getFeatures(json))
log.debug(s"Features: $features")
featureListener.get ! features
// ..
}

Due to the nature of how value classes work, both your examples will cause the allocation of Feature. Once due to run-time checking in your pattern matching example, and the other due to the signature of receive which requires Any as the input type.
As the docs for Value Classes specify (emphasis mine):
Allocation Summary
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
This means that if you're seeing a Vector[_] type, that means your actually passing a concrete vector from some place in the code.

Related

Get type from string termname

Basically I want to read untyped JSON into a type that's specified by a string. Pseudocode.
def getObject(json: Json, typeString: String): typeOf(typeString) = extract[typeOf(typeString)](json)
typeOf is just some random thing that gives a type from a string.
I would say that it is impossible without runtime reflection. With runtime reflection I would try the following:
get the Class[_] by its name from ClassLoader - it would only work if you specified full name (with packages and everything) in String,
then use something like Jackson which uses runtime reflection to (de)serialize things new ObjectMapper().readValue(json, obtainedClass),
obviously return type would be Any - theoretically you could use path-dependent types here, but personally I see little benefit.
However:
it would be pretty fragile - any mismatch between JSON and class and it fails,
you would have to pass full name of a class (again, fragile) - if you passed data from outside, that sounds like a potential security issue, if you passing data internally... why not using type class?
if you need it for persistence... then you could persist it together with a discriminator, which you could use to provide right type class. (After all set of classes that you serialize is finite and could easily be traced). Then type safe approach with e.g. Circe would be possible.
Well I guess you could also change signature into:
def getObject[T: ClassTag](json: Json): T
or
def getObject[T](json: Json, clazz: Class[T]): T
and be sure that function return what you want. Getting Class[_] by its name and passing it reduces us to original solution.
EDIT:
To show an example of how to extract type from discriminator (pseudocode):
// store discriminator in DB
// use it to deserialize and dispatch with predefined function
def deserializeAndHandle(discriminator: String, json: String): Unit = discriminator match {
case "my.package.A" => decode[my.package.A](json).map(handlerForA)
case "my.package.B" => decode[my.package.B](json).map(handlerForB)
case "my.package.C" => decode[my.package.C](json).map(handlerForC)
case _ =>
}
deserializeAndHandle(discriminator, json)
// store discriminator in DB
// use it to deserialize to Object which can be pattern-matched later
def deserializeToObject(discriminator: String, json: String): Option[Any] = discriminator match {
case "my.package.A" => decode[my.package.A](json).toOption
case "my.package.B" => decode[my.package.B](json).toOption
case "my.package.C" => decode[my.package.C](json).toOption
case _ => None
}
deserializeToObject(discriminator, json) map {
case a : A => ...
case b : B => ...
case c : C => ...
} getOrElse ???
// wrap unrelated types with sealed trait to make it sum type
// use sum type support of Circe
sealed trait Envelope
final case class EnvelopeA(a: A) extends Envelope
final case class EnvelopeB(b: B) extends Envelope
final case class EnvelopeA(c: C) extends Envelope
def deserializeEnveloped(json): Option[Envelope] = decode[Envelope](json).toOption
deserializeEnveloped(json) map {
case EnvelopeA(a) => ...
case EnvelopeB(b) => ...
case EnvelopeC(c) => ...
} getOrElse ???

Scala Multiple Future wrapped in Try

Suppose I have many akka services which all return a case class of type AbcDto wrapped in a Try.
So I call all these services using map and get back a List[Future[Any]].
Now I use Future.sequence to convert this to Future[List[Any]].
How do I unwrap my final list of results? I want to process them only when all of them are a Success and even if one fails I want to throw an error.
I tried mapping Future[List[Any]] as:
val a: List[Future[Any]]
a.map {
case r: List[Success[AbcDto]] => println("hello")
}
But this gives error:
case r: List[Try[AbcDto]]. At this point its giving error: non-variable type argument scala.util.Try[AbcDto] in type pattern List[scala.util.Try[AbcDto]] (the underlying of List[scala.util.Try[AbcDto]])
since all akka services return AbcDtowrapped in a Try the proper type of val a should be List[Future[Try[AbcDto]]]. Now the desired result can be achieved by a combination of Future.sequence and flatMap operation to check for any Failures in the service as shown below.
val a: List[Future[Try[AbcDto]]] = ...
val result: Future[List[AbcDto]] = Future.sequence(a) flatMap {
case r: List[Try[AbcDto]] #unchecked if r.find(!_.isSuccess).isDefined => Future.failed(new RuntimeException("not all results are successful"))
case r => Future.successful(r.collect({ case Success(x) => x}))
}
Future[A] and Try[A] in the actor context are so similar that I see no point to return Try[A] from those actors. You just return A in case of success, which will be Future[A] on asking side, a List of which you can sequence and get Future[List[A]], which, in case of a single failure, will contain the first encountered exception. It seems to be exactly what you're asking for.
To communicate failure from an actor to the asker, you should send akka.actor.Status.Failure with the relevant instance of Throwable.
P.S. regarding comment that using try-catch is non-idiomatic Scala. It actually is. Here's how Try creation is implemented:
object Try {
/** Constructs a `Try` using the by-name parameter. This
* method will ensure any non-fatal exception is caught and a
* `Failure` object is returned.
*/
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
As you can see, it uses try-catch inside. If Scala standard library authors are fine with that, so should be you. :)
If I understand you correctly (the types in your question are a bit confusing),
You start with a val responseFutures: List[Future[Any]] and after conversion you have a val responsesFuture: Future[List[Any]]. rogue-ones answer is correct, but it could use some clarification:
Your compiler error is caused by the fact that Success is not a class, but an extractor object with unapply for Try. Therefore you cannot use in in type extraction this way.
So something like case r: List[Try[AbcDto]] if r.forall(_.isSuccess) => println("hello") should compile. However, as AbcDto is erased, you will get a compiler warning about erasure. Thus the #unchecked.
UPDATE
Type erasure means, the compiler cannot check type argument type in pattern matches at compile time. In your case, all the compile knows of your input type is Future[List[Try[Any]]].
So
future.map {
case _: List[Try[AbcDto]] => ???
}
will cause a compiler warning because the compiler only sees.
future.map {
case _: List[Try[_]] => ???
}
The #unchecked annotation just suppresses the corresponding compiler warning.
In the end with the pattern match above you just cast whatever is coming in to Try[AbcDto] without any compile time type safety.

How can I use the pattern matching result in the List of matching type?

I've found some weird behaviour in Akka. When I matching the pattern, I cannot add a message to the list:
var msgs: List[Message] = Message() :: Nil
...
override def receive: Receive = {
case msg # Message => {
msgs = msgs.::(Message()) // ok
//msgs = msgs.::(msg) // doesn't work
sender ! "Thanks!"
}
case Request => { sender ! msgs.head }
case _ =>
}
Is this a bug in Scala or Akka?
To fix it I need to case the type
msgs = msgs.::(msg.asInstanceOf[Message])
which is not a convenient solution.
The problem is that you are making a subtle mistake in the matching happening in your case statement. You are matching there against the Message companion object, not the Message case class.
Another way of looking at it is that the case x # Y syntax can be thought of as saying "match with any instance of type Y, and then run the equivalent of val x: Y = <incoming value>.asInstanceOf[Y]", but here the inferred type you are providing is a parameter-less type called Message, which the compiler takes to be object Message, not case class Message().
So, to fix the line, write in the parameter list. For example, if the Message class is defined as:
case class Message()
this will be:
case msg # Message() => ...
If instead we had, say:
case class Message(text: String, id: Int)
then the case statement becomes something like:
case msg # Message(txt, is) => ...
or, if we don't care (or need to use) the text and id parameters:
case msg # Message(_, _) => ...
For a slightly more technically correct description of what is happening here, the case statement is actually attempting a match with any "unapply" methods available in the companion object (also called "extractors"). By default, there will be both an apply and an unapply method provided for free in the companion object for any case class that match exactly the parameter list provided in the case class constructor. In the second version of the Message class above, the relevant unapply method will have a signature like:
def unapply(text: String, id: Int): Option[Message]
You can read more about extractors here and here.

Does it make sense to return Try[Option[String]]?

I'm writing a method which has three possible return states:
The operation can have failed with an exception
The operation can have succeeded with a String result
The operation can have succeeded with no results
Does the return type Try[Option[String]] seem correct? Or is there a better alternative?
Lift's Box does a nice job of combining the semantics of Option and Try
A Box can be:
Full(value) => Some(value): Option
Empty => None: Option
Failure(ex) => Failure: Try
http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/common/Box.html
Your approach is perfectly fine. Another approach is to define your own case classes/objects
sealed trait Result
case class OperationFailed(e: Exception) extends Result
case class SuccessValue(str: String) extends Result
case object SuccessNoValue extends Result
Edit:
This approach may work better with pattern matching, specially if you are using Akka.
val f: Any => Any = { case x: Try[Option[String]] => x.get.get.toUpperCase }
f(Try(Some("x")))
f(Try(Some(5))) // Throws java.lang.ClassCastException
The above would show the following warning
warning: non-variable type argument Option[String] in type pattern
scala.util.Try[Option[String]] is unchecked since it is eliminated by
erasure

Akka & Case Statements

I'm learning Akka for Scala, and have also been reading up on Scala's pattern matching/case statements.
In Akka, I can write an actor as follows:
class MyActor extends Actor {
def receive: Receive = {
case msg: MyMsgClass => sender ! "message received!"
}
}
Questions:
Is this a case of Scala's pattern matching? If so, why is there no corresponding match keyword?
In the case line, is msg an identifier and is it required? What happens if I omit it and just use the class name (and presumably if I provide no identifier, I can't use the variable itself)?
It is a case of pattern matching, in combination with a Partial Function. In short, the partial function
{
case msg: MyMsgClass
}
only matches if there is an argument of type MyMsgClass. It handles a 'partial subset' of possible values. This syntax generates a PartialFunction object that handles the case where the input is a MyMsgClass.
You can also write:
{
case MyMsgClass(value) => sender ! value
}
but in this case you only get the value. You can even do complex things like:
{
case m # MyMsgClass(AnotherClass(_), "this must be this string", a) => sender ! doSomething(m, a)
}
and it will nicely match only MyMsgClass objects that have a first parameter of type AnotherClass (regardless of its parameters, hence the _), the exact string "this must be this string", and a value a. The m # syntax denotes that the object itself is also used, not just as a type.
Some more info: http://blog.bruchez.name/2011/10/scala-partial-functions-without-phd.html
You can see from the Scaladocs for Actor that Receive is defined as having type: type
Receive = PartialFunction[Any, Unit], which effectively means that it's an abstract member defined as being a partial function.