How to change a base class attribute of a case class - scala

Take the following code:
class A(val i: Int)
case class B(str: String) extends A(1)
val b = B("test")
In my scenario I am restricted by the definition of B which cannot be changed. I also wish to avoid reconstructing B as I have many such objects with a lot more attributes than in this example.
Is there any way I can create a new copy of b (using reflection or otherwise) with a new value for i?
Something that would be the equivalent of:
val b2 = b.copy(i = 2)
NOTE: The question is can it be done? Not what the best programming practice is.

You can do it using reflection with something like this:
val b = B("foo")
val c = b.copy()
println(b.i) // prints 1
val field = classOf[A].getDeclaredFields.head
field.setAccessible(true)
field.setInt(c, 2)
println(c.i) // prints 2
But beware, this is not just "bad programming practice", but rather complete breakage of the contract.
Your declaration case class B(s: Sting) extends A(1) promises that all instances of B will always have i equal to 1, which is a lie.
Not to mention, fun facts like b == c being true or c.copy.i being 1 etc.
Don't do this.

Not if you define B that way. But this code works for me:
class A(val i: Int)
case class B(str: String, override val i:Int=1) extends A(i)
val b = B("test")
val b2 = b.copy(i = 2)
P.S. well technically you can just copy with no modifications and then edit i using reflection but this is not a great idea IMHO.

You can do this:
val b2 = new B(b.str) { override val i = 2 }
The downside is that you have to re-construct B from the parameters which may be cumbersome if there are a lot of them.

Related

Scala covariant doc example allows apples and oranges

