How do I reflectively create a new collection? - scala

I have an instance of a collection that I want to store externally and then restore back into the original collection type. For example
class Foo {
var x : List[Int]
}
val f = new Foo
f.x = List(1, 2, 3)
I "serialize" out f, I want to reflectively create a new Foo, f2, and populate f2.x with the correct results.
I can create the new Foo by doing classOf[Foo].newInstance, but how do I then create the correct collection type and populate it?
Note, I'm making lots of assumptions here, notable:
1) I know the type of f.x, and I can even serialize out the type of that
2) I'm serializing out the contents of x into something that preserves the values
3) I don't want to use any "standard" serialization
I've tried to use the builders available on the original collection, but I don't quite understand how that works enough to pull it off.
Thanks,
Dave

It would be easier to help here if we had a better idea of the problem you're trying to solve, e.g. why you don't want to use the standard object serialization.
That said, if you really do want to do reflection over Scala collection classes, you'll probably need to know a few things about how Scala currently compiles its classes and objects:
If you want the class for the List object (not for the List class), the name is scala.collection.immutable.List$ - note the final dollar sign.
If you want the singleton List object instance, that's stored as the field MODULE$.
Most scala collection companion objects provide a newBuilder method which creates an object that has a += ($plus$eq) method and a result method that allow you to create a new collection.
So you could do something like:
scala> def buildByReflection[T](collectionClassName: String, items: Array[T]) = {
| val companionClass = Class.forName(collectionClassName + "$")
| val companion = companionClass.getField("MODULE$").get(null)
| val newBuilder = companionClass.getMethod("newBuilder")
| val builder = newBuilder.invoke(companion)
| val plusEq = builder.getClass.getMethod("$plus$eq", classOf[Object])
| for (item <- items) {
| plusEq.invoke(builder, item.asInstanceOf[AnyRef])
| }
| builder.getClass.getMethod("result").invoke(builder)
| }
buildByReflection: [T](collectionClassName: String,items: Array[T])java.lang.Object
scala> buildByReflection("scala.collection.immutable.List", Array(1, 2, 3))
res0: java.lang.Object = List(1, 2, 3)

Related

How to extend object properties with other in Scala?

I am new to Scala, how to extends one properties value from other object. Like in angular, angular.copy(obj1, obj2);
If I have two objects, obj1, obj2, both having the same property names. obj2's value would be overridden to obj1.
How to achieve this in Scala?
Unlike JavaScript, Scala does not allow you to add new properties to existing objects. Objects are created with a specific type and that type defines the properties on the object. If your objects are implemented as a case class then it is easy to make a copy of an object:
val obj = obj1.copy()
You can also update some of the fields of obj1 during the copy and leave others untouched:
val obj = obj1.copy(count=obj2.count)
This will create a new object with all the same values as obj1 except for the field count which will take the value from obj2.
It has been noted in the comments that in Scala you will typically create new objects from old ones, rather than modifying existing objects. This may seem cumbersome at first but it make it much easier to understand the behaviour of more complex code because you know that objects won't change under your feet.
Note on object in Scala vs JS
Note that object in Scala as a completely different concept from the usage in Javascript.
Scala deliberately is not using Prototype base inheritance.
So in short that this is not possible is a feature not a mssing capability of the language.
see https://docs.scala-lang.org/tour/singleton-objects.html
Solution : Use a Map to simulate protoype base inheritance/composition
You can use a Map to achieve a similar behaviour:
scala> val sayHello = () => "Hello"
sayHello: () => String = <function0>
scala> val obj1 = Map("sayHello" -> sayHello , "id" -> 99)
obj1: scala.collection.immutable.Map[String,Any] = Map(sayHello -> <function0>, id -> 99)
scala> val obj2 = Map("id" -> 1)
obj2: scala.collection.immutable.Map[String,Int] = Map(id -> 1)
scala> obj1 ++ obj2
res0: scala.collection.immutable.Map[String,Any] = Map(sayHello -> <function0>, id -> 1)

Getting a null with a val depending on abstract def in a trait [duplicate]

