How can I use a mutable Map in an Actor object to store values depending on the messages?
My code
object TestActor {
final case class Command()
private val mmap: mutable.Map[Int,Map[Int,Int]] = mutable.Map[Int,Map[Int,Int]]()
def apply(): Behavior[Command] = Behaviors.receive {
case (context, Command()) =>
mmap += (1 -> (2 -> 3))
context.log.info(nmap)
}
}
This causes problems since the mmap is static as it is a field in a scala object. So when multiple threads add some values to the map, it would result in strange values. Since I still need to use typed akka actors, is there a way to have a field in an object (or in some other way) to make it possible for me to still use objects to represent the actors using akka.typed and still have fields that are not shared among the TestActor actors?
In Akka Typed's functional API (which this example uses), the typical way to do this would be along these lines:
object TestActor {
final case class Command()
def apply(): Behavior[Command] =
fromMap(mutable.Map.empty)
private def fromMap(mmap: mutable.Map[Int, Map[Int, Int]]): Behavior[Command] =
Behaviors.receive {
case (context, Command()) =>
mmap += (1 -> Map(2 -> 3))
context.log.info(mmap)
Behaviors.same
}
}
Here, the object is basically serving as just a module and the fields of that object are better thought of as globals.
Note that there is also an object-oriented API for defining typed actors, which in Scala would be:
object TestActor {
final case class Command()
def apply(): Behavior[TestActor.Command] =
Behaviors.setup { context =>
new TestActor(context)
}
}
class TestActor(context: ActorContext[TestActor.Command]) extends AbstractBehavior[TestActor.Command](context) {
val mmap: mutable.Map[Int, Map[Int, Int]] = mutable.Map.empty
def onMessage(msg: TestActor.Command): Behavior[TestActor.Command] = {
mmap += (1 -> Map(2, 3))
context.log.info(mmap)
this
}
}
It's worth noting that in Akka, it's generally better to have vars of immutable data (i.e. mutable containers holding immutable values) than vals of mutable data (i.e. immutable containers holding mutable values), as that can expose a channel for something outside the actor (including another actor) to "pull the rug out from under" an actor.
In the functional API, this would typically take a form like:
object TestActor {
final case class Command()
def apply(): Behavior[Command] =
fromMap(Map.empty)
private def fromMap(map: Map[Int, Map[Int, Int]]): Behavior[Command] =
Behaviors.receive {
case (context, Command()) =>
val nextMap = map + (1 -> Map(2 -> 3))
context.log.info(nextMap)
withMap(nextMap)
}
}
And in the OO Scala API:
// TestActor companion object is as before
class TestActor(context: ActorContext[TestActor.Command]) extends AbstractBehavior[TestActor.Command](context) {
var map: Map[Int, Map[Int, Int]] = Map.empty
def onMessage(msg: TestActor.Command): Behavior[TestActor.Command] = {
map = map + (1 -> Map(2 -> 3))
context.log.info(map)
this
}
}
Related
I am using reactivemongo-akka-stream and trying to transform the AkkaStreamCursor.documentSource. I am and am seeing two problems:
the documentation states that this operation returns a Source[T, NotUsed] but collection.find(query).cursor[BSONDocument].doccumentSource() returns a Source[BSONDocument, Future[State]]. Is there a way to avoid the State object?
Assuming I use Future[State] I am looking to get source of class as below
case class Inner(foo: String, baz: Int)
case class Outer(bar: Inner)
//
implicit object InnerReader extends BSONDocumentReader[Inner]//defined
val getCollection: Future[BSONCollection] = connection.database("db").map(_.collection("things")
def stream()(implicit m: Materializer): Source[Outer, Future[State]] = {
getCollection.map(_.find().cursor[Inner]().documentSource()).map(_.via(Flow[Inner].map(in => Outer(in))))
But instead of getting back a Future[Source[Outer, Future[State]] that i could deal with, this returns a Future[Source[Inner, Future[State]]#Repr[Outer]]
How can bson readers be used with this library?
As per the suggestion by cchantep, I needed to use fromFuture with flatMapConcat:
def stream()(implicit m: Materializer): Source[Outer, NotUsed] = {
val foo = getCollection.map(x => col2Source(x))
fix(foo).via(flowmap(map = Outer(_)))
}
def col2Source(col: BSONCollection): Source[Inner, Future[State]] = {
val cursor: AkkaStreamCursor[Inner] =
col.find(BSONDocument.empty).cursor[Inner]()
cursor.documentSource()
}
def flowmap[In, Out](
map: (In) => Out
): Flow[In, Out, NotUsed] = Flow[In].map(e => map(e))
def fix[Out, Mat](futureSource: Future[Source[Out, Mat]]): Source[Out, NotUsed] = {
Source.fromFuture(futureSource).flatMapConcat(identity)
}
What is considered the correct way to create polymorphic constructors in Scala? On the one hand, I can use a companion object:
class UserRecord (override val userID: String,
override val rep: Int) extends Record
object UserRecord extends Record {
def apply(line: String) = {
val elem = XML.loadString(line)
val userID = elem \ "#Id" text
val rep = (elem \ "#Reputation" text).toInt
val out = new UserRecord(userID, rep)
out
}
}
but this loses some encapsulation, and the call to the object without new obscures the fact that a non-singleton object is being created. On the other hand, I can use a sequence of alternate constructors, which is a bit clunky:
class UserRecord (override val userID: String,
override val rep: Int) extends Record {
def this(t: Tuple2[String, Int]) = this(t._1, t._2)
def this(line: String) = this({
val elem = XML.loadString(line)
val userID = elem \ "#Id" text
val rep = (elem \ "#Reputation" text).toInt
val out = (userID, rep)
out
})
}
I've also looked for ways to avoid using the first alternate constructor using .tupled but haven't really cracked that nut.
I want to write some function that can return different Class objects according to the arguments.
For example, I have some classes that extends akka Actor, and I want to get their classes by passing different Int values. The code below is not correct, but I think you can understand what I mean:
def createActor(num: Int): Unit {
val c: Class = o.getActorClass(num)
system.actorOf(Props[c]) ! "hello"
}
object o {
val m: Map[Int, Class] = Map(1->classOf[Actor1], 2->classOf[Actor2])
def getActorClass(num: Int): Class {
m(num)
}
}
Hope my question is understandable. Thank you!
If you simply return the ActorRef, you should be fine.
def createActor(num: Int): ActorRef = {
val c = o.getActorClass(num)
val actor = system.actorOf(Props(c))
actor ! "hello"
actor
}
I would create a generator method for creating a new mapping under a given actor system like so:
def newMapping(sys: ActorSystem, mappings: (Int, Props)*) = {
assert(sys != null)
val m = mappings.toMap
(i: Int) => sys.actorOf(m(i))
}
Then you can create different mappings with different ActorSystems and ultimately get the effect you desire:
val sys = ActorSystem("sys1")
val myMapping = newMapping(sys, 1 -> Props[Class1], 2 -> Props[Class2])
val actor1 = myMapping(1)
val actor2 = myMapping(2)
I want to build a Scala DSL to convert from a existing structure of Java POJOs to a structure equivalent to a Map.
However the incoming objects structure is very likely to contain a lot of null references, which will result in no value in the output map.
The performance is very important in this context so I need to avoid both reflection and throw/catch NPE.
I have considered already this topic which does not meet with my requirements.
I think the answer may lie in the usage of macros to generate some special type but I have no experience in the usage of scala macros.
More formally :
POJO classes provided by project : (there will be like 50 POJO, nested, so I want a solution which does not require to hand-write and maintain a class or trait for each of them)
case class Level1(
#BeanProperty var a: String,
#BeanProperty var b: Int)
case class Level2(
#BeanProperty var p: Level1,
#BeanProperty var b: Int)
expected behaviour :
println(convert(null)) // == Map()
println(convert(Level2(null, 3))) // == Map("l2.b" -> 3)
println(convert(Level2(Level1("a", 2), 3))) // == Map(l2.p.a -> a, l2.p.b -> 2, l2.b -> 3)
correct implementation but I want an easier DSL for writing the mappings
implicit def toOptionBuilder[T](f: => T) = new {
def ? : Option[T] = Option(f)
}
def convert(l2: Level2): Map[String, _] = l2? match {
case None => Map()
case Some(o2) => convert(o2.p, "l2.p.") + ("l2.b" -> o2.b)
}
def convert(l1: Level1, prefix: String = ""): Map[String, _] = l1? match {
case None => Map()
case Some(o1) => Map(
prefix + "a" -> o1.a,
prefix + "b" -> o1.b)
}
Here is how I want to write with a DSL :
def convertDsl(l2:Level2)={
Map(
"l2.b" -> l2?.b,
"l2.p.a" -> l2?.l1?.a,
"l2.p.b" -> l2?.l1?.b
)
}
Note that it is perfectly fine for me to specify that the property is optional with '?'.
What I want is to generate statically using a macro a method l2.?l1 or l2?.l1 which returns Option[Level1] (so type checking is done correctly in my DSL).
I couldn't refine it down to precisely the syntax you gave above, but generally, something like this might work:
sealed trait FieldSpec
sealed trait ValueFieldSpec[+T] extends FieldSpec
{
def value: Option[T]
}
case class IntFieldSpec(value: Option[Int]) extends ValueFieldSpec[Int]
case class StringFieldSpec(value: Option[String]) extends ValueFieldSpec[String]
case class Level1FieldSpec(input: Option[Level1]) extends FieldSpec
{
def a: ValueFieldSpec[_] = StringFieldSpec(input.map(_.a))
def b: ValueFieldSpec[_] = IntFieldSpec(input.map(_.b))
}
case class Level2FieldSpec(input: Option[Level2]) extends FieldSpec
{
def b: ValueFieldSpec[_] = IntFieldSpec(input.map(_.b))
def l1 = Level1FieldSpec(input.map(_.p))
}
case class SpecArrowAssoc(str: String)
{
def ->(value: ValueFieldSpec[_]) = (str, value)
}
implicit def str2SpecArrowAssoc(str: String) = SpecArrowAssoc(str)
implicit def Level2ToFieldSpec(input: Option[Level2]) = Level2FieldSpec(input)
def map(fields: (String, ValueFieldSpec[_])*): Map[String, _] =
Map[String, Any]((for {
field <- fields
value <- field._2.value
} yield (field._1, value)):_*)
def convertDsl(implicit l2: Level2): Map[String, _] =
{
map(
"l2.b" -> l2.?.b,
"l2.p.a" -> l2.?.l1.a,
"l2.p.b" -> l2.?.l1.b
)
}
Then we get:
scala> val myL2 = Level2(Level1("a", 2), 3)
myL2: Level2 = Level2(Level1(a,2),3)
scala> convertDsl(myL2)
res0: scala.collection.immutable.Map[String,Any] = Map(l2.b -> 3, l2.p.a -> a, l2.p.b -> 2)
Note that the DSL uses '.?' rather than just '?' as the only way I could see around Scala's trouble with semicolon inference and postfix operators (see, eg., #0__ 's answer to scala syntactic suger question).
Also, the strings you can provide are arbitrary (no checking or parsing of them is done), and this simplistic 'FieldSpec' hierarchy will assume that all your POJOs use 'a' for String fields and 'b' for Int fields etc.
With a bit of time and effort I'm sure this could be improved on.
What I need is a class X I can construct with a Map that takes Strings into either other Strings or Maps that take Strings into Strings, and then an arbitrary number of other instances of X. With my limited grasp of Scala, I know I can do this:
class Person (stringParms : Map[String, String],
mapParms : Map[String, Map[String, String]],
children : List[X]) {
}
but that doesn't look very Scala-ish ("Scalish"? "Scalerific"? "Scalogical"?) I'd like to be able to do is the following:
Person bob = Person("name" -> "Bob", "pets" -> ("cat" -> "Mittens", "dog" -> "Spot"), "status" -> "asleep",
firstChild, secondChild)
I know I can get rid of the "new" by using the companion object and I'm sure I can look Scala varargs. What I'd like to know is:
How I can use -> (or some similarly plausible operator) to construct elements to be made into a Map in the construction?
How I can define a single map so either it can do an Option-like switch between two very disparate types or becomes a recursive tree, where each (named) node points to either a leaf in the form of a String or another node like itself?
The recursive version really appeals to me because, although it doesn't address a problem I actually have today, it maps neatly into a subset of JSON containing only objects and strings (no numbers or arrays).
Any help, as always, greatly appreciated.
-> is just a syntactic sugar to make a pair (A, B), so you can use it too. Map object takes a vararg of pairs:
def apply [A, B] (elems: (A, B)*) : Map[A, B]
You should first check out The Architecture of Scala Collections if you're interested in mimicking the collections library.
Having said that, I don't think the signature you proposed for Person looks like Map, because it expects variable argument, yet children are not continuous with the other (String, A) theme. If you say "child1" -> Alice, and internally store Alice seperately, you could define:
def apply(elems: (String, Any)*): Person
in the companion object. If Any is too loose, you could define PersonElem trait,
def apply(elems: (String, PersonElem)*): Person
and implicit conversion between String, Map[String, String], Person, etc to PersonElem.
This gets you almost there. There is still a Map I don't get easily rid of.
The basic approach is to have a somewhat artificial parameter types, which inherit from a common type. This way the apply method just takes a single vararg.
Using implicit conversion method I get rid of the ugly constructors for the parameter types
case class Child
case class Person(stringParms: Map[String, String],
mapParms: Map[String, Map[String, String]],
children: List[Child]) { }
sealed abstract class PersonParameter
case class MapParameter(tupel: (String, Map[String, String])) extends PersonParameter
case class StringParameter(tupel: (String, String)) extends PersonParameter
case class ChildParameter(child: Child) extends PersonParameter
object Person {
def apply(params: PersonParameter*): Person = {
var stringParms = Map[String, String]()
var mapParms = Map[String, Map[String, String]]()
var children = List[Child]()
for (p ← params) {
p match {
case StringParameter(t) ⇒ stringParms += t
case MapParameter(t) ⇒ mapParms += t
case ChildParameter(c) ⇒ children = c :: children
}
}
new Person(stringParms, mapParms, children)
}
implicit def tupel2StringParameter(t: (String, String)) = StringParameter(t)
implicit def child2ChildParameter(c: Child) = ChildParameter(c)
implicit def map2MapParameter(t: (String, Map[String, String])) = MapParameter(t)
def main(args: Array[String]) {
val firstChild = Child()
val secondChild = Child()
val bob: Person = Person("name" -> "Bob","pets" -> Map("cat" -> "Mittens", "dog" -> "Spot"),"status"
-> "asleep",
firstChild, secondChild)
println(bob)
} }
Here's one way:
sealed abstract class PersonParam
object PersonParam {
implicit def toTP(tuple: (String, String)): PersonParam = new TupleParam(tuple)
implicit def toMap(map: (String, Map[String, String])): PersonParam = new MapParam(map)
implicit def toSP(string: String): PersonParam = new StringParam(string)
}
class TupleParam(val tuple: (String, String)) extends PersonParam
class MapParam(val map: (String, Map[String, String])) extends PersonParam
class StringParam(val string: String) extends PersonParam
class Person(params: PersonParam*) {
val stringParams = Map(params collect { case parm: TupleParam => parm.tuple }: _*)
val mapParams = Map(params collect { case parm: MapParam => parm.map }: _*)
val children = params collect { case parm: StringParam => parm.string } toList
}
Usage:
scala> val bob = new Person("name" -> "Bob",
| "pets" -> Map("cat" -> "Mittens", "dog" -> "Spot"),
| "status" -> "asleep",
| "little bob", "little ann")
bob: Person = Person#5e5fada2
scala> bob.stringParams
res11: scala.collection.immutable.Map[String,String] = Map((name,Bob), (status,asleep))
scala> bob.mapParams
res12: scala.collection.immutable.Map[String,Map[String,String]] = Map((pets,Map(cat -> Mittens, dog -> Spot)))
scala> bob.children
res13: List[String] = List(little bob, little ann)