This page is no longer maintained — Please continue to the home page at www.scala-lang.org

How to deal with immutable classes

17 replies
Petr Gladkikh
Joined: 2009-01-20,
User offline. Last seen 42 years 45 weeks ago.

Hi.

I am trying to write manipulation methods for classes that have only
constant value members (val). Say I have class

class Account(val balance : Double) {

}

Petr Gladkikh
Joined: 2009-01-20,
User offline. Last seen 42 years 45 weeks ago.
Re: How to deal with immutable classes

Sorry :) I have accidently sent inconplpete message

I am trying to write manipulation methods for classes that have only
constant value members (val). Say I have method that changes state by
creating updated clone

class Account(val balance : Double) {
def change(diff : Double) = new Account(balance + diff)
}

But this all quickly gets too boring if I have many classes with
multiple member values

class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
def changeC(diff : Int) = new Snake(a, b, c + diff, d)
}

Moreover a class can be Inherited from something I do not really care
about and want just copy as-is.

If I would write all this manually it quickly gets unmanageable. Is
there some ready-to-use library or Scala pattern description that can
help with abstracting immutable fields update?

Chris Twiner
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

Although its still alot of boilerplate I've found that using abstract
type members and a virtual constructor is flexible and handles derived
classes and type parameters nicely:

package vcExample

abstract class RESTRICTED
abstract class NOT_RESTRICTED

abstract class Base[R](val restricted: Boolean, val str : String) {
type thisType[T] <: Base[T]
def newThis[R](restricted: Boolean, str : String) : thisType[R]

def restrict() : thisType[RESTRICTED] = newThis[RESTRICTED](true, str)
}

abstract class Derived[R](restricted: Boolean, str : String) extends
Base[R](restricted, str){

type thisType[T] <: Derived[T]

}

object Derived {
def apply[R](restricted: Boolean, str : String) : Derived[R] =
new Derived[R]( restricted, str) {
type thisType[T] = Derived[T]
override def newThis[R](restricted: Boolean, str : String) :
thisType[R] = apply(restricted, str)
}
}

class WorksWithRestricted(restricted : Base[RESTRICTED]) {}

object dummy {
val derived = Derived(false, "").restrict

val works = new WorksWithRestricted(derived)
}

Each derived class can create a further virtual constructor to handle
added constructor parameters.

On 1/30/09, Petr Gladkikh wrote:
> Sorry :) I have accidently sent inconplpete message
>
>
> I am trying to write manipulation methods for classes that have only
>
> constant value members (val). Say I have method that changes state by
> creating updated clone
>
>
> class Account(val balance : Double) {
>
> def change(diff : Double) = new Account(balance + diff)
> }
>
> But this all quickly gets too boring if I have many classes with
> multiple member values
>
> class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
> def changeC(diff : Int) = new Snake(a, b, c + diff, d)
> }
>
> Moreover a class can be Inherited from something I do not really care
> about and want just copy as-is.
>
> If I would write all this manually it quickly gets unmanageable. Is
> there some ready-to-use library or Scala pattern description that can
> help with abstracting immutable fields update?
>
> --
>
> Petr Gladkikh
>

Victor NOEL
Joined: 2008-12-21,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