This question already has answers here:
Scala - initialization order of vals
(3 answers)
Closed 7 years ago.
I'm seeing some initialization weirdness when mixing val's and def's in my trait. The situation can be summarized with the following example.
I have a trait which provides an abstract field, let's call it fruit, which should be implemented in child classes. It also uses that field in a val:
scala> class FruitTreeDescriptor(fruit: String) {
| def describe = s"This tree has loads of ${fruit}s"
| }
defined class FruitTreeDescriptor
scala> trait FruitTree {
| def fruit: String
| val descriptor = new FruitTreeDescriptor(fruit)
| }
defined trait FruitTree
When overriding fruit with a def, things work as expected:
scala> object AppleTree extends FruitTree {
| def fruit = "apple"
| }
defined object AppleTree
scala> AppleTree.descriptor.describe
res1: String = This tree has loads of apples
However, if I override fruit using a val...
scala> object BananaTree extends FruitTree {
| val fruit = "banana"
| }
defined object BananaTree
scala> BananaTree.descriptor.describe
res2: String = This tree has loads of nulls
What's going on here?
In simple terms, at the point you're calling:
val descriptor = new FruitTreeDescriptor(fruit)
the constructor for BananaTree has not been given the chance to run yet. This means the value of fruit is still null, even though it's a val.
This is a subcase of the well-known quirk of the non-declarative initialization of vals, which can be illustrated with a simpler example:
class A {
val x = a
val a = "String"
}
scala> new A().x
res1: String = null
(Although thankfully, in this particular case, the compiler will detect something being afoot and will present a warning.)
To avoid the problem, declare fruit as a lazy val, which will force evaluation.
The problem is the initialization order. val fruit = ... is being initialized after val descriptor = ..., so at the point when descriptor is being initialized, fruit is still null. You can fix this by making fruit a lazy val, because then it will be initialized on first access.
Your descriptor field initializes earlier than fruit field as trait intializes earlier than class, that extends it. null is a field's value before initialization - that's why you get it. In def case it's just a method call instead of accessing some field, so everything is fine (as method's code may be called several times - no initialization here). See, http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html
Why def is so different? That's because def may be called several times, but val - only once (so its first and only one call is actually initialization of the fileld).
Typical solution to such problem - using lazy val instead, it will intialize when you really need it. One more solution is early intializers.
Another, simpler example of what's going on:
scala> class A {val a = b; val b = 5}
<console>:7: warning: Reference to uninitialized value b
class A {val a = b; val b = 5}
^
defined class A
scala> (new A).a
res2: Int = 0 //null
Talking more generally, theoretically scala could analize the dependency graph between fields (which field needs other field) and start initialization from final nodes. But in practice every module is compiled separately and compiler might not even know those dependencies (it might be even Java, which calls Scala, which calls Java), so it's just do sequential initialization.
So, because of that, it couldn't even detect simple loops:
scala> class A {val a: Int = b; val b: Int = a}
<console>:7: warning: Reference to uninitialized value b
class A {val a: Int = b; val b: Int = a}
^
defined class A
scala> (new A).a
res4: Int = 0
scala> class A {lazy val a: Int = b; lazy val b: Int = a}
defined class A
scala> (new A).a
java.lang.StackOverflowError
Actually, such loop (inside one module) can be theoretically detected in separate build, but it won't help much as it's pretty obvious.

differences between List(), Array() and new List(), new Array() in Scala

I know that when you type:
val list = List(2,3)
you are accessing the apply method of the List object which returns a List. What I can't understand is why is this possible when the List class is abstract and therefore cannot be directly instanciated(new List() won't compile)?
I'd also like to ask what is the difference between:
val arr = Array(4,5,6)
and
val arr = new Array(4, 5, 6)
The List class is sealed and abstract. It has two concreate implementations
Nil which represents an empty list
::[B] which represents a non empty list with head and tail. ::[B] in the documentation
When you call List.apply it will jump through some hoops and supply you with an instance of the ::[B] case class.
About array: new Array(4, 5, 6) will throw a compile error as the constructor of array is defined like this: new Array(_length: Int). The apply method of the Array companion object uses the arguments to create a new instance of an Array (with the help of ArrayBuilder).
I started writing that the easy way to determine this is to look at the sources for the methods you're calling, which are available from the ScalaDoc. However, the various levels of indirection that are gone through to actually build a list give lie to the term 'easy'! It's worth having a look through if you want, starting from the apply method in the List object which is defined as follows:
override def apply[A](xs: A*): List[A] = xs.toList
You may or may not know that a parameter of the form xs : A* is treated internally as a Seq, which means that we're calling the toList method on a Seq, which is defined in TraversableOnce. This then delegates to a generic to method, which looks for an implicit
CanBuildFrom which actually constructs the list. So what you're getting back is some implementation of List which is chosen by the CanBuildFrom. What you actually get is a scala.collection.immutable.$colon$colon, which implements a singly-linked list.
Luckily, the behaviour of Array.apply is a little easier to look up:
def apply[T: ClassTag](xs: T*): Array[T] = {
val array = new Array[T](xs.length)
var i = 0
for (x <- xs.iterator) { array(i) = x; i += 1 }
array
}
So, Array.apply just delegates to new Array and then sets elements appropriately.

How to use mutable collections in Scala

