I want to create an enity system with some special properties, based on Scala traits.
The main idea is this: all components are traits that inherit from the common trait:
trait Component
trait ComponentA extends Component
sometimes, in case of a more complex hierarchy and inter-dependant components it can get like this:
trait ComponentN extends ComponentM {
self: ComponentX with ComponentY =>
var a = 1
var b = "hello"
}
and so on. I have come to the conclusion that the data relevant to each component should be contained in itself and not in some storage inside an Entity or elsewhere because of the speed of the access. As a side note - that is also why everything is mutable, so there is no need in thinking about immutability.
Then Entities are created, mixing in the traits:
class Entity
class EntityANXY extends ComponentA
with ComponentN
with ComponentX
with ComponentY
Here all is fine, however I do have a special requirement that I do not know how to fulfill with the code. The requirement is this:
Each trait must provide an encoding method(?) that facilitates collection of the trait-related data in a universal form, for example in a form of a JSON or a Map like Map("a" -> "1", "b" -> "hello") and a decoding method to translate such a map, if received, back into the trait-related variables. Also: 1) all the encoding and decoding methods of all the mixed-in traits are called in a bunch, in an arbitrary order by Entity's methods encode and decode(Map) and 2) should be made available to be called separately by specifying a trait type, or better, by a string parameter like decode("component-n", Map).
It is not possible to use methods with the same name as they will be lost due to shadowing or overriding. I can think of a solution, where all the methods are stored in a Map[String, Map[String, String] => Unit] for decode and Map[String, () => Map[String, String]] for encode in every entity. This would work - the by-name as well as a bunch call would certainly be available. However, this will result in storing the same information in every entity which is unacceptable.
It is also possible to store these maps in a companion object so that it is not duplicated anywhere and call the object's encode and decode method with an extra parameter denoting a particular instance of the entity.
The requirement may seem strange, but it is necessary because of the required speed and modularity. All of these solutions are clumsy and i think there is a better and idiomatic solution in Scala, or maybe I am missing some important architectural pattern here. So is there any simpler and more idiomatic approach than the one with the companion object?
EDIT: I think that aggregation instead of inheritance could probably resolve these problems but at a cost of not being able to call methods directly on an entity.
UPDATE: Exploring the pretty promising way proposed by Rex Kerr, I have stumbled upon something that hinders. Here is the test case:
trait Component {
def encode: Map[String, String]
def decode(m: Map[String, String])
}
abstract class Entity extends Component // so as to enforce the two methods
trait ComponentA extends Component {
var a = 10
def encode: Map[String, String] = Map("a" -> a.toString)
def decode(m: Map[String, String]) {
println("ComponentA: decode " + m)
m.get("a").collect{case aa => a = aa.toInt}
}
}
trait ComponentB extends ComponentA {
var b = 100
override def encode: Map[String, String] = super.encode + ("b" -> b.toString)
override def decode (m: Map[String, String]) {
println("ComponentB: decoding " + m)
super.decode(m)
m.get("b").foreach{bb => b = bb.toInt}
}
}
trait ComponentC extends Component {
var c = "hey!"
def encode: Map[String, String] = Map("c" -> c)
def decode(m: Map[String, String]) {
println("ComponentC: decode " + m)
m.get("c").collect{case cc => c = cc}
}
}
trait ComponentD extends ComponentB with ComponentC {
var d = 11.6f
override def encode: Map[String, String] = super.encode + ("d" -> d.toString)
override def decode(m: Map[String, String]) {
println("ComponentD: decode " + m)
super.decode(m)
m.get("d").collect{case dd => d = dd.toFloat}
}
}
and finally
class EntityA extends ComponentA with ComponentB with ComponentC with ComponentD
so that
object Main {
def main(args: Array[String]) {
val ea = new EntityA
val map = Map("a" -> "1", "b" -> "3", "c" -> "what?", "d" -> "11.24")
println("BEFORE: " + ea.encode)
ea.decode(map)
println("AFTER: " + ea.encode)
}
}
which gives:
BEFORE: Map(c -> hey!, d -> 11.6)
ComponentD: decode Map(a -> 1, b -> 3, c -> what?, d -> 11.24)
ComponentC: decode Map(a -> 1, b -> 3, c -> what?, d -> 11.24)
AFTER: Map(c -> what?, d -> 11.24)
The A and B components are not influenced, being cut-off by the inheritance resolution. So this approach is only applicable in certain hierarchy cases. In this case we see that the ComponentD has shadowed everything else. Any comments are welcomed.
UPDATE 2: I place the comment that answers this problem here, for better reference: "Scala linearizes all the traits. There should be a supertrait of everything which will terminate the chain. In your case, that means that C and A should still call super, and Component should be the one to terminate the chain with a no-op." – Rex Kerr
Travis had an essentially correct answer; not sure why he deleted it. But, anyway, you can do this without too much grief as long as you're willing to make your encoding method take an extra parameter, and that when you decode you're happy to just set mutable variables, not create a new object. (Complex trait-stacking effectively-at-runtime ranges from difficult to impossible.)
The basic observation is that when you chain traits together, it defines a hierarchy of superclass calls. If each of these calls takes care of the data in that trait, you'd be set, as long as you can find a way to get all that data back. So
trait T {
def encodeMe(s: Seq[String]): Seq[String] = Seq()
def encode = encodeMe(Seq())
}
trait A extends T {
override def encodeMe(s: Seq[String]) = super.encodeMe(s) :+ "A"
}
trait B extends T {
override def encodeMe(s: Seq[String]) = super.encodeMe(s) :+ "B"
}
Does it work?
scala> val a = new A with B
a: java.lang.Object with A with B = $anon$1#41a92be6
scala> a.encode
res8: Seq[String] = List(A, B)
scala> val b = new B with A
b: java.lang.Object with B with A = $anon$1#3774acff
scala> b.encode
res9: Seq[String] = List(B, A)
Indeed! Not only does it work, but you get the order for free.
Now we need a way to set variables based on this encoding. Here, we follow the same pattern--we take some input and just go up the super chain with it. If you have very many traits stacked on, you may want to pre-parse text into a map or filter out those parts applicable to the current trait. If not, just pass on everything to super, and then set yourself after it.
trait T {
var t = 0
def decode(m: Map[String,Int]) { m.get("t").foreach{ ti => t = ti } }
}
trait C extends T {
var c = 1
override def decode(m: Map[String,Int]) {
super.decode(m); m.get("c").foreach{ ci => c = ci }
}
}
trait D extends T {
var d = 1
override def decode(m: Map[String,Int]) {
super.decode(m); m.get("d").foreach{ di => d = di }
}
}
And this too works just like one would hope:
scala> val c = new C with D
c: java.lang.Object with C with D = $anon$1#549f9afb
scala> val d = new D with C
d: java.lang.Object with D with C = $anon$1#548ea21d
scala> c.decode(Map("c"->4,"d"->2,"t"->5))
scala> "%d %d %d".format(c.t,c.c,c.d)
res1: String = 5 4 2
Related
TL;DR
Given a hierarchy of case classes, a tree of instances can be constructed:
How do I convert that into "something else" via appropriate builders in a type safe (and user friendly) manner (without touching or altering the respective case classes) ?
Update 2021-02-25
I could make it somehow work with
import org.scalajs.dom.console
trait Root
case class Bob(name: String, as: Seq[Root]) extends Root
case class Charles(value: Int, as: Seq[Root]) extends Root
trait Builder[T] {
// can not make T covariant, as this is obj is not in a covariant position
def build(obj: T) : String
}
object Foo {
var r: Map[Root, Builder[Root]] = Map()
def attachBuilder[T <: Root](a: T, builder: Builder[T]) : Unit = {
val e = (a, builder)
r = r + e.asInstanceOf[(Root, Builder[Root])]
}
def b(name : String, as: Root*)(implicit builderB: Builder[Bob]): Bob = {
val b = Bob(name, as)
attachBuilder(b, builderB)
b
}
def c(value: Int, as: Root*)(implicit builderC: Builder[Charles]): Charles = {
val c = Charles(value, as)
attachBuilder(c, builderC)
c
}
def build(obj: Root) : String = {
r(obj).build(obj)
}
}
object UseMe {
implicit val builderB: Builder[Bob] = new Builder[Bob] {
override def build(obj: Bob): String = {
obj.name.toString + obj.as.map(a => Foo.build(a)).mkString(" ")
}
}
implicit val builderC: Builder[Charles] = new Builder[Charles] {
override def build(obj: Charles): String = {
obj.value.toString + obj.as.map(a => Foo.build(a)).mkString(" ")
}
}
def yahoo() : Unit = {
val x = Foo.b("martin", Foo.b("b2"), Foo.c(127))
console.log("This is x: " + Foo.build(x))
}
}
Still, I am very unsatisfied. Idea, I followed: Have a map that catches the respective builder for Bob or Charles. Still,
Builder[T] can not be made covariant. This prevents Builder[Bob] to be a subtype of Builder[Root]
I do not see the proper type signature for Map, so I had to typecast.
Requirements
Hierarchy of Root, Bob, Alice is extensible (so can not be sealed)
No use of static typing (by means of Shapeless HList or similar) as I simply will not have the full types as I am doing computations to assemble the tree.
Questions
What is a better approach?
Original Post
Prelude
Sigh .... mind-bending waste of hours ....... I seriously need your help!
Scenario
Given an ADT
trait A
case class B(name: String, as: Seq[A]) extends A
case class C(value: Int, as: Seq[A]) extends A
that is expected to be extended (not sealed).
Further, assume a
trait Builder[T] {
def build(obj: T) : String
}
Furthermore, with the code below we have "the creator functions" that expect the appropriate builders to in scope.
object Foo {
def b(name : String, as: A*)(implicit builderB: Builder[B]): B = {
???
// How to link ADT instance B with builder for B in a type-safe manner?
// How to trigger builder for `as`: Seq[A] ?
}
def c(value: Int, as: A*)(implicit builderC: Builder[C]): C = {
???
// How to link ADT instance C with builder for C in a type-safe manner?
}
}
With that I want to be able, after defining appropriate builders as implicit vals for B and C to do
object UseMe {
implicit val builderB: Builder[B] = new Builder[B] {
override def build(obj: B): String = {
obj.toString
// and build the as
}
}
implicit val builderC: Builder[C] = new Builder[C] {
override def build(obj: C): String = {
obj.value.toString
// and also build the as
}
}
val x = Foo.b("martin", Foo.b("b2"), Foo.c(127))
// Questions
// How to create a string representation (that is what the builder is doing) for x
// Something like:
// build(x)
}
By intention, I removed all my misleading tries in code, also not to induce any bias.
Tries
A builder impl that uses dynamic type information (via case b: B => ...) is working, but as I expect the ADT to be extended, this is not an option.
All my tries to model by generic types have failed. (Approaches with HList (Shapeless) might be feasible but are not considered, as I think this can be solved in plain Scala)
Questions
How to define methods in Foo?
How to solve builder pattern / creational pattern best for ADTs?
Looking forward to your answers!
Because I didn't find a solution to my problem anywhere, I think I might be thinking into a very wrong direction.
Here's my problem:
I have a trait A and another trait B and companion objects AB1, AB2, AB3 and so on. The singleton objects extend trait A and the classes extend trait B. Many objects of those classes are in a list.
On these objects I want to use functions defined in the corresponding singleton object which take objects of the same class as the singleton object is.
This an example for the traits:
trait A {
def compare(firstB: B, secondB: B) : Int
}
trait B {}
And the companion objects:
class AB1(val variable: Int) extends B {}
object AB1 extends A {
def apply(list: List[Int]): Option[AB1] = {
if(list.foldLeft(0)(_ + _) < 10 && list.nonEmpty)
some(new AB1(list.head))
else
null
}
override def compare(ab11: AB1, ab12: AB1): Int = {
if(ab11 > ab12)
1
else if(ab11 > ab12)
-1
else
0
}
}
and
class AB2(val variable1: Int, val variable2: Int) extends B {}
object AB2 extends A {
def apply(list: List[Int]): Option[AB1] = {
if(list.foldLeft(0)(_ + _) < 20 && list.length >= 2)
some(new AB1(list.head, list.tail.head))
else
null
}
override def compare(ab21: AB2, ab22: AB2): Int = {
if(ab11 > ab12)
10
else if(ab11 > ab12)
-10
else
0
}
}
So I've already filtered the some objects and put the "real" objects into a list, let's call it bList. On every element of the list I want to call the compare function.
I think it would look something like this:
val bList: List[B]
val a = getA(bList.head) // getA should be a function that returns the companion object of a class
a.compare(bList.head, bList.tail.head)
On my way I encountered two problems:
b.getClass does not equal AB1.getClass when b is an object the class AB1. But this is not my main problem. I found a solution using String comparison, which really is not pretty, but for now, it works.
The compare function has to be defined in the trait, because otherwise it can't be casted on any singleton object that extends trait A. But I find no way to define the function with parameters of a variable type.
I really hope you can help me with this problem!
EDIT: Now I see that I've forgotten to mention something:
I think that I have to go a bit deeper into what I am trying to do to make you understand my problem:
I have a List[List[C]]. A List of those C's may be capable of creating an AB1 object with it but maybe AB2, or maybe AB3 and so on. So I have
val c: List[C] = (C1, C2, C4)
val aList: List[A] = (AB1, AB2, AB3, ...)
val bestB: B = (for{
element <- aList
} yield element (c)).flatten.head // Because the List aList is ordered: AB1 is the best case, AB2 the second best and so on.
I do that for every List[C] out of the List[List[C]]. So in the end I may have a List[B] = (AB3, AB1, AB2, AB1) (for example). From this list I want to get the "best" Bs according to the order of aList at first - then i get List[B] = (AB1, AB1). These two AB1's I want to compare and then put the "better" Element (according to the compare function of the corresponding object) or both into a new list. This is want I want to accomplish.
To be honest, I'm still confused with what is your root problem. But I will try to answer what I understood.
First, if you want to change the type of the arguments when overriding a function from a trait. Then, the answer is you can't! - Because that would break the Liskov Substitution Principle.
But you can achieve what you want with a Type Class .
trait B {}
trait BComparator[Bi <: B] {
def compare(firstB: Bi, secondB: Bi): Int
}
Now if you want a way to dynamically obtain the right instance of the BComparator for your sub-classes you could make those instances as implicit values in their companion objects.
class B1 extends B {}
object B1 {
implicit val B1Comparator: BComparator[B1] = new BComparator[B1] {
override def compare(firstB: B1, secondB: B2): Int = ???
}
}
Now, given b11 and b12 as instances of B1 you could just write
implicitly[BComparator[B1]].compare(b11, b12)
I think you need to use implicit Comparator:
trait B
case class AB1(variable: Int) extends B
case class AB2(variable1: Int, variable2: Int) extends B
implicit object AB1Comparator extends Comparator[AB1] {
override def compare(o1: AB1, o2: AB1): Int = java.lang.Integer.compare(o1.variable, o2.variable)
}
implicit object AB2Comparator extends Comparator[AB2] {
override def compare(o1: AB2, o2: AB2): Int = java.lang.Integer.compare(o1.variable1, o2.variable1) match {
case 0 => java.lang.Integer.compare(o1.variable2, o2.variable2)
case other => other
}
}
def compare[A](obj1: A, obj2: A)(implicit comparator: Comparator[A]) = {
comparator.compare(obj1, obj2)
}
val ab1List = List(AB1(1), AB1(2), AB1(3))
val ab1Compare = compare(ab1List.head, ab1List.tail.head)
val ab2List = List(AB2(1, 1), AB2(1, 1), AB2(1, 3))
val ab2Compare = compare(ab2List.head, ab2List.tail.head)
Or if you want to sort list you should use Ordering:
trait B
case class AB1(variable: Int) extends B
implicit object AB1Ordering extends Ordering[AB1] {
override def compare(o1: AB1, o2: AB1): Int = java.lang.Integer.compare(o1.variable, o2.variable)
}
val ab1List = List(AB1(1), AB1(2), AB1(3))
val ab1ListSorted = ab1List.sorted
It appears there are three (or more) ways to limit which classes can mix-in a given scala trait:
Using a common ancestor [trait]
Using abstract declaration
Using self-type in the trait
The common ancestor method requires additional restrictions and it seems suboptimal. Meanwhile, both self-typing and abstract declarations seems to be identical. Would someone care to explain the difference and use-cases (especially between 2 & 3)?
My example is:
val exampleMap = Map("one" -> 1, "two" -> 2)
class PropsBox (val properties : Map[String, Any])
// Using Common Ancestor
trait HasProperties {
val properties : Map[String, Any]
}
trait KeysAsSupertype extends HasProperties {
def keys : Iterable[String] = properties.keys
}
class SubProp(val properties : Map[String, Any]) extends HasProperties
val inCommonAncestor = new SubProp(exampleMap) with KeysAsSupertype
println(inCommonAncestor.keys)
// prints: Set(one, two)
// Using Abstract Declaration
trait KeysAsAbstract {
def properties : Map[String, Any]
def keys : Iterable[String] = properties.keys
}
val inAbstract = new PropsBox(exampleMap) with KeysAsAbstract
println(inSelfType.keys)
// prints: Set(one, two)
// Using Self-type
trait KeysAsSelfType {
this : PropsBox =>
def keys : Iterable[String] = properties.keys
}
val inSelfType = new PropsBox(exampleMap) with KeysAsSelfType
println(inSelfType.keys)
// prints: Set(one, two)
In your example, PropsBox does not impose any interesting constraints on properties - it simply has a member properties: Map[String, Any]. Therefore, there is no way to detect the difference between inheriting from PropsBox and simply requiring a def properties: Map[String, Any].
Consider the following example, where the difference is actually there. Suppose we have two classes GoodBox and BadBox.
GoodBox has properties, and all keys are short string that contain only digits
BadBox just has properties, and does not guarantee anything about the structure of the keys
In code:
/** Has `properties: Map[String, Any]`,
* and also guarantees that all the strings are
* actually decimal representations of numbers
* between 0 and 99.
*/
class GoodBox(val properties: Map[String, Any]) {
require(properties.keys.forall {
s => s.forall(_.isDigit) && s.size < 3
})
}
/** Has `properties: Map[String, Any]`, but
* guarantees nothing about the keys.
*/
class BadBox(val properties: Map[String, Any])
Now suppose that we for some reason want to transform the Map[String, Any] into a sparsely populated Array[Any], and use keys as array indices. Here, again, are two ways to do this: one with self-type declaration, and one with the abstract def properties member declaration:
trait AsArrayMapSelfType {
self: GoodBox =>
def asArrayMap: Array[Any] = {
val n = 100
val a = Array.ofDim[Any](n)
for ((k, v) <- properties) {
a(k.toInt) = v
}
a
}
}
trait AsArrayMapAbstract {
def properties: Map[String, Any]
def asArrayMap: Array[Any] = {
val n = 100
val a = Array.ofDim[Any](n)
for ((k, v) <- properties) {
a(k.toInt) = v
}
a
}
}
Now try it out:
val goodBox_1 =
new GoodBox(Map("1" -> "one", "42" -> "fourtyTwo"))
with AsArrayMapSelfType
val goodBox_2 =
new GoodBox(Map("1" -> "one", "42" -> "fourtyTwo"))
with AsArrayMapAbstract
/* error: illegal inheritance
val badBox_1 =
new BadBox(Map("Not a number" -> "mbxkxb"))
with AsArrayMapSelfType
*/
val badBox_2 =
new BadBox(Map("Not a number" -> "mbxkxb"))
with AsArrayMapAbstract
goodBox_1.asArrayMap
goodBox_2.asArrayMap
// badBox_1.asArrayMap - not allowed, good!
badBox_2.asArrayMap // Crashes with NumberFormatException, bad
With a goodBox, both methods will work and produce the same results. However, with a badBox, the self-type vs. abstract-def behave differently:
self-type version does not allow the code to compile (error catched at compile-time)
abstract-def version crashes at runtime with a NumberFormatException (error happens at runtime)
That's the difference.
My API prototype is the following:
I have third party API object named ZResponseHandler which has method
printZ(z:Z)
no I have the following:
case class X
case class Y
case class Z(x:X,y:Y)
now when I use my API calling printZ method with new z instance it works OK.
ZResponseHandler.printZ(new Z(x,y))
but I would like to create something like this:
implicit def convertXYtoZ(x:X,y:Y):Z = new Z(x,y)
ZResponseHandler.printZ(x,y)
this code gives me compilation error - too many arguments for method printZ:
is there any way to make any implicit class which will accept printZ(x,y)?
Implicits can be used wrap or "pimp" the receiver to decorate it with more methods.
class R {
def m (s: String) = println(s)
}
// This uses an anonymous type, but it could also return
// `new RichR(r)` or similar as appropriate.
implicit def toRichR(r: R) = new {
def m(a: String, b: String) = r.m(a + " " + b)
}
val r = new R()
r.m("hello", "world") // -> toRichR(r).m("hello", "world")
Implicit classes (Scala 2.10+) also allow the above pattern to be written more clearly.
implicit class RichR(r: R) {
def m(a: String, b: String) = r.m(a + " " + b)
}
Objects can also be "pimped" in Scala 2.10 (but not 2.8)
object R {
def m (s: String) = println(s)
}
// uses TheObject.type
implicit def toRichR(_r: R.type) = new {
// (could use _r.m instead of R.m)
def m(a: String, b: String) = R.m(a + " " + b)
}
R.m("hello", "world") // -> toRichR(r).m("hello", "world")
(There are also implicit objects, but I could not get such to work without a common [non-object] base type.)
An implicit method will convert a single argument, so you can't change the arity (number of parameters). However, you might be able to get around this using a Tuple:
implicit def convertXYtoZ(t: (X,Y) ):Z = new Z(t._1, t._2)
ZResponseHandler.printZ(x,y)
Given a Tuple type
type T = (String, Int, String)
Is there any way I can get a type T1, where T1 would be
type T1 = (MyClass, String, Int, String)
I would love to be able to declare a class like
class TupleTypes[T] extends AnotherClass[T1]
Note: The tuple size is not known and
type T1 = (MyClass, T)
will not return what I want, it will return (MyClass, (String, Int, String)) which is different.
Thanks
You can do this using the HList to tuple conversions from shapeless.
scala> import shapeless._ ; import Tuples._
import shapeless._
import Tuples._
scala> class MyClass ; val m = new MyClass
defined class MyClass
m: MyClass = MyClass#3859023a
scala> val t1 = ("foo", 23, "bar")
t1: (String, Int, String) = (foo,23,bar)
scala> val t2 = (m :: t1.hlisted) tupled
t2: (MyClass, String, Int, String) = (MyClass#3859023a,foo,23,bar)
In my opinion there are no such constructs for tuples, but HList have a behavior very similar to the one you showed. They are considered has advanced type programming structure, and usage can be difficult depending on what you want to achieve. Here are an excellent starter and a nice implementation.
late to the party, but in the event that you are seeking a "better" solution in regard to the ScalaQuery problem, try this:
1) create mapper base class with ID
import org.scalaquery.ql.extended.{ExtendedTable => Table}
abstract class Mapper[T](table: String) extends Table[T](None, table) {
def id = column[Int]("id", O PrimaryKey)
}
2) extend mapper base using case class/companion object (i.e. not tuple based)
case class Foo (bar: String)
object Foos extends _Mapper[Foo]("foo") {
def foo = column[String]("foo")
}
then you can do something like:
def show: List[Foo] = {
val q = (for { f <- Foos } yield f)
val foos = db withSession {
foos.list map { case t:T => t }
}
render(foos)
}
and have a navigable object to work with (vs. index-based tuple).
Now, sometimes you don't want an enormous object graph when you only need a subset of fields from a group of entities.
That's where projections come in, just create a case class that represents the set of fields that you want and, voila, a navigable projection object to work with:
case class Yum (foo: String, baz: String)
def show: List[Yum] = {
val q = (for { f <- Foos; b <- Bars; if f.id is b.fooID } yield (f.foo, b.baz))
val yums = db withSession {
yums.list map { case t:T => t }
}
render(yums)
}
Fairly simple, should be encapsulated in a cake driven DAO, but the general principle is: take the case class/object route.
Should note that ScalaQuery kicks unbelievable azz, Zeiger is brilliant! (as are many in the Scala community, future is looking bright on Scala way ;-))