On Fri, Jan 30, 2009 at 11:09:44AM +0100, Chris Twiner wrote:
> Although its still alot of boilerplate I've found that using abstract
> type members and a virtual constructor is flexible and handles derived
> classes and type parameters nicely:
>
> package vcExample
>
> abstract class RESTRICTED
> abstract class NOT_RESTRICTED
>
> abstract class Base[R](val restricted: Boolean, val str : String) {
> type thisType[T] <: Base[T]
> def newThis[R](restricted: Boolean, str : String) : thisType[R]
>
> def restrict() : thisType[RESTRICTED] = newThis[RESTRICTED](true, str)
> }
>
> abstract class Derived[R](restricted: Boolean, str : String) extends
> Base[R](restricted, str){
>
> type thisType[T] <: Derived[T]
>
> }
>
> object Derived {
> def apply[R](restricted: Boolean, str : String) : Derived[R] =
> new Derived[R]( restricted, str) {
> type thisType[T] = Derived[T]
> override def newThis[R](restricted: Boolean, str : String) :
> thisType[R] = apply(restricted, str)
> }
> }
>
> class WorksWithRestricted(restricted : Base[RESTRICTED]) {}
>
> object dummy {
> val derived = Derived(false, "").restrict
>
> val works = new WorksWithRestricted(derived)
> }
>
> Each derived class can create a further virtual constructor to handle
> added constructor parameters.

Hello,

I am not sure to understand how to use it, can't you give us a
better example, maybe something like the one in the question
(Account) ?

Thanks,

Victor

>
> On 1/30/09, Petr Gladkikh wrote:
> > Sorry :) I have accidently sent inconplpete message
> >
> >
> > I am trying to write manipulation methods for classes that have only
> >
> > constant value members (val). Say I have method that changes state by
> > creating updated clone
> >
> >
> > class Account(val balance : Double) {
> >
> > def change(diff : Double) = new Account(balance + diff)
> > }
> >
> > But this all quickly gets too boring if I have many classes with
> > multiple member values
> >
> > class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
> > def changeC(diff : Int) = new Snake(a, b, c + diff, d)
> > }
> >
> > Moreover a class can be Inherited from something I do not really care
> > about and want just copy as-is.
> >
> > If I would write all this manually it quickly gets unmanageable. Is
> > there some ready-to-use library or Scala pattern description that can
> > help with abstracting immutable fields update?
> >
> > --
> >
> > Petr Gladkikh
> >

Chris Twiner
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

how about:

abstract class Account[ D ](val balance: D) {

type accountType[T] <: Account[T]

def newThis( balance : D ) : accountType[D]

def setBalance( balance : D) = newThis(balance)
}

object Account {
def apply[D <: Number]( balance : D ) : Account[D] = new Account[D](balance){
type accountType[T] = Account[T]
def newThis(balance : D) : accountType[D] = apply[D](balance)
}
}

class NamedAccount[D](val name : String, override val balance : D)
extends Account[D](balance) {
type accountType[T] = NamedAccount[T]
def newThis(balance : D) : accountType[D] = new NamedAccount[D](name, balance)
def newThis(name :String, balance : D) : accountType[D] = new
NamedAccount[D](name, balance)

def setName( name : String ) = newThis( name, balance)
}

object Test {
def main(args : Array[String]) : Unit = {
val named1 = new NamedAccount("name1", 100.0)
val named2 = named1 setBalance 200.00
val named3 = named1 setName "newname"
println(named3.name)
}
}

it gets even more wordy if you want a named account to be a base
class, then that too must have a seperate construction function.

It should be noted that adding restrictions to the type like Account[D
<: Number] causes grief with the refinements of the abstract type.

Also if you don't need type parameters then the this.type syntax works
great and doesn't require abstract type members. In this example its
safe to say that you'd model the balance with a decent currency /
number class from the outset and just this.type would be safe (unless
you again added type parameters later in the hierarchy).