I think I may be failing to understand how mutable collections work. I would expect mutable collections to be affected by applying map to them or adding new elements, however:
scala> val s: collection.mutable.Seq[Int] = collection.mutable.Seq(1)
s: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
scala> s :+ 2 //appended an element
res32: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2)
scala> s //the original collection is unchanged
res33: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
scala> s.map(_.toString) //mapped a function to it
res34: scala.collection.mutable.Seq[java.lang.String] = ArrayBuffer(1)
scala> s //original is unchanged
res35: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
//maybe mapping a function that changes the type of the collection shouldn't work
//try Int => Int
scala> s.map(_ + 1)
res36: scala.collection.mutable.Seq[Int] = ArrayBuffer(2)
scala> s //original unchanged
res37: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
This behaviour doesn't seem to be separate from the immutable collections, so when do they behave separately?
For both immutable and mutable collections, :+ and +: create new collections. If you want mutable collections that automatically grow, use the += and +=: methods defined by collection.mutable.Buffer.
Similarly, map returns a new collection — look for transform to change the collection in place.
map operation applies the given function to all the elements of collection, and produces a new collection.
The operation you are looking for is called transform. You can think of it as an in-place map except that the transformation function has to be of type a -> a instead of a -> b.
scala> import collection.mutable.Buffer
import collection.mutable.Buffer
scala> Buffer(6, 3, 90)
res1: scala.collection.mutable.Buffer[Int] = ArrayBuffer(6, 3, 90)
scala> res1 transform { 2 * }
res2: res1.type = ArrayBuffer(12, 6, 180)
scala> res1
res3: scala.collection.mutable.Buffer[Int] = ArrayBuffer(12, 6, 180)
The map method never modifies the collection on which you call it. The type system wouldn't allow such an in-place map implementation to exist - unless you changed its type signature, so that on some type Collection[A] you could only map using a function of type A => A.
(Edit: as other answers have pointed out, there is such a method called transform!)
Because map creates a new collection, you can go from a Collection[A] to a Collection[B] using a function A => B, which is much more useful.
As others have noted, the map and :+ methods return a modified copy of the collection. More generally, all methods defined in collections from the scala.collection package will never modify a collection in place even when the dynamic type of the collection is from scala.collection.mutable. To modify a collection in place, look for methods defined in scala.collection.mutable._ but not in scala.collection._.
For example, :+ is defined in scala.collection.Seq, so it will never perform in-place modification even when the dynamic type is a scala.collection.mutable.ArrayBuffer. However, +=, which is defined in scala.collection.mutable.ArrayBuffer and not in scala.collection.Seq, will modify the collection in place.

Does Scala have record update syntax for making modified clones of immutable data structures?

In Mercury I can use:
A = B^some_field := SomeValue
to bind A to a copy of B, except that some_field is SomeValue instead of whatever it was in B. I believe the Haskell equivalent is something like:
a = b { some_field = some_value }
Does Scala have something like this for "modifying" immutable values. The alternative seems to be to have a constructor that directly sets every field in the instance, which isn't always ideal (if there are invarients the constructor should be maintaining). Plus it would be really clunky and much more fragile if I had to explicitly pass every other value in the instance I want to have a modified copy of.
I couldn't find anything about this by googling, or in a brief survey of the language reference manual or "Scala By Example" (which I have read start-to-finish, but haven't absorbed all of yet, so it may well be in there).
I can see that this feature could have some weird interactions with Java-style access protection and subclasses though...
If you define your class as a case class, a convenient copy method is generated, and calling it you can specify with named parameters new values for certain fields.
scala> case class Sample(str: String, int: Int)
defined class Sample
scala> val s = Sample("text", 42)
s: Sample = Sample(text,42)
scala> val s2 = s.copy(str = "newText")
s2: Sample = Sample(newText,42)
It even works with polymorphic case classes:
scala> case class Sample[T](t: T, int: Int)
defined class Sample
scala> val s = Sample("text", 42)
s: Sample[java.lang.String] = Sample(text,42)
scala> val s2 = s.copy(t = List(1,2,3), 42)
s2: Sample[List[Int]] = Sample(List(1, 2, 3),42)
Note that s2 has a different type than s.
You can use case classes for this, but you don't have to. Case classes are nothing magical - the modifier case just saves you a lot of typing.
The copy method is realized by the use of named and default parameters. The names are the same as the fields and the defaults are the current values of the fields. Here's an example:
class ClassWithCopy(val field1:String, val field2:Int) {
def copy(field1:String = this.field1, field2:Int = this.field2) = {
new ClassWithCopy(field1,field2);
}
}
You can use this just like the copy method on case classes. Named and default parameters are a very useful feature, and not only for copy methods.
If the object you're planning on modifying is a case class then you can use the autogenerated copy method:
scala> val user = User(2, "Sen")
user: User = User(2,Sen)
scala> val corrected = user.copy(name = "Sean")
corrected: User = User(2,Sean)