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

equality for accessors and mutators

7 replies
Sam Stainsby
Joined: 2009-01-14,
User offline. Last seen 42 years 45 weeks ago.

I've run into a problem with 'equality' when exploring objects that act
as accessors and mutators for an object property ...

Given a class such as:

scala> class C(var a:Int)
defined class C

I needed a convenient way to pass around accessors and mutators for the
property 'a' and attach metadata to them (such as security metadata).
This is what I am using:

scala> val accessor:(C) => Int = _.a
accessor: (C) => Int =

scala> val mutator:(C,Int) => Any = _.a = _
mutator: (C, Int) => Any =

They work as expected:

scala> val c = new C(776)
c: C = C@a85268

scala> accessor(c)
res3: Int = 776

scala> mutator(c, 556)
res15: Any = ()

scala> c.a
res16: Int = 556

THis is all good. However, there comes a point when you want to do things
like use accessors in a map as keys, and so you need an equals
relation ... but:

scala> val accessor2:(C) => Int = _.a
accessor2: (C) => Int =

scala> accessor == accessor2
res4: Boolean = false

scala> accessor.equals(accessor2)
res5: Boolean = false

scala> accessor.eq(accessor2)
res5: Boolean = false

In fact:

scala> accessor.hashCode
res12: Int = 24670213

scala> accessor2.hashCode
res13: Int = 33505231

My mutator functions are similarly uncooperative.

Should equality work for these functions? Any ideas on how to go about
this, or do things differently to avoid the problem?

Cheers,
Sam.

Ben Jackman
Joined: 2009-02-04,
User offline. Last seen 42 years 45 weeks ago.
Re: equality for accessors and mutators

What about something like this (just a rough draft you would obviously want
to refine it):

//Helper object that uses manifests implicity to make typing easier, you can
drop the varType manifest
//It is a left over from the VarMutator object that I ended up not finishing
due to laziness
object VarAccessor {
import scala.reflect.Manifest
def apply[A,B](name : String)(implicit clz : Manifest[A], varType :
Manifest[B]) : VarAccessor[A,B] = new
VarAccessor[A,B](clz.erasure.asInstanceOf[Class[A]], name)
}

case class VarAccessor[A,B](val clz : Class[A], val name : String) {
import java.lang.reflect.Method
val meth = clz.getMethod(name)
def apply(a : A) : B = meth.invoke(a).asInstanceOf[B]
}

scala> val accessor = VarAccessor[C,Int]("a")
accessor: VarAccessor[C,Int] = VarAccessor(class C,a)

scala> val accessor2 = VarAccessor[C,Int]("a")
accessor2: VarAccessor[C,Int] = VarAccessor(class C,a)

scala> accessor == accessor2
res0: Boolean = true

scala> accessor(new C(34))
res8: Int = 34

also you could override apply to call directly on your object, thereby
increasing the speed by avoiding reflection if it is an issue for you. that
might mean making VarAccessor not a case class anymore (and writing your own
equals and hashCode for it), since i am not sure if == holds between a
subclass of a case class and the case class itself.

here is the mutator, note the name + "_$eq" that is how scala makes the
setters look when reflected

case class VarMutator[A,B](val clz : Class[A], val clzB : Class[B], val name
: String) {
import java.lang.reflect.Method
val meth = clz.getMethod(name+"_$eq", clzB)
def apply(obj : A, newVal : B) : Unit = meth.invoke(obj,
newVal.asInstanceOf[AnyRef])
}

scala> val x = new C(3)
x: C = C@75a30f

scala> x.a
res4: Int = 3

scala> mutator(x,23)

scala> x.a
res6: Int = 23

scala> accessor(x)
res7: Int = 23

Good Luck,
Ben

Sam Stainsby-2 wrote:
>
> I've run into a problem with 'equality' when exploring objects that act
> as accessors and mutators for an object property ...
>
> Given a class such as:
>
> scala> class C(var a:Int)
> defined class C
>
> I needed a convenient way to pass around accessors and mutators for the
> property 'a' and attach metadata to them (such as security metadata).
> This is what I am using:
>
> scala> val accessor:(C) => Int = _.a
> accessor: (C) => Int =
>
> scala> val mutator:(C,Int) => Any = _.a = _
> mutator: (C, Int) => Any =
>
> They work as expected:
>
> scala> val c = new C(776)
> c: C = C@a85268
>
> scala> accessor(c)
> res3: Int = 776
>
> scala> mutator(c, 556)
> res15: Any = ()
>
> scala> c.a
> res16: Int = 556
>
> THis is all good. However, there comes a point when you want to do things
> like use accessors in a map as keys, and so you need an equals
> relation ... but:
>
> scala> val accessor2:(C) => Int = _.a
> accessor2: (C) => Int =
>
> scala> accessor == accessor2
> res4: Boolean = false
>
> scala> accessor.equals(accessor2)
> res5: Boolean = false
>
> scala> accessor.eq(accessor2)
> res5: Boolean = false
>
> In fact:
>
> scala> accessor.hashCode
> res12: Int = 24670213
>
> scala> accessor2.hashCode
> res13: Int = 33505231
>
> My mutator functions are similarly uncooperative.
>
> Should equality work for these functions? Any ideas on how to go about
> this, or do things differently to avoid the problem?
>
> Cheers,
> Sam.
>
>
>

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: equality for accessors and mutators

On Wed, Feb 04, 2009 at 12:36:24AM +0000, Sam Stainsby wrote:
> scala> val accessor:(C) => Int = _.a
> accessor: (C) => Int =

This creates an anonymous function object.

> scala> val accessor2:(C) => Int = _.a
> accessor2: (C) => Int =

As does this.

> scala> accessor == accessor2
> res4: Boolean = false