On 1/30/09, Victor NOEL wrote:
> On Fri, Jan 30, 2009 at 11:09:44AM +0100, Chris Twiner wrote:
> > Although its still alot of boilerplate I've found that using abstract
> > type members and a virtual constructor is flexible and handles derived
> > classes and type parameters nicely:
> >
> > package vcExample
> >
> > abstract class RESTRICTED
> > abstract class NOT_RESTRICTED
> >
> > abstract class Base[R](val restricted: Boolean, val str : String) {
> > type thisType[T] <: Base[T]
> > def newThis[R](restricted: Boolean, str : String) : thisType[R]
> >
> > def restrict() : thisType[RESTRICTED] = newThis[RESTRICTED](true, str)
> > }
> >
> > abstract class Derived[R](restricted: Boolean, str : String) extends
> > Base[R](restricted, str){
> >
> > type thisType[T] <: Derived[T]
> >
> > }
> >
> > object Derived {
> > def apply[R](restricted: Boolean, str : String) : Derived[R] =
> > new Derived[R]( restricted, str) {
> > type thisType[T] = Derived[T]
> > override def newThis[R](restricted: Boolean, str : String) :
> > thisType[R] = apply(restricted, str)
> > }
> > }
> >
> > class WorksWithRestricted(restricted : Base[RESTRICTED]) {}
> >
> > object dummy {
> > val derived = Derived(false, "").restrict
> >
> > val works = new WorksWithRestricted(derived)
> > }
> >
> > Each derived class can create a further virtual constructor to handle
> > added constructor parameters.
>
>
> Hello,
>
> I am not sure to understand how to use it, can't you give us a
> better example, maybe something like the one in the question
> (Account) ?
>
> Thanks,
>
>
> Victor
>
>
> >
> > On 1/30/09, Petr Gladkikh wrote:
> > > Sorry :) I have accidently sent inconplpete message
> > >
> > >
> > > I am trying to write manipulation methods for classes that have only
> > >
> > > constant value members (val). Say I have method that changes state by
> > > creating updated clone
> > >
> > >
> > > class Account(val balance : Double) {
> > >
> > > def change(diff : Double) = new Account(balance + diff)
> > > }
> > >
> > > But this all quickly gets too boring if I have many classes with
> > > multiple member values
> > >
> > > class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
> > > def changeC(diff : Int) = new Snake(a, b, c + diff, d)
> > > }
> > >
> > > Moreover a class can be Inherited from something I do not really care
> > > about and want just copy as-is.
> > >
> > > If I would write all this manually it quickly gets unmanageable. Is
> > > there some ready-to-use library or Scala pattern description that can
> > > help with abstracting immutable fields update?
> > >
> > > --
> > >
> > > Petr Gladkikh
> > >
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.6 (GNU/Linux)
>
> iD8DBQFJgtR1Q7uDv7gIkeURAm4tAJ4+2Y5Hn2TyG2sCXjkW7YeaVUi26gCfTbWx
> zXxf0Xv1PllAcdtNbMA3DLs=
> =x42R
> -----END PGP SIGNATURE-----
>
>
>

Luc Duponcheel
Joined: 2008-12-19,
User offline. Last seen 34 weeks 3 days ago.
Re: Re: How to deal with immutable classes
apart from the (I agree!) interesting generic code, the answer to the original question could also simply have been something along the following lines:   implement methods like deposit(amount: Double) by creating a new account with a balance that has been incremented by amount

or am I missing something?

--
  __~O
 -\ <,
(*)/ (*)

reality goes far beyond imagination

Chris Twiner
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

could be, I understood the question more in the lines of a previous
post on case class field mutators. Either way there are no pretty
options for it :<

On 1/30/09, Luc Duponcheel wrote:
> apart from the (I agree!) interesting generic code,
> the answer to the original question could also
> simply have been something along the following lines:
>
> implement methods like deposit(amount: Double)
> by creating a new account
> with a balance that has been incremented by amount
>
> or am I missing something?
>
> --
> __~O
> -\ <,
> (*)/ (*)
>
> reality goes far beyond imagination
>
>

Detering Dirk
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
RE: Re: How to deal with immutable classes

> apart from the (I agree!) interesting generic code,
> the answer to the original question could also
> simply have been something along the following lines:
>
> implement methods like deposit(amount: Double)
> by creating a new account
> with a balance that has been incremented by amount

I admit, I haven't understood this reply.

Wasn't the original question exactly all about this
"create new amount with balance x" stuff?

Where this quote should be extended with
"... while all the other properties' values are the same"