See this implementation follows the upper bound example http://docs.scala-lang.org/tutorials/tour/upper-type-bounds.html
class Fruit(name: String)
class Apple (name: String) extends Fruit(name)
class Orange(name: String) extends Fruit(name)
class BigOrange(name:String) extends Orange(name)
class BigFLOrange(name:String) extends BigOrange(name)
// Straight from the doc
trait Node[+B ] {
def prepend[U >: B ](elem: U)
}
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
def prepend[U >:B ](elem: U) = ListNode[U](elem, this)
def head: B = h
def tail = t
}
case class Nil[+B ]() extends Node[B] {
def prepend[U >: B ](elem: U) = ListNode[U](elem, this)
}
But this definition seems to allow multiple unrelated things in the same container
val f = new Fruit("fruit")
val a = new Apple("apple")
val o = new Orange("orange")
val bo = new BigOrange("big orange")
val foo :ListNode[BigOrange] = ListNode[BigOrange](bo, Nil())
foo.prepend(a) // add an apple to BigOrangeList
foo.prepend(o) // add an orange to BigOrangeList
val foo2 : ListNode[Orange] = foo // and still get to assign to OrangeList
So I am not sure this is a great example in the docs. And, question, how doe I modify the constraints so that..this behaves more like a List?
User #gábor-bakos points out that I am confusing invariance with covariance. So I tried the mutable list buffer. It does not later allow apple to be inserted into an Orange list Buffer, but it is not covariant
val ll : ListBuffer[BigOrange]= ListBuffer(bo)
ll += bo //good
ll += a // not allowed
So..can my example above (ListNode) be modified so that
1. it is covariant (it already is)
2 It is mutable, but mutable like the ListBuffer example (will not later allow apples to be inserted into BigOrange list
A mutable list cannot/should not be covariant in its argument type.
Exactly because of the reason you noted.
Suppose, you could have a MutableList[Orange], that was a subclass of a MutableList[Fruit]. Now, there is nothing that would prevent you from making a function:
def putApple(fruits: MutableList[Fruit], idx: Int) =
fruits(idx) = new Apple
You can add Apple to the list of Fruits, because Apple is a Fruit, nothing wrong with this.
But once you have a function like that, there is no reason you can't call it like this:
val oranges = new MutableList[Orange](new Orange, new Orange)
putApple(oranges, 0)
This will compile, since MutableList[Orange] is a subclass of MutableList[Fruit]. But now:
val firstOrange: Orange = oranges(0)
will crash, because the first element of oranges is actually an Apple.
For this reason, mutable collections have to be invariant in the element type (to answer the question you asked in the comments, to make the list invariant remove the + before B, and also get rid of the type parameter in prepend. It should just be def pretend(elem: B)).
How to get around it? The best solution is to simply not use mutable collections. You should not need them in 99% or real life scala code. If you think you need one, there is a 99% you are doing something wrong.
The major thing that you are probably missing is that prepend does not modify a list. In the line val foo2 : ListNode[Orange] = foo the list foo is still of type ListNode[BigOrange] and given covariance of parameters this assignment is valid (and there is nothing particularly awkward about it). Compiler will prevent you from assigning Fruits to Oranges (in this case assigning an Apple to an Orange is hazardous) but you have to save modified lists beforehand:
val foo: ListNode[Fruit] = ListNode[BigOrange](bo, Nil()).prepend(a).prepend(o)
val foo2: ListNode[Orange] = foo // this is not valid
Moreover your Node definition lacks return type for prepend - thus compiler infers wrong return type (Unit instead of ListNode[U]).
Here's fixed version:
trait Node[+B] {
def prepend[U >: B ](elem: U): Node[U]
}

Why do each new instance of case classes evaluate lazy vals again in Scala?

From what I have understood, scala treats val definitions as values.
So, any instance of a case class with same parameters should be equal.
But,
case class A(a: Int) {
lazy val k = {
println("k")
1
}
val a1 = A(5)
println(a1.k)
Output:
k
res1: Int = 1
println(a1.k)
Output:
res2: Int = 1
val a2 = A(5)
println(a1.k)
Output:
k
res3: Int = 1
I was expecting that for println(a2.k), it should not print k.
Since this is not the required behavior, how should I implement this so that for all instances of a case class with same parameters, it should only execute a lazy val definition only once. Do I need some memoization technique or Scala can handle this on its own?
I am very new to Scala and functional programming so please excuse me if you find the question trivial.
Assuming you're not overriding equals or doing something ill-advised like making the constructor args vars, it is the case that two case class instantiations with same constructor arguments will be equal. However, this does not mean that two case class instantiations with same constructor arguments will point to the same object in memory:
case class A(a: Int)
A(5) == A(5) // true, same as `A(5).equals(A(5))`
A(5) eq A(5) // false
If you want the constructor to always return the same object in memory, then you'll need to handle this yourself. Maybe use some sort of factory:
case class A private (a: Int) {
lazy val k = {
println("k")
1
}
}
object A {
private[this] val cache = collection.mutable.Map[Int, A]()
def build(a: Int) = {
cache.getOrElseUpdate(a, A(a))
}
}
val x = A.build(5)
x.k // prints k
val y = A.build(5)
y.k // doesn't print anything
x == y // true
x eq y // true
If, instead, you don't care about the constructor returning the same object, but you just care about the re-evaluation of k, you can just cache that part:
case class A(a: Int) {
lazy val k = A.kCache.getOrElseUpdate(a, {
println("k")
1
})
}
object A {
private[A] val kCache = collection.mutable.Map[Int, Int]()
}
A(5).k // prints k
A(5).k // doesn't print anything
The trivial answer is "this is what the language does according to the spec". That's the correct, but not very satisfying answer. It's more interesting why it does this.
It might be clearer that it has to do this with a different example:
case class A[B](b: B) {
lazy val k = {
println(b)
1
}
}
When you're constructing two A's, you can't know whether they are equal, because you haven't defined what it means for them to be equal (or what it means for B's to be equal). And you can't statically intitialize k either, as it depends on the passed in B.
If this has to print twice, it would be entirely intuitive if that would only be the case if k depends on b, but not if it doesn't depend on b.
When you ask
how should I implement this so that for all instances of a case class with same parameters, it should only execute a lazy val definition only once
that's a trickier question than it sounds. You make "the same parameters" sound like something that can be known at compile time without further information. It's not, you can only know it at runtime.
And if you only know that at runtime, that means you have to keep all past uses of the instance A[B] alive. This is a built in memory leak - no wonder Scala has no built-in way to do this.
If you really want this - and think long and hard about the memory leak - construct a Map[B, A[B]], and try to get a cached instance from that map, and if it doesn't exist, construct one and put it in the map.
I believe case classes only consider the arguments to their constructor (not any auxiliary constructor) to be part of their equality concept. Consider when you use a case class in a match statement, unapply only gives you access (by default) to the constructor parameters.
Consider anything in the body of case classes as "extra" or "side effect" stuffs. I consider it a good tactic to make case classes as near-empty as possible and put any custom logic in a companion object. Eg:
case class Foo(a:Int)
object Foo {
def apply(s: String) = Foo(s.toInt)
}
In addition to dhg answer, I should say, I'm not aware of functional language that does full constructor memoizing by default. You should understand that such memoizing means that all constructed instances should stick in memory, which is not always desirable.
Manual caching is not that hard, consider this simple code
import scala.collection.mutable
class Doubler private(a: Int) {
lazy val double = {
println("calculated")
a * 2
}
}
object Doubler{
val cache = mutable.WeakHashMap.empty[Int, Doubler]
def apply(a: Int): Doubler = cache.getOrElseUpdate(a, new Doubler(a))
}
Doubler(1).double //calculated
Doubler(5).double //calculated
Doubler(1).double //most probably not calculated

Member-wise assignment in scala?

Does scala support memberwise assignment?
Given:
case class C(var x:Int, var y:Int)
val c = C(1,2)
val d = C(3,4)
is there an operator to assign each member of d to c.
In C/C++ you would have:
struct C{ int x, int y)
C c = {1,2}
C d = {3,4}
c = d
edit1
One of the great benefits of member-wise assignment is that its automatic, in
Both
c() = d
and
c.copyFrom( d )
both suffer from a maintenance problem - if new members are added members to C its easy to overlook adding the member to the user-created function. What's really desired is automated way to copy these values.
edit2
Another use case for this behavior:
val props:ThirdPartyType = ThirdPartyLibrary.getProperties()
val myprops:ThirdPartyType = MyLibrary.loadPropertiesFromFile()
props #= myprops // magic member-wise assignment
Here we may find:
We're stuck with ThirdPartyLibrary and ThirdPartyType (can't change it)
The library doesn't provide the ability to reassign the property object.
The library does provide the ability to assign the property's values. It behaves as a JavaBean POJO with public members.
I can do:
props.a = myprops.a
props.b = myprops.b
...
but this pattern break when we update to V2 of the ThirdParty library. ThirdPartyType has gained new members that we didn't not copy.
Can't this be solved through reflection?
The enhancement is easy:
scala> :pa
// Entering paste mode (ctrl-D to finish)
case class C(var x:Int, var y:Int)
val c = C(1,2)
val d = C(3,4)
// Exiting paste mode, now interpreting.
defined class C
c: C = C(1,2)
d: C = C(3,4)
scala> implicit class CUpdater(val c: C) { def update(d: C) = { c.x=d.x ; c.y=d.y } }
defined class CUpdater
scala> c() = d
scala> c
res1: C = C(3,4)
Usual caveats around mutability apply. And you didn't hear it from me.
This will get you a separate object with all case class constructor arguments copied over, although it will not change the original object that d pointed to:
d = c.copy()
If you really want to modify the original object that d points to (perhaps because you have a reference to it somewhere else - think of pointers to structs in the C programming language), then I think you would have to do something fancy with metaprogramming (i.e. reflection or macros) to get a general solution for this.
But I recommend programming with immutable vals, almost always! If you can live with that, copy is fine.

How to clone a case class instance and change just one field in Scala?

Let's say I have a case class that represents personas, people on different social networks. Instances of that class are fully immutable, and are held in immutable collections, to be eventually modified by an Akka actor.
Now, I have a case class with many fields, and I receive a message that says I must update one of the fields, something like this:
case class Persona(serviceName : String,
serviceId : String,
sentMessages : Set[String])
// Somewhere deep in an actor
val newPersona = Persona(existingPersona.serviceName,
existingPersona.serviceId,
existingPersona.sentMessages + newMessage)
Notice I have to specify all fields, even though only one changes. Is there a way to clone existingPersona and replace only one field, without specifying all the fields that don't change? Can I write that as a trait and use it for all my case classes?
If Persona was a Map-like instance, it would be easy to do.
case classcomes with a copy method that is dedicated exactly to this usage:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)
Since 2.8, Scala case classes have a copy method that takes advantage of named/default params to work its magic:
val newPersona =
existingPersona.copy(sentMessages = existing.sentMessages + newMessage)
You can also create a method on Persona to simplify usage:
case class Persona(
svcName : String,
svcId : String,
sentMsgs : Set[String]
) {
def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}
then
val newPersona = existingPersona plusMsg newMsg
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
Consider using lens in Shapeless library:
import shapeless.lens
case class Persona(serviceName : String,
serviceId : String,
sentMessages : Set[String])
// define the lens
val messageLens = lens[Persona] >> 'sentMessages
val existingPersona = Persona("store", "apple", Set("iPhone"))
// When you need the new copy, by setting the value,
val newPersona1 = messageLens.set(existingPersona)(Set.empty)
// or by other operation based on current value.
val newPersona2 = messageLens.modify(existingPersona)(_ + "iPad")
// Results:
// newPersona1: Persona(store,apple,Set())
// newPersona2: Persona(store,apple,Set(iPhone, iPad))
Moreover, in case you have nested case classes, the getter and setter methods can be a bit tedious to compose. It will be a good chance to simplify by using lens library.
Please also refer to:
Shapeless Github / Boilerplate-free lenses for arbitrary case classes
Quicklens Github
Lens in scala
I didn't want to include a big library to do complex lenses that let you set values deep in nested case classes. It turns out it is just a few lines of code in the scalaz library:
/** http://stackoverflow.com/a/5597750/329496 */
case class Lens[A, B](get: A => B, set: (A, B) => A) extends ((A) => B) with Immutable {
def apply(whole: A): B = get(whole)
def mod(a: A, f: B => B) = set(a, f(this (a)))
def compose[C](that: Lens[C, A]) = Lens[C, B](
c => this(that(c)),
(c, b) => that.mod(c, set(_, b))
)
def andThen[C](that: Lens[B, C]) = that compose this
}
You can then create lenses that set deeply nested values far easier than using the built in copy feature. Here is a link to a big set if complex lenses that that my library uses to set heavily nested values.

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)