Right, they're different objects. Since you haven't overridden it, you're using the equals method from java.lang.Object, which is
the same as object equality.

You can either make sure they're the same object, or redefine equals. I'm not quite sure what you want equality to mean here, so I
made up an example where accessors defined through this interface are equal if they're accessing the same object, regardless of
whether they're providing "naive" access or messing with the value on the way through (because who doesn't like to have their
accessors arbitrarily double things once in a while.) I'll leave mutators as an exercise.

object o
{
class C(var a: Int)
object C {
def mkAccessor(x: C, fn: () => Int) = new Accessor(x, fn)

class Accessor(val x: C, fn: () => Int) extends AnyRef with Function0[Int] {
def apply() = fn()
override def hashCode() = x.hashCode
override def equals(other: Any) = other match {
case y: Accessor => x == y.x
case _ => false
}
}
}

def main(args: Array[String]): Unit = {
import C._

val c = new C(776)
val acc = mkAccessor(c, c.a _)
val acc2 = mkAccessor(c, () => c.a * 2)

println("acc == " + acc())
println("acc2 == " + acc2())
println("acc == acc2? " + (acc == acc2))

val acc3 = mkAccessor(new C(511), c.a _)
println("acc == acc3? " + (acc == acc3))
}
}

And when I run it:

$ scala o
acc == 776
acc2 == 1552
acc == acc2? true
acc == acc3? false

Sam Stainsby
Joined: 2009-01-14,
User offline. Last seen 42 years 45 weeks ago.
Re: equality for accessors and mutators

On Tue, 03 Feb 2009 20:31:22 -0800, Paul Phillips wrote:

> You can either make sure they're the same object, or redefine equals.

The latter is the only acceptable solution for my case.

> I'm not quite sure what you want equality to mean here,

When we say an object has an "accessor" for a particular property, that
means we think of it a one thing. I'm trying to isolate that thing and
make it into an object.

> so I made up an
> example where accessors defined through this interface are equal if
> they're accessing the same object, regardless of whether they're
> providing "naive" access or messing with the value on the way through
> (because who doesn't like to have their accessors arbitrarily double
> things once in a while.) I'll leave mutators as an exercise.

Thanks, I was starting to think along these lines after posting, and
it seems it will provide the solution I need. I'm still interest to know
whether some sort of generic equals method is appropriate for functions
in the Scala library.

But anyway, the generic version of your example is:

class Accessor[T,V](val x:T, fn:(T)=>V) extends AnyRef with Function0[V]
{
def apply() = fn(x)
override def hashCode() = x.hashCode
override def equals(other:Any) = other match {
case y:Accessor[_,_] => x == y.x
case _ => false
}
}

object Accessor {
def apply[T,V](x:T, fn:(T)=>V) = new Accessor[T,V](x, fn)
}

Testing this out:

scala> class C(var
a:Int)
defined class C

scala> val c = new C(4567)
c: C = C@930120

scala> val ax = Accessor(c, (c:C) => c.a)
ax: Accessor[C,Int] =

scala> ax()
res0: Int = 4567

scala> val ax2 = Accessor(c, (c:C) => c.a)
ax2: Accessor[C,Int] =

scala> ax == ax2
res2: Boolean = true

So all good, thanks! I'm sure mutators will follow along the same lines.
Will post when I have it.

Cheers,
Sam.

Sam Stainsby
Joined: 2009-01-14,
User offline. Last seen 42 years 45 weeks ago.
Re: equality for accessors and mutators

On Tue, 03 Feb 2009 20:31:22 -0800, Paul Phillips wrote:

> override def hashCode() = x.hashCode
> override def equals(other: Any) = other match {
> case y: Accessor => x == y.x
> case _ => false
> }

Ah, just noticed a problem there. The condition for 'equals' is too
general, is it not. It equates all accessors of an object.

Sam Stainsby
Joined: 2009-01-14,
User offline. Last seen 42 years 45 weeks ago.
Re: equality for accessors and mutators

On Tue, 03 Feb 2009 19:11:28 -0800, Ben Jackman wrote:

> What about something like this (just a rough draft you would obviously
> want to refine it):
>
> //Helper object that uses manifests implicity to make typing easier, you
> can drop the varType manifest
> //It is a left over from the VarMutator object that I ended up not
> finishing due to laziness
> object VarAccessor {
> import scala.reflect.Manifest
> def apply[A,B](name : String)(implicit clz : Manifest[A],
varType :
> Manifest[B]) : VarAccessor[A,B] = new
> VarAccessor[A,B](clz.erasure.asInstanceOf[Class[A]], name) }
>
> case class VarAccessor[A,B](val clz : Class[A], val name : String) {
> import java.lang.reflect.Method
> val meth = clz.getMethod(name)
> def apply(a : A) : B = meth.invoke(a).asInstanceOf[B]
> }

Thanks for your help Ben, but I'm trying to avoid reflection if possible.

Cheers,
Sam.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: equality for accessors and mutators

On Wed, Feb 04, 2009 at 06:04:05AM +0000, Sam Stainsby wrote:
> Thanks, I was starting to think along these lines after posting, and
> it seems it will provide the solution I need. I'm still interest to know
> whether some sort of generic equals method is appropriate for functions
> in the Scala library.

There is no general way to say that two functions are equal based on anything but identity; it's impossible without constraining the
problem way past the point where a generic solution will be useful.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: equality for accessors and mutators

On Wed, Feb 04, 2009 at 06:10:24AM +0000, Sam Stainsby wrote:
> Ah, just noticed a problem there. The condition for 'equals' is too
> general, is it not. It equates all accessors of an object.

That was intentional, to illustrate that you can use whatever equality logic you want.

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