As far as I understood the question, it was
about something like how to copycreate an instance
without having to code the setValue(getValue) semantics
all by hand.

Am I wrong or haven't I understood the answer?

BTW: As an ordinary Java developer, used to work with
mutable properties of business models, I came to the
same question as I stepped over this "use as much immutability
as possible" approach.

Or more general: I cannot imagine a typical Audio-CD-collection
maintenance program with a really consequent immutability
approach. How would that look like, if you need to maintain a
net of relationships of business classes?

KR
Det

Luc Duponcheel
Joined: 2008-12-19,
User offline. Last seen 34 weeks 3 days ago.
Re: Re: How to deal with immutable classes
I think that the answer to your question lies in the development of a library that, on the one hand is defined internally using vars, but does not allow its users to make use of vars   after all, even the List library has been designed that way (for efficiency reasons)   ps1: in principle the the library itself could also be defined without using vars and rely upon generic libraries that, on the one hand are defined internally using of vars, but does not allow its users to make use of vars   ps2: in priciple even those generic libraries could be defined without using vars (for the die-hards) relying upon appropriate computation abstractions [ of course when your CD collection is in a DB, then behind  the scenes of those abstractions, external side effects would happen (as opposed to internal (in memory) side effects because of using vars)]

On Fri, Jan 30, 2009 at 4:06 PM, Detering Dirk <Dirk [dot] Detering [at] bitmarck [dot] de> wrote:
> apart from the (I agree!) interesting generic code,
> the answer to the original question could also
> simply have been something along the following lines:
>
> implement methods like deposit(amount: Double)
> by creating a new account
> with a balance that has been incremented by amount

I admit, I haven't understood this reply.

Wasn't the original question exactly all about this
"create new amount with balance x" stuff?

Where this quote should be extended with
"... while all the other properties' values are the same"

As far as I understood the question, it was
about something like how to copycreate an instance
without having to code the setValue(getValue) semantics
all by hand.

Am I wrong or haven't I understood the answer?


BTW: As an ordinary Java developer, used to work with
mutable properties of business models, I came to the
same question as I stepped over this "use as much immutability
as possible" approach.

Or more general: I cannot imagine a typical Audio-CD-collection
maintenance program with a really consequent immutability
approach.  How would that look like, if you need to maintain a
net of relationships of business classes?

KR
Det



--
  __~O
 -\ <,
(*)/ (*)

reality goes far beyond imagination

Stepan Koltsov
Joined: 2008-12-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

I hope, problem will be solved in 2.8:

http://lampsvn.epfl.ch/svn-repos/scala/lamp-sip/named-args/sip.xhtml#mot...

S.

On Fri, Jan 30, 2009 at 12:56, Petr Gladkikh wrote:
>
> I am trying to write manipulation methods for classes that have only
> constant value members (val). Say I have method that changes state by
> creating updated clone
>
> class Account(val balance : Double) {
> def change(diff : Double) = new Account(balance + diff)
> }
>
> But this all quickly gets too boring if I have many classes with
> multiple member values
>
> class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
> def changeC(diff : Int) = new Snake(a, b, c + diff, d)
> }

Jens Alfke
Joined: 2009-01-30,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

On Jan 30, 2009, at 1:56 AM, Petr Gladkikh wrote:

> If I would write all this manually it quickly gets unmanageable. Is
> there some ready-to-use library or Scala pattern description that can
> help with abstracting immutable fields update?

(Disclaimer: I'm a Scala and functional-programming newbie, though
very experienced with OOP)

I think you might want to re-think the issue at a higher level. It
does seem as though the specific thing you're trying to do is awkward
(or at least boring to code) with immutable objects.

