
|
equals in case class does not seem to be working...
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using
assertEquals() in Junit 4 with objects from the same case class with
exactly the same content wold cut it. Yet, I run into some troubles
and ended up doing this small snippet in the scala shell that shows me
it does not work.
Am I doing anything wrong or is this to be expected?
If it is to be expected could you explain/point to some reading that
makes it clear why.
Thanks in advance
Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node
import scala.xml.Node
scala> import scala.xml.Elem
import scala.xml.Elem
scala> import scala.reflect.BeanProperty
import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty
import scala.reflect.BeanProperty
scala> import java.util.Date
import java.util.Date
scala> import scala.reflect.BeanInfo
import scala.reflect.BeanInfo
scala> /**
| * Represents a user of WikiModels
| */
scala> @BeanInfo
| case class User(usrName:String,
| pssword:String,
| frstName:String,
| lstName:String,
| mail:String ) extends DataModel {
|
| var userName = usrName
| var password = pssword
| var firstName = frstName
| var lastName = lstName
| var emailAddress = mail
|
| def this() = {
| this("","","","","")
| }
|
| /**
| * generates a XML representation of the user, excluding password
| * @return the XML representing the user
| */
| def toXML:Node =
| <user>
| <username>{userName}</username>
| <!--<password>{password}</password>-->
| <firstName>{firstName}</firstName>
| <lastName>{lastName}</lastName>
| <email>{emailAddress}</email>
| </user>
|
| def extractXML(xml:Node):Unit = {
| xml match {
| case <username>{contents}</username> =>
this.userName = contents.text
| case <firstName>{contents}</firstName> =>
this.firstName = contents.text
| case <lastName>{contents}</lastName> =>
this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress
= contents.text
| case <user>{c @ _ *}</user> => for (val child <- c)
extractXML(child)
| case _ => { }
| }
| }
| }
defined class User
scala> var u = User("alex","alexp","Alexandre",
"Martins"," alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
scala> var d = new User()
d: User = User(,,,,)
scala> u == d
res2: Boolean = false
scala> u
res3: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
scala> d
res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password
res18: String = alexpscala> d
res7: User = User(,,,,)
scala> d.toXML == u.toXML
res8: Boolean = true
scala> d == u
res9: Boolean = false
|

|
Re: equals in case class does not seem to be working...
Case classes define immutable fields, as well as a generated equals method. The fact that you defined additional mutable fields does not change the generated equals method.
-------------------------------------
Alexandre< alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using
assertEquals() in Junit 4 with objects from the same case class with
exactly the same content wold cut it. Yet, I run into some troubles
and ended up doing this small snippet in the scala shell that shows me
it does not work.
Am I doing anything wrong or is this to be expected?
If it is to be expected could you explain/point to some reading that
makes it clear why.
Thanks in advance
Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node
import scala.xml.Node
scala> import scala.xml.Elem
import scala.xml.Elem
scala> import scala.reflect.BeanProperty
import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty
import scala.reflect.BeanProperty
scala> import java.util.Date
import java.util.Date
scala> import scala.reflect.BeanInfo
import scala.reflect.BeanInfo
scala> /**
| * Represents a user of WikiModels
| */
scala> @BeanInfo
| case class User(usrName:String,
| pssword:String,
| frstName:String,
| lstName:String,
| mail:String ) extends DataModel {
|
| var userName = usrName
| var password = pssword
| var firstName = frstName
| var lastName = lstName
| var emailAddress = mail
|
| def this() = {
| this("","","","","")
| }
|
| /**
| * generates a XML representation of the user, excluding password
| * @return the XML representing the user
| */
| def toXML:Node =
| <user>
| <username>{userName}</username>
| <!--<password>{password}</password>-->
| <firstName>{firstName}</firstName>
| <lastName>{lastName}</lastName>
| <email>{emailAddress}</email>
| </user>
|
| def extractXML(xml:Node):Unit = {
| xml match {
| case <username>{contents}</username> =>
this.userName = contents.text
| case <firstName>{contents}</firstName> =>
this.firstName = contents.text
| case <lastName>{contents}</lastName> =>
this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress
= contents.text
| case <user>{c @ _ *}</user> => for (val child <- c)
extractXML(child)
| case _ => { }
| }
| }
| }
defined class User
scala> var u = User("alex","alexp","Alexandre",
"Martins"," alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
scala> var d = new User()
d: User = User(,,,,)
scala> u == d
res2: Boolean = false
scala> u
res3: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
scala> d
res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password
res18: String = alexpscala> d
res7: User = User(,,,,)
scala> d.toXML == u.toXML
res8: Boolean = true
scala> d == u
res9: Boolean = false
|

|
Re: equals in case class does not seem to be working...
You'd have more luck in making the constructor args into vars:
case class User(
var userName : String, var password : String, var firstName : String, var lastName : String,
var emailAddress : String ) extends DataModel { def this() = ...
... ...
}
Not tried this myself on case classes, but can't think of any reason why it wouldn't work :)
On Wed, Jul 8, 2009 at 2:51 AM, Naftoli Gugenhem <naftoligug@...> wrote:
Case classes define immutable fields, as well as a generated equals method. The fact that you defined additional mutable fields does not change the generated equals method.
-------------------------------------
Alexandre< alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using
assertEquals() in Junit 4 with objects from the same case class with
exactly the same content wold cut it. Yet, I run into some troubles
and ended up doing this small snippet in the scala shell that shows me
it does not work.
Am I doing anything wrong or is this to be expected?
If it is to be expected could you explain/point to some reading that
makes it clear why.
Thanks in advance
Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node
import scala.xml.Node
scala> import scala.xml.Elem
import scala.xml.Elem
scala> import scala.reflect.BeanProperty
import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty
import scala.reflect.BeanProperty
scala> import java.util.Date
import java.util.Date
scala> import scala.reflect.BeanInfo
import scala.reflect.BeanInfo
scala> /**
| * Represents a user of WikiModels
| */
scala> @BeanInfo
| case class User(usrName:String,
| pssword:String,
| frstName:String,
| lstName:String,
| mail:String ) extends DataModel {
|
| var userName = usrName
| var password = pssword
| var firstName = frstName
| var lastName = lstName
| var emailAddress = mail
|
| def this() = {
| this("","","","","")
| }
|
| /**
| * generates a XML representation of the user, excluding password
| * @return the XML representing the user
| */
| def toXML:Node =
| <user>
| <username>{userName}</username>
| <!--<password>{password}</password>-->
| <firstName>{firstName}</firstName>
| <lastName>{lastName}</lastName>
| <email>{emailAddress}</email>
| </user>
|
| def extractXML(xml:Node):Unit = {
| xml match {
| case <username>{contents}</username> =>
this.userName = contents.text
| case <firstName>{contents}</firstName> =>
this.firstName = contents.text
| case <lastName>{contents}</lastName> =>
this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress
= contents.text
| case <user>{c @ _ *}</user> => for (val child <- c)
extractXML(child)
| case _ => { }
| }
| }
| }
defined class User
scala> var u = User("alex","alexp","Alexandre",
"Martins"," alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
scala> var d = new User()
d: User = User(,,,,)
scala> u == d
res2: Boolean = false
scala> u
res3: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
scala> d
res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password
res18: String = alexpscala> d
res7: User = User(,,,,)
scala> d.toXML == u.toXML
res8: Boolean = true
scala> d == u
res9: Boolean = false
|

|
Re: equals in case class does not seem to be working...
IIRC, the way that immutable accessors are generated is by simply defaulting to the "val" prefix on all constructor args. In this case, by explicitly using "var" you override this default behaviour, so that the mutable accessors will be generated instead of the immutable defaults, not in addition to them.
Behind the scenes, val arg1 will be expanded to (in Java) class User { private String arg1;
void User(String arg1) { this.arg1 = arg1;
} String arg1() { return this.arg1; }
}Using a var does exactly the same, but also adds the method: void arg1_=(String arg1) {
this.arg1 = arg1;}
This isn't the exact encoding of the name (due to the = symbol), but that would just confuse the issue :) The reason your example fails is that the generated equality method on case classes only works with constructor parameters, so that arg1 will be compared ("with args" != "without args") and field1 is simply ignored. Of course, you can always override that as well, but doing so could cause a lot of other problems.
On Wed, Jul 8, 2009 at 7:40 AM, Alexandre <alexmsmartins@...> wrote:
Not really but thanks none the less.
I had that version at the beginning and only took the "var" from the
constructor's args as an experiment.
Besides, as Naftoli Gugenhem ponted out "Case classes define immutable
fields.as well as a generated equals method". In your example you are
also generating mutable fields. You're simply generating them in a
different place than me.
To try that for yourself just run the code of this shell session.
-------------------------------------------------------------------------------
scala> case class User(arg1:String) { var field1 = arg1
| def this() = {
| this("without args")
| }
| }
defined class User
scala> val u1 = User("with args")
u1: User = User(with args)
scala> val u2 = new User()
u2: User = User(without args)
scala> u2.field1 = "with args"
scala> u1 == u2
res0: Boolean = false
scala> u1.field1 == u2.field1
res1: Boolean = true
On Wed, Jul 8, 2009 at 6:50 AM, Kevin
Wright< kev.lee.wright@...> wrote:
> You'd have more luck in making the constructor args into vars:
> case class User(
> var userName : String,
> var password : String,
> var firstName : String,
> var lastName : String,
> var emailAddress : String ) extends DataModel
> {
> def this() = ...
> ...
> ...
> }
>
> Not tried this myself on case classes, but can't think of any reason why it
> wouldn't work :)
>
> On Wed, Jul 8, 2009 at 2:51 AM, Naftoli Gugenhem < naftoligug@...>
> wrote:
>>
>> Case classes define immutable fields, as well as a generated equals
>> method. The fact that you defined additional mutable fields does not change
>> the generated equals method.
>>
>> -------------------------------------
>> Alexandre< alexmsmartins@...> wrote:
>>
>> Hi to you all
>>
>> First of all I'm a less than a year scala hobbyist (e. g. user) and
>> this is my first post to this list. So bare with me. :)
>> I was trying to do unit testing and I thought that using
>> assertEquals() in Junit 4 with objects from the same case class with
>> exactly the same content wold cut it. Yet, I run into some troubles
>> and ended up doing this small snippet in the scala shell that shows me
>> it does not work.
>> Am I doing anything wrong or is this to be expected?
>> If it is to be expected could you explain/point to some reading that
>> makes it clear why.
>>
>> Thanks in advance
>> Alexandre Martins
>>
>>
>> ------------------------------------------------------------------------------------------
>>
>> scala> import scala.xml.Node
>> import scala.xml.Node
>>
>> scala> import scala.xml.Elem
>> import scala.xml.Elem
>>
>> scala> import scala.reflect.BeanProperty
>> import scala.reflect.BeanProperty
>>
>> scala> import scala.reflect.BeanProperty
>> import scala.reflect.BeanProperty
>>
>> scala> import java.util.Date
>> import java.util.Date
>>
>> scala> import scala.reflect.BeanInfo
>> import scala.reflect.BeanInfo
>>
>> scala> /**
>> | * Represents a user of WikiModels
>> | */
>>
>> scala> @BeanInfo
>> | case class User(usrName:String,
>> | pssword:String,
>> | frstName:String,
>> | lstName:String,
>> | mail:String ) extends DataModel {
>> |
>> | var userName = usrName
>> | var password = pssword
>> | var firstName = frstName
>> | var lastName = lstName
>> | var emailAddress = mail
>> |
>> | def this() = {
>> | this("","","","","")
>> | }
>> |
>> | /**
>> | * generates a XML representation of the user, excluding password
>> | * @return the XML representing the user
>> | */
>> | def toXML:Node =
>> | <user>
>> | <username>{userName}</username>
>> | <!--<password>{password}</password>-->
>> | <firstName>{firstName}</firstName>
>> | <lastName>{lastName}</lastName>
>> | <email>{emailAddress}</email>
>> | </user>
>> |
>> | def extractXML(xml:Node):Unit = {
>> | xml match {
>> | case <username>{contents}</username> =>
>> this.userName = contents.text
>> | case <firstName>{contents}</firstName> =>
>> this.firstName = contents.text
>> | case <lastName>{contents}</lastName> =>
>> this.lastName = contents.text
>> | case <email>{contents}</email> => this.emailAddress
>> = contents.text
>> | case <user>{c @ _ *}</user> => for (val child <- c)
>> extractXML(child)
>> | case _ => { }
>> | }
>> | }
>> | }
>> defined class User
>>
>> scala> var u = User("alex","alexp","Alexandre",
>> "Martins"," alexmsmartins@...")
>> u: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
>>
>> scala> var d = new User()
>> d: User = User(,,,,)
>>
>> scala> u == d
>> res2: Boolean = false
>>
>> scala> u
>> res3: User = User(alex,alexp,Alexandre,Martins, alexmsmartins@...)
>>
>> scala> d
>> res4: User = User(,,,,)
>>
>> scala> d.extractXML( u.toXML)
>>
>> scala> d.password = u.password
>>
>> scala> d.password
>> res18: String = alexpscala> d
>> res7: User = User(,,,,)
>>
>> scala> d.toXML == u.toXML
>> res8: Boolean = true
>>
>> scala> d == u
>> res9: Boolean = false
>
>
|

|
Re: equals in case class does not seem to be working...
Why did you expect this to work? Case classes are supposed to be immutable.
On Tue, Jul 7, 2009 at 10:39 PM, Alexandre <alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using assertEquals() in Junit 4 with objects from the same case class with exactly the same content wold cut it. Yet, I run into some troubles and ended up doing this small snippet in the scala shell that shows me
it does not work. Am I doing anything wrong or is this to be expected? If it is to be expected could you explain/point to some reading that makes it clear why.
Thanks in advance Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node import scala.xml.Node
scala> import scala.xml.Elem import scala.xml.Elem
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import java.util.Date import java.util.Date
scala> import scala.reflect.BeanInfo import scala.reflect.BeanInfo
scala> /** | * Represents a user of WikiModels | */
scala> @BeanInfo | case class User(usrName:String,
| pssword:String, | frstName:String, | lstName:String, | mail:String ) extends DataModel { | | var userName = usrName | var password = pssword
| var firstName = frstName | var lastName = lstName | var emailAddress = mail | | def this() = { | this("","","","","")
| } | | /** | * generates a XML representation of the user, excluding password | * @return the XML representing the user | */ | def toXML:Node = | <user>
| <username>{userName}</username> | <!--<password>{password}</password>--> | <firstName>{firstName}</firstName> | <lastName>{lastName}</lastName>
| <email>{emailAddress}</email> | </user> | | def extractXML(xml:Node):Unit = { | xml match { | case <username>{contents}</username> =>
this.userName = contents.text | case <firstName>{contents}</firstName> => this.firstName = contents.text | case <lastName>{contents}</lastName> => this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress = contents.text | case <user>{c @ _ *}</user> => for (val child <- c) extractXML(child) | case _ => { }
| } | } | } defined class User
scala> var u = User("alex","alexp","Alexandre", "Martins","alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> var d = new User() d: User = User(,,,,)
scala> u == d res2: Boolean = false
scala> u res3: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> d res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password res18: String = alexpscala> d res7: User = User(,,,,)
scala> d.toXML == u.toXML res8: Boolean = true
scala> d == u res9: Boolean = false
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
|

|
Re: equals in case class does not seem to be working...
Agreed, it's a horrible thing to do, it *might* break pattern matching optimisations and any number of design contracts. On the other hand, if it does work, then we have a convenient shortcut to auto-generate extractors and equality methods on mutable objects.
On Wed, Jul 8, 2009 at 4:17 PM, Daniel Sobral <dcsobral@...> wrote:
Why did you expect this to work? Case classes are supposed to be immutable.
On Tue, Jul 7, 2009 at 10:39 PM, Alexandre <alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using assertEquals() in Junit 4 with objects from the same case class with exactly the same content wold cut it. Yet, I run into some troubles and ended up doing this small snippet in the scala shell that shows me
it does not work. Am I doing anything wrong or is this to be expected? If it is to be expected could you explain/point to some reading that makes it clear why.
Thanks in advance Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node import scala.xml.Node
scala> import scala.xml.Elem import scala.xml.Elem
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import java.util.Date import java.util.Date
scala> import scala.reflect.BeanInfo import scala.reflect.BeanInfo
scala> /** | * Represents a user of WikiModels | */
scala> @BeanInfo | case class User(usrName:String,
| pssword:String, | frstName:String, | lstName:String, | mail:String ) extends DataModel { | | var userName = usrName | var password = pssword
| var firstName = frstName | var lastName = lstName | var emailAddress = mail | | def this() = { | this("","","","","")
| } | | /** | * generates a XML representation of the user, excluding password | * @return the XML representing the user | */ | def toXML:Node = | <user>
| <username>{userName}</username> | <!--<password>{password}</password>--> | <firstName>{firstName}</firstName> | <lastName>{lastName}</lastName>
| <email>{emailAddress}</email> | </user> | | def extractXML(xml:Node):Unit = { | xml match { | case <username>{contents}</username> =>
this.userName = contents.text | case <firstName>{contents}</firstName> => this.firstName = contents.text | case <lastName>{contents}</lastName> => this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress = contents.text | case <user>{c @ _ *}</user> => for (val child <- c) extractXML(child) | case _ => { }
| } | } | } defined class User
scala> var u = User("alex","alexp","Alexandre", "Martins","alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> var d = new User() d: User = User(,,,,)
scala> u == d res2: Boolean = false
scala> u res3: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> d res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password res18: String = alexpscala> d res7: User = User(,,,,)
scala> d.toXML == u.toXML res8: Boolean = true
scala> d == u res9: Boolean = false
-- Daniel C. Sobral
Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
|

|
Re: equals in case class does not seem to be working...
On Wed, Jul 08, 2009 at 04:29:40PM +0100, Kevin Wright wrote:
> On the other hand, if it does work, then we have a convenient shortcut
> to auto-generate extractors and equality methods on mutable objects.
I don't know what's supposed to be happening in the original code, but
are case classes not a short enough shortcut for you?
scala> case class Foo(var x: Int, var y: Int)
defined class Foo
scala> val x = Foo(5,10)
x: Foo = Foo(5,10)
scala> val y = Foo(5,10)
y: Foo = Foo(5,10)
scala> x == y
res0: Boolean = true
scala> x.y = 20
scala> x == y
res1: Boolean = false
--
Paul Phillips | Every election is a sort of advance auction sale
Everyman | of stolen goods.
Empiricist | -- H. L. Mencken
pull his pi pal! |----------* http://www.improving.org/paulp/ *----------
|

|
Re: equals in case class does not seem to be working...
Let me expand a bit on this. Case classes serve a specific purpose, which is to implement algebraic types in an object-oriented way. To do this, a bunch of methods are automatically created, and just a little bit of magic compiler glue is applied. But for those methods to work, some assumptions have to be made, and making parts of the case class mutable violate those assumptions. See here, for instance:
scala> case class T(var i: Int) // Our mutable case class defined class T
scala> T(0) // One instance of it res4: T = T(0)
scala> res4.hashCode // It's hashcode res5: Int = 3444
scala> scala.collection.immutable.HashMap(res4 -> 'a) // Let's use it as a key res9: scala.collection.immutable.HashMap[T,Symbol] = Map(T(0) -> 'a)
scala> res9(res4) // Can we retrieve it? res10: Symbol = 'a
scala> res4.hashCode // Check the hashCode res11: Int = 3444
scala> res4.i = 1 // Let's mutate it
scala> res4.hashCode // How's the hashCode now? res12: Int = 3445
scala> res9(res4) // Can we retrieve it now? java.util.NoSuchElementException: key not found: T(1) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:9) at .<clinit>(<console>)
a...
scala> res9(T(0)) // Well, we changed T(0) into a T(1), so let's retrieve a T(0) java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177)
at scala.collection.immutable.HashMap.default(HashMap.scala:30) at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30)
at .<init>(<console>:9) at .<clinit>(<console>) a...
scala> T(0) // That didn't work. Let's make a new T(0) res15: T = T(0)
scala> res15.hashCode // Check hashCode... res16: Int = 3444
scala> res9(res15) // Can we get it? java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:10) at .<clinit>(<console>)
... scala> res9.keys foreach println // What _are_ the keys? <console>:9: warning: method keys in trait MapTemplate is deprecated: use `keysIterator' instead res9.keys foreach println
^ T(1)
scala>
On Wed, Jul 8, 2009 at 12:17 PM, Daniel Sobral <dcsobral@...> wrote:
Why did you expect this to work? Case classes are supposed to be immutable.
On Tue, Jul 7, 2009 at 10:39 PM, Alexandre <alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using assertEquals() in Junit 4 with objects from the same case class with exactly the same content wold cut it. Yet, I run into some troubles and ended up doing this small snippet in the scala shell that shows me
it does not work. Am I doing anything wrong or is this to be expected? If it is to be expected could you explain/point to some reading that makes it clear why.
Thanks in advance Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node import scala.xml.Node
scala> import scala.xml.Elem import scala.xml.Elem
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import java.util.Date import java.util.Date
scala> import scala.reflect.BeanInfo import scala.reflect.BeanInfo
scala> /** | * Represents a user of WikiModels | */
scala> @BeanInfo | case class User(usrName:String,
| pssword:String, | frstName:String, | lstName:String, | mail:String ) extends DataModel { | | var userName = usrName | var password = pssword
| var firstName = frstName | var lastName = lstName | var emailAddress = mail | | def this() = { | this("","","","","")
| } | | /** | * generates a XML representation of the user, excluding password | * @return the XML representing the user | */ | def toXML:Node = | <user>
| <username>{userName}</username> | <!--<password>{password}</password>--> | <firstName>{firstName}</firstName> | <lastName>{lastName}</lastName>
| <email>{emailAddress}</email> | </user> | | def extractXML(xml:Node):Unit = { | xml match { | case <username>{contents}</username> =>
this.userName = contents.text | case <firstName>{contents}</firstName> => this.firstName = contents.text | case <lastName>{contents}</lastName> => this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress = contents.text | case <user>{c @ _ *}</user> => for (val child <- c) extractXML(child) | case _ => { }
| } | } | } defined class User
scala> var u = User("alex","alexp","Alexandre", "Martins","alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> var d = new User() d: User = User(,,,,)
scala> u == d res2: Boolean = false
scala> u res3: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> d res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password res18: String = alexpscala> d res7: User = User(,,,,)
scala> d.toXML == u.toXML res8: Boolean = true
scala> d == u res9: Boolean = false
-- Daniel C. Sobral
Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
|

|
Re: equals in case class does not seem to be working...
This isn't limited to subverted case classes, it's true of *any* mutable object in a hashmap (strictly speaking, it's only true of any object with a mutatable hashkey, so any mutability should be restricted to volatile uses - such as caching - and not used in hashing or equality tests)
So long as you steer away from hashmaps and other such issues, I still believe that there is a valid use case for generating equality and extractor boilerplate code in certain mutable objects.
On Wed, Jul 8, 2009 at 4:35 PM, Daniel Sobral <dcsobral@...> wrote:
Let me expand a bit on this. Case classes serve a specific purpose, which is to implement algebraic types in an object-oriented way. To do this, a bunch of methods are automatically created, and just a little bit of magic compiler glue is applied. But for those methods to work, some assumptions have to be made, and making parts of the case class mutable violate those assumptions. See here, for instance:
scala> case class T(var i: Int) // Our mutable case class defined class T
scala> T(0) // One instance of it res4: T = T(0)
scala> res4.hashCode // It's hashcode res5: Int = 3444
scala> scala.collection.immutable.HashMap(res4 -> 'a) // Let's use it as a key res9: scala.collection.immutable.HashMap[T,Symbol] = Map(T(0) -> 'a)
scala> res9(res4) // Can we retrieve it? res10: Symbol = 'a
scala> res4.hashCode // Check the hashCode res11: Int = 3444
scala> res4.i = 1 // Let's mutate it
scala> res4.hashCode // How's the hashCode now? res12: Int = 3445
scala> res9(res4) // Can we retrieve it now? java.util.NoSuchElementException: key not found: T(1) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:9) at .<clinit>(<console>)
a...
scala> res9(T(0)) // Well, we changed T(0) into a T(1), so let's retrieve a T(0) java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177)
at scala.collection.immutable.HashMap.default(HashMap.scala:30) at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30)
at .<init>(<console>:9) at .<clinit>(<console>) a...
scala> T(0) // That didn't work. Let's make a new T(0) res15: T = T(0)
scala> res15.hashCode // Check hashCode... res16: Int = 3444
scala> res9(res15) // Can we get it? java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:10) at .<clinit>(<console>)
... scala> res9.keys foreach println // What _are_ the keys? <console>:9: warning: method keys in trait MapTemplate is deprecated: use `keysIterator' instead res9.keys foreach println
^ T(1)
scala>
On Wed, Jul 8, 2009 at 12:17 PM, Daniel Sobral <dcsobral@...> wrote:
Why did you expect this to work? Case classes are supposed to be immutable.
On Tue, Jul 7, 2009 at 10:39 PM, Alexandre <alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :)
I was trying to do unit testing and I thought that using assertEquals() in Junit 4 with objects from the same case class with exactly the same content wold cut it. Yet, I run into some troubles and ended up doing this small snippet in the scala shell that shows me
it does not work. Am I doing anything wrong or is this to be expected? If it is to be expected could you explain/point to some reading that makes it clear why.
Thanks in advance Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node import scala.xml.Node
scala> import scala.xml.Elem import scala.xml.Elem
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import java.util.Date import java.util.Date
scala> import scala.reflect.BeanInfo import scala.reflect.BeanInfo
scala> /** | * Represents a user of WikiModels | */
scala> @BeanInfo | case class User(usrName:String,
| pssword:String, | frstName:String, | lstName:String, | mail:String ) extends DataModel { | | var userName = usrName | var password = pssword
| var firstName = frstName | var lastName = lstName | var emailAddress = mail | | def this() = { | this("","","","","")
| } | | /** | * generates a XML representation of the user, excluding password | * @return the XML representing the user | */ | def toXML:Node = | <user>
| <username>{userName}</username> | <!--<password>{password}</password>--> | <firstName>{firstName}</firstName> | <lastName>{lastName}</lastName>
| <email>{emailAddress}</email> | </user> | | def extractXML(xml:Node):Unit = { | xml match { | case <username>{contents}</username> =>
this.userName = contents.text | case <firstName>{contents}</firstName> => this.firstName = contents.text | case <lastName>{contents}</lastName> => this.lastName = contents.text
| case <email>{contents}</email> => this.emailAddress = contents.text | case <user>{c @ _ *}</user> => for (val child <- c) extractXML(child) | case _ => { }
| } | } | } defined class User
scala> var u = User("alex","alexp","Alexandre", "Martins","alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> var d = new User() d: User = User(,,,,)
scala> u == d res2: Boolean = false
scala> u res3: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> d res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password res18: String = alexpscala> d res7: User = User(,,,,)
scala> d.toXML == u.toXML res8: Boolean = true
scala> d == u res9: Boolean = false
-- Daniel C. Sobral
Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
|

|
Re: equals in case class does not seem to be working...
Well, just don't expect the auto-generated methods to work.
On Wed, Jul 8, 2009 at 12:52 PM, Kevin Wright <kev.lee.wright@...> wrote:
This isn't limited to subverted case classes, it's true of *any* mutable object in a hashmap (strictly speaking, it's only true of any object with a mutatable hashkey, so any mutability should be restricted to volatile uses - such as caching - and not used in hashing or equality tests)
So long as you steer away from hashmaps and other such issues, I still believe that there is a valid use case for generating equality and extractor boilerplate code in certain mutable objects.
On Wed, Jul 8, 2009 at 4:35 PM, Daniel Sobral <dcsobral@...> wrote:
Let me expand a bit on this. Case classes serve a specific purpose, which is to implement algebraic types in an object-oriented way. To do this, a bunch of methods are automatically created, and just a little bit of magic compiler glue is applied. But for those methods to work, some assumptions have to be made, and making parts of the case class mutable violate those assumptions. See here, for instance:
scala> case class T(var i: Int) // Our mutable case class defined class T
scala> T(0) // One instance of it res4: T = T(0)
scala> res4.hashCode // It's hashcode res5: Int = 3444
scala> scala.collection.immutable.HashMap(res4 -> 'a) // Let's use it as a key res9: scala.collection.immutable.HashMap[T,Symbol] = Map(T(0) -> 'a)
scala> res9(res4) // Can we retrieve it? res10: Symbol = 'a
scala> res4.hashCode // Check the hashCode res11: Int = 3444
scala> res4.i = 1 // Let's mutate it
scala> res4.hashCode // How's the hashCode now? res12: Int = 3445
scala> res9(res4) // Can we retrieve it now? java.util.NoSuchElementException: key not found: T(1) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:9) at .<clinit>(<console>)
a...
scala> res9(T(0)) // Well, we changed T(0) into a T(1), so let's retrieve a T(0) java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177)
at scala.collection.immutable.HashMap.default(HashMap.scala:30) at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30)
at .<init>(<console>:9) at .<clinit>(<console>) a...
scala> T(0) // That didn't work. Let's make a new T(0) res15: T = T(0)
scala> res15.hashCode // Check hashCode... res16: Int = 3444
scala> res9(res15) // Can we get it? java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:10) at .<clinit>(<console>)
... scala> res9.keys foreach println // What _are_ the keys? <console>:9: warning: method keys in trait MapTemplate is deprecated: use `keysIterator' instead res9.keys foreach println
^ T(1)
scala>
On Wed, Jul 8, 2009 at 12:17 PM, Daniel Sobral <dcsobral@...> wrote:
Why did you expect this to work? Case classes are supposed to be immutable.
On Tue, Jul 7, 2009 at 10:39 PM, Alexandre <alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :) I was trying to do unit testing and I thought that using assertEquals() in Junit 4 with objects from the same case class with exactly the same content wold cut it. Yet, I run into some troubles
and ended up doing this small snippet in the scala shell that shows me it does not work. Am I doing anything wrong or is this to be expected? If it is to be expected could you explain/point to some reading that
makes it clear why.
Thanks in advance Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node import scala.xml.Node
scala> import scala.xml.Elem import scala.xml.Elem
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import java.util.Date import java.util.Date
scala> import scala.reflect.BeanInfo import scala.reflect.BeanInfo
scala> /** | * Represents a user of WikiModels | */
scala> @BeanInfo | case class User(usrName:String, | pssword:String, | frstName:String, | lstName:String, | mail:String ) extends DataModel {
| | var userName = usrName | var password = pssword | var firstName = frstName | var lastName = lstName | var emailAddress = mail | | def this() = {
| this("","","","","") | } | | /** | * generates a XML representation of the user, excluding password | * @return the XML representing the user
| */ | def toXML:Node = | <user> | <username>{userName}</username> | <!--<password>{password}</password>--> | <firstName>{firstName}</firstName>
| <lastName>{lastName}</lastName> | <email>{emailAddress}</email> | </user> | | def extractXML(xml:Node):Unit = { | xml match {
| case <username>{contents}</username> => this.userName = contents.text | case <firstName>{contents}</firstName> => this.firstName = contents.text | case <lastName>{contents}</lastName> =>
this.lastName = contents.text | case <email>{contents}</email> => this.emailAddress = contents.text | case <user>{c @ _ *}</user> => for (val child <- c)
extractXML(child) | case _ => { } | } | } | } defined class User
scala> var u = User("alex","alexp","Alexandre", "Martins","alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> var d = new User() d: User = User(,,,,)
scala> u == d res2: Boolean = false
scala> u res3: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> d res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password res18: String = alexpscala> d res7: User = User(,,,,)
scala> d.toXML == u.toXML res8: Boolean = true
scala> d == u res9: Boolean = false
-- Daniel C. Sobral
Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
|

|
Re: equals in case class does not seem to be working...
I don't!  On Wed, Jul 8, 2009 at 5:04 PM, Daniel Sobral <dcsobral@...> wrote:
Well, just don't expect the auto-generated methods to work.
On Wed, Jul 8, 2009 at 12:52 PM, Kevin Wright <kev.lee.wright@...> wrote:
This isn't limited to subverted case classes, it's true of *any* mutable object in a hashmap
(strictly speaking, it's only true of any object with a mutatable hashkey, so any mutability should be restricted to volatile uses - such as caching - and not used in hashing or equality tests)
So long as you steer away from hashmaps and other such issues, I still believe that there is a valid use case for generating equality and extractor boilerplate code in certain mutable objects.
On Wed, Jul 8, 2009 at 4:35 PM, Daniel Sobral <dcsobral@...> wrote:
Let me expand a bit on this. Case classes serve a specific purpose, which is to implement algebraic types in an object-oriented way. To do this, a bunch of methods are automatically created, and just a little bit of magic compiler glue is applied. But for those methods to work, some assumptions have to be made, and making parts of the case class mutable violate those assumptions. See here, for instance:
scala> case class T(var i: Int) // Our mutable case class defined class T
scala> T(0) // One instance of it res4: T = T(0)
scala> res4.hashCode // It's hashcode res5: Int = 3444
scala> scala.collection.immutable.HashMap(res4 -> 'a) // Let's use it as a key res9: scala.collection.immutable.HashMap[T,Symbol] = Map(T(0) -> 'a)
scala> res9(res4) // Can we retrieve it? res10: Symbol = 'a
scala> res4.hashCode // Check the hashCode res11: Int = 3444
scala> res4.i = 1 // Let's mutate it
scala> res4.hashCode // How's the hashCode now? res12: Int = 3445
scala> res9(res4) // Can we retrieve it now? java.util.NoSuchElementException: key not found: T(1) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:9) at .<clinit>(<console>)
a...
scala> res9(T(0)) // Well, we changed T(0) into a T(1), so let's retrieve a T(0) java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177)
at scala.collection.immutable.HashMap.default(HashMap.scala:30) at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30)
at .<init>(<console>:9) at .<clinit>(<console>) a...
scala> T(0) // That didn't work. Let's make a new T(0) res15: T = T(0)
scala> res15.hashCode // Check hashCode... res16: Int = 3444
scala> res9(res15) // Can we get it? java.util.NoSuchElementException: key not found: T(0) at scala.collection.generic.MapTemplate$class.default(MapTemplate.scala:177) at scala.collection.immutable.HashMap.default(HashMap.scala:30)
at scala.collection.generic.MapTemplate$class.apply(MapTemplate.scala:92) at scala.collection.immutable.HashMap.apply(HashMap.scala:30) at .<init>(<console>:10) at .<clinit>(<console>)
... scala> res9.keys foreach println // What _are_ the keys? <console>:9: warning: method keys in trait MapTemplate is deprecated: use `keysIterator' instead res9.keys foreach println
^ T(1)
scala>
On Wed, Jul 8, 2009 at 12:17 PM, Daniel Sobral <dcsobral@...> wrote:
Why did you expect this to work? Case classes are supposed to be immutable.
On Tue, Jul 7, 2009 at 10:39 PM, Alexandre <alexmsmartins@...> wrote:
Hi to you all
First of all I'm a less than a year scala hobbyist (e. g. user) and
this is my first post to this list. So bare with me. :) I was trying to do unit testing and I thought that using assertEquals() in Junit 4 with objects from the same case class with exactly the same content wold cut it. Yet, I run into some troubles
and ended up doing this small snippet in the scala shell that shows me it does not work. Am I doing anything wrong or is this to be expected? If it is to be expected could you explain/point to some reading that
makes it clear why.
Thanks in advance Alexandre Martins
------------------------------------------------------------------------------------------
scala> import scala.xml.Node import scala.xml.Node
scala> import scala.xml.Elem import scala.xml.Elem
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import scala.reflect.BeanProperty import scala.reflect.BeanProperty
scala> import java.util.Date import java.util.Date
scala> import scala.reflect.BeanInfo import scala.reflect.BeanInfo
scala> /** | * Represents a user of WikiModels | */
scala> @BeanInfo | case class User(usrName:String, | pssword:String, | frstName:String, | lstName:String, | mail:String ) extends DataModel {
| | var userName = usrName | var password = pssword | var firstName = frstName | var lastName = lstName | var emailAddress = mail | | def this() = {
| this("","","","","") | } | | /** | * generates a XML representation of the user, excluding password | * @return the XML representing the user
| */ | def toXML:Node = | <user> | <username>{userName}</username> | <!--<password>{password}</password>--> | <firstName>{firstName}</firstName>
| <lastName>{lastName}</lastName> | <email>{emailAddress}</email> | </user> | | def extractXML(xml:Node):Unit = { | xml match {
| case <username>{contents}</username> => this.userName = contents.text | case <firstName>{contents}</firstName> => this.firstName = contents.text | case <lastName>{contents}</lastName> =>
this.lastName = contents.text | case <email>{contents}</email> => this.emailAddress = contents.text | case <user>{c @ _ *}</user> => for (val child <- c)
extractXML(child) | case _ => { } | } | } | } defined class User
scala> var u = User("alex","alexp","Alexandre", "Martins","alexmsmartins@...")
u: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> var d = new User() d: User = User(,,,,)
scala> u == d res2: Boolean = false
scala> u res3: User = User(alex,alexp,Alexandre,Martins,alexmsmartins@...)
scala> d res4: User = User(,,,,)
scala> d.extractXML( u.toXML)
scala> d.password = u.password
scala> d.password res18: String = alexpscala> d res7: User = User(,,,,)
scala> d.toXML == u.toXML res8: Boolean = true
scala> d == u res9: Boolean = false
-- Daniel C. Sobral
Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
-- Daniel C. Sobral Something I learned in academia: there are three kinds of academic reviews: review by name, review by reference and review by value.
|