Rather than modeling the objects as simple containers with setters for
every attribute, would it work better to define only the methods that
reflect the higher-level state changes [I hate the term "business
logic", but that's what I mean] that your app calls for? So instead of
a_=, b_=, c_=, ... j_=, have a smaller number of mutators like
twiddleWith( ), openPodBayDoors( ), dispusion_= ? The implementations
of these would create new instances with modified attribute values,
but there are fewer of these to write.

(A side benefit of this, I've found, is that it makes it impossible to
have model objects in an inconsistent state, which helps reliability.)

—Jens

Chris Twiner
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

case class Foo[A](a: A, b: Int) {
def copy(a: A = this.a, b: Int = this.b) = Foo(a, b)
}
val foo1 = Foo("test", 1)
val foo2 = foo1.copy(b = 2)

please note that while this tackles the boilerplate of many setters /
modifiers directly it can't help for solving the rest of the
boilerplate problems I outlined (virtual constructor vs type params
etc).

There are language mechanims for that, as per the code I pasted, but
they aren't overly pleasent. However other than the proposal I put
forward (http://code.google.com/p/scala-scales/wiki/VirtualConstructorPreSIP)
I've not seen many people seem to be interested in the problem set
itself.

By way of explanation I'm interested in creating fully immutable trees
of components, allowing caching etc, these components need type
parameters and calling a modifier function on a base class should not
change the type of the object being used and returned.

On 1/30/09, Stepan Koltsov wrote:
> I hope, problem will be solved in 2.8:
>
> http://lampsvn.epfl.ch/svn-repos/scala/lamp-sip/named-args/sip.xhtml#mot...
>
>
> S.
>
>
> On Fri, Jan 30, 2009 at 12:56, Petr Gladkikh wrote:
> >
> > I am trying to write manipulation methods for classes that have only
> > constant value members (val). Say I have method that changes state by
> > creating updated clone
> >
> > class Account(val balance : Double) {
> > def change(diff : Double) = new Account(balance + diff)
> > }
> >
> > But this all quickly gets too boring if I have many classes with
> > multiple member values
> >
> > class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
> > def changeC(diff : Int) = new Snake(a, b, c + diff, d)
> > }
>

Alex Boisvert
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes
On Fri, Jan 30, 2009 at 8:05 AM, Chris Twiner <chris [dot] twiner [at] gmail [dot] com> wrote:
case class Foo[A](a: A, b: Int) {
 def copy(a: A = this.a, b: Int = this.b) = Foo(a, b)
}
val foo1 = Foo("test", 1)
val foo2 = foo1.copy(b = 2)

please note that while this tackles the boilerplate of many setters /
modifiers directly it can't help for solving the rest of  the
boilerplate problems I outlined (virtual constructor vs type params
etc).

Agreed.
 
There are language mechanims for that, as per the code I pasted, but
they aren't overly pleasent.  However other than the proposal I put
forward (http://code.google.com/p/scala-scales/wiki/VirtualConstructorPreSIP)
I've not seen many people seem to be interested in the problem set
itself.

I am!
(but I have no time to contribute to the project itself, I'm interested from a user standpoint... I think it can have a significant impact on scala libraries/extensions too, such as O-R mappoing, etc.)
 
By way of explanation I'm interested in creating fully immutable trees
of components, allowing caching etc, these components need type
parameters and calling a modifier function on a base class should not
change the type of the object being used and returned.

Exactly.

alex
Meredith Gregory
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes
Stepan,

This is a remarkably well-spec'd feature. Bravo!

Have you looked at the feature from the code maintenance point of view? The language R has this feature and in going through legacy code in preparation to build a compiler for R to the JVM i found that code that made heavy use of this feature was confusing for me to read and understand.

i really don't know where the right balance point is; but, code is both written and read. Features that simplify the writing part can negatively impact the reading part. Best. example. ever : APL. This is a write only language!

Best wishes,

--greg

On Fri, Jan 30, 2009 at 7:42 AM, Stepan Koltsov <stepan [dot] koltsov [at] gmail [dot] com> wrote:
I hope, problem will be solved in 2.8:

http://lampsvn.epfl.ch/svn-repos/scala/lamp-sip/named-args/sip.xhtml#motivation

S.

On Fri, Jan 30, 2009 at 12:56, Petr Gladkikh <petrglad [at] gmail [dot] com> wrote:
>
> I am trying to write manipulation methods for classes that have only
> constant value members (val). Say I have method that changes state by
> creating updated clone
>
> class Account(val balance : Double) {
>  def change(diff : Double) = new Account(balance + diff)
> }
>
> But this all quickly gets too boring if I have many classes with
> multiple member values
>
> class Snake(val a : Int, val b : Int, val c : Int, d : Int) {
>  def changeC(diff : Int) = new Snake(a, b, c + diff, d)
> }



--
L.G. Meredith
Managing Partner
Biosimilarity LLC
806 55th St NE
Seattle, WA 98105

+1 206.650.3740

http://biosimilarity.blogspot.com
Petr Gladkikh
Joined: 2009-01-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

Thank you for your suggestions.

I am designing a system in which most objects do not change (actually
it is required that you can track changes in any object for some time
back). Instead of having some kind of history associated with objects
I decided that it would be better to just create new copy of changed
object while keeping old one intact. So in a sense it is functional.
But I would like it to work nicely with Java ORM frameworks so they
know what to store. It also is expected that some customization will
be done by less experienced users so API should be less confusing.

I think that in my case mutators that return modified state would do.
(This is one of cases that this is not a Lisp, where you can DRY
essentially any boilerplate code :) I have also experimented with
AspectJ and found out that it can provide "near optimal" solution.

As a side note, I found that MVCC caching in JBoss Cache does very
similar things but unfortunately it would be hard to re-use this
functionality ...

Thanks again.

Detering Dirk
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
RE: Re: How to deal with immutable classes

Hi Stepan,

I had only a short look at the spec,

> http://lampsvn.epfl.ch/svn-repos/scala/lamp-sip/named-args/sip
> .xhtml#motivation

but I missed mention of how this looks like with function type
parameters.

How would the correct syntax in this default scenario look like
(if possible anyway)?

def myFunc(a:Int, b:Int, f: (Int,Int) => Int = (x:Int, y:Int) => x+1 ) =
f(a,b)

KR
Det

Stepan Koltsov
Joined: 2008-12-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: How to deal with immutable classes

First, Lukas Rytz is the spec author.

Seems like your code is correct.

However, parameter type is optional when specifying default value:

===
def myFunc(a:Int, b:Int, f = (x:Int, y:Int) => x + 1) = f(a,b)
===

or function type can be aliased:

===
type F = (Int,Int) => Int
def myFunc(a:Int, b:Int, f: F = (x:Int, y:Int) => x + 1) = f(a,b)
===

S.

On Mon, Feb 2, 2009 at 14:07, Detering Dirk wrote:
> I had only a short look at the spec,
>
>> http://lampsvn.epfl.ch/svn-repos/scala/lamp-sip/named-args/sip
>> .xhtml#motivation
>
> but I missed mention of how this looks like with function type
> parameters.
>
> How would the correct syntax in this default scenario look like
> (if possible anyway)?
>
>
> def myFunc(a:Int, b:Int, f: (Int,Int) => Int = (x:Int, y:Int) => x+1 ) =
> f(a,b)

Detering Dirk
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
RE: Re: How to deal with immutable classes

> First, Lukas Rytz is the spec author.

I've seen, but thought you could answer this nevertheless :-) .

> However, parameter type is optional when specifying default value:
>
> ===
> def myFunc(a:Int, b:Int, f = (x:Int, y:Int) => x + 1) = f(a,b)
> ===

This looks indeed very interesting. Thought of a collector method
I know in a Java app, where filtering is possible but a default filter
applied if none explicitly given.

This syntax would indeed improve such code.

KR
Det

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland