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

Re: equality for accessors and mutators

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

On Tue, 03 Feb 2009 22:28:15 -0800, Paul Phillips wrote:

> 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.

In that case you are back to the same problem testing the equality of two
functions (the internal 'fn' in this case), and so back to the start
again.

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 3 days ago.
Re: Re: equality for accessors and mutators
You can combine Ben and Paul's solutions...

  class Accessor[T,V](val x:T, val name: String, fn:(T)=>V) extends AnyRef with Function0[V] {
    ...
    override def equals(other:Any) = other match {
      case y:Accessor[_,_] => x == y.x && name == y.name
      case _ => false
    }
   ...
  }
  class C(var a: Int)
  val c = new C(4567)
  val ax = Accessor(c, "a", (c:C) => c.a)

But it starts getting verbose (not to mention error-prone).

--j

On Tue, Feb 3, 2009 at 10:33 PM, Sam Stainsby <sam@sustainablesoftware.com.au> wrote:
On Tue, 03 Feb 2009 22:28:15 -0800, Paul Phillips wrote:

> 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.

In that case you are back to the same problem testing the equality of two
functions (the internal 'fn' in this case), and so back to the start
again.


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 22:39:30 -0800, Jorge Ortiz wrote:

> You can combine Ben and Paul's solutions...
...
> But it starts getting verbose (not to mention error-prone).

Yes, its the type-safety aspects of Scala that I want to preserve if
possible, otherwise I'd go back to python.

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 3 days ago.
Re: Re: equality for accessors and mutators
Another approach is to wrap your values inside something like a Ref.

  class Ref[T](private var x: T) {
    def get = x
    def set(y: T) { x = y }
  }

  class C {
    object x extends Ref(176)
  }

  val c = new C
  c.x.get
  c.x.set(12)

Instead of passing around the accessors and mutators, you pass around the object x directly, which has accessors and mutators. The security metadata, etc can all go inside the x object (or as a part of the Ref class).

If you don't like get/set, there alternatives:

  class Ref[T](private var x: T) {
    def apply() = x
    def update(y: T) { x = y }
  }
  ...
  c.x()
  c.x() = 12

Or even

  class Ref[T](private var x: T) {
    def ! = x
    def :=(y: T) { x = y }
  }
  ...
  c.x!
  c.x := 12

--j

On Tue, Feb 3, 2009 at 10:49 PM, Sam Stainsby <sam@sustainablesoftware.com.au> wrote:
On Tue, 03 Feb 2009 22:39:30 -0800, Jorge Ortiz wrote:

> You can combine Ben and Paul's solutions...
...
> But it starts getting verbose (not to mention error-prone).

Yes, its the type-safety aspects of Scala that I want to preserve if
possible, otherwise I'd go back to python.


Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 3 days ago.
Re: Re: equality for accessors and mutators
Also:

Lift uses something similar to this pattern in the ORM. To make the accessor look "normal", there's an implicit conversion from Ref[T] to T which just calls Ref[T]'s get method.

Then c.x will automatically turn into an Int if Int is the expected type. The mutator remains "ugly" tho.

I wouldn't recommend this approach as it is bug-prone. (For example 12 == c.x will return false no matter what x is, because == is defined in Any and takes an Any as the parameter, so the expected type will never be Int.)

--j

On Tue, Feb 3, 2009 at 11:29 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
Another approach is to wrap your values inside something like a Ref.

  class Ref[T](private var x: T) {
    def get = x
    def set(y: T) { x = y }
  }

  class C {
    object x extends Ref(176)
  }

  val c = new C
  c.x.get
  c.x.set(12)

Instead of passing around the accessors and mutators, you pass around the object x directly, which has accessors and mutators. The security metadata, etc can all go inside the x object (or as a part of the Ref class).

If you don't like get/set, there alternatives:

  class Ref[T](private var x: T) {
    def apply() = x
    def update(y: T) { x = y }
  }
  ...
  c.x()
  c.x() = 12

Or even

  class Ref[T](private var x: T) {
    def ! = x
    def :=(y: T) { x = y }
  }
  ...
  c.x!
  c.x := 12

--j

On Tue, Feb 3, 2009 at 10:49 PM, Sam Stainsby <sam@sustainablesoftware.com.au> wrote:
On Tue, 03 Feb 2009 22:39:30 -0800, Jorge Ortiz wrote:

> You can combine Ben and Paul's solutions...
...
> But it starts getting verbose (not to mention error-prone).

Yes, its the type-safety aspects of Scala that I want to preserve if
possible, otherwise I'd go back to python.



Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 3 days ago.
Re: Re: equality for accessors and mutators
Sorry, one more thing. Lift also uses this for security. For example, in the context of a web app, Ref.get can look at the current session, figure out which user is logged in, and decide whether that user has read privileges for that piece of data. The security policy is encoded once, when the object is defined, and then it becomes much harder to violate that policy accidentally elsewhere in your app, because the rest of your code doesn't need to check the security metadata to see whether it has access. If the code calls getUser("Fred").creditCardNumber.get, then Fred (and maybe Customer Service) will see XXXX-XXXX-XXXX-1234, but everyone else sees blank or all X's.

--j

On Tue, Feb 3, 2009 at 11:33 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
Also:

Lift uses something similar to this pattern in the ORM. To make the accessor look "normal", there's an implicit conversion from Ref[T] to T which just calls Ref[T]'s get method.

Then c.x will automatically turn into an Int if Int is the expected type. The mutator remains "ugly" tho.

I wouldn't recommend this approach as it is bug-prone. (For example 12 == c.x will return false no matter what x is, because == is defined in Any and takes an Any as the parameter, so the expected type will never be Int.)

--j

On Tue, Feb 3, 2009 at 11:29 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
Another approach is to wrap your values inside something like a Ref.

  class Ref[T](private var x: T) {
    def get = x
    def set(y: T) { x = y }
  }

  class C {
    object x extends Ref(176)
  }

  val c = new C
  c.x.get
  c.x.set(12)

Instead of passing around the accessors and mutators, you pass around the object x directly, which has accessors and mutators. The security metadata, etc can all go inside the x object (or as a part of the Ref class).

If you don't like get/set, there alternatives:

  class Ref[T](private var x: T) {
    def apply() = x
    def update(y: T) { x = y }
  }
  ...
  c.x()
  c.x() = 12

Or even

  class Ref[T](private var x: T) {
    def ! = x
    def :=(y: T) { x = y }
  }
  ...
  c.x!
  c.x := 12

--j

On Tue, Feb 3, 2009 at 10:49 PM, Sam Stainsby <sam@sustainablesoftware.com.au> wrote:
On Tue, 03 Feb 2009 22:39:30 -0800, Jorge Ortiz wrote:

> You can combine Ben and Paul's solutions...
...
> But it starts getting verbose (not to mention error-prone).

Yes, its the type-safety aspects of Scala that I want to preserve if
possible, otherwise I'd go back to python.




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

On Wed, 04 Feb 2009 00:03:38 -0800, Jorge Ortiz wrote:

> Sorry, one more thing. Lift also uses this for security. For example, in
> the context of a web app, Ref.get can look at the current session,
> figure out which user is logged in, and decide whether that user has
> read privileges for that piece of data. The security policy is encoded
> once, when the object is defined, and then it becomes much harder to
> violate that policy accidentally elsewhere in your app, because the rest
> of your code doesn't need to check the security metadata to see whether
> it has access. If the code calls getUser("Fred").creditCardNumber.get,
> then Fred (and maybe Customer Service) will see XXXX-XXXX-XXXX-1234, but
> everyone else sees blank or all X's.
On Wed, 04 Feb 2009 00:03:38 -0800, Jorge Ortiz wrote:

Now this is interesting because I'm trying to do something similar.
However, I'm using a pure OO database (DB4O) and so I am particularly
keen to avoid polluting my domain objects, since they will be stored
directly in the database. In case you haven't seen DB4O, you can do
things like this:

scala> class Person(var familyName:String, var givenName:String)
defined class Person

scala> val p1 = new Person("Bloggs", "Fred")
p1: Person = Person@1f95cce

scala> val db = Db4o.openFile("tmp.db")
db: com.db4o.ObjectContainer = tmp.db

scala> db.store(p1)

scala> db.query(new Predicate[Person]()
{
| override def `match`(p:Person) = p.familyName == "Bloggs"
| })
res6: com.db4o.ObjectSet[Person] = [Person@1f95cce]

Thus I want to layer my view framework over the 'pure' Person object.
In my framework, it would look something like this:

class PersonSchema extends FieldsViewSchema[Person] {
override def fieldViewSchemas = Array(
new SimpleFieldViewSchema[Person,String](
Name("family name"),
AccessorAction(_.familyName),
MutatorAction((_:Person).familyName = (_:String))
),
new SimpleFieldViewSchema[Person,String](
Name("given name"),
AccessorAction(_.givenName),
MutatorAction((_:Person).givenName = (_:String))
)
)
}

where AccessorAction looks like this:

case class AccessorAction[T,V](val accessor:(T) => V)
extends PermissionAction {
override def requiredPermissions = Set(Permission.VIEW)
}

MutatorAction is similar.

Probably some syntactic sugar would make this less verbose. The 'Name's
are just used for presentation (probably will be I18N keys at some
stage).
Will also add validation. Security-wise, this is all working, and
integrates with Apache Wicket's security. You can create simple Wicket
forms like this for example:

val person = new Person("", "")
val schema = new PersonSchema()
val form = new SimpleFieldsForm[Person](id, schema, person)

where 'form' is a Wicket form.

By slicing and dicing schemas, in theory you can create all sorts of
views flexibly. To do that you need keys to pick them out. You could use
the name as others have suggested and that had already occurred to me.
If possible though, I would prefer a type-safe key which should be the
accessor or mutator itself. Hence the function equality problem.

Maybe there is a way to use Refs and trick the database into storing pure
object - I will have to think about it. Thanks for the ideas.

Cheers,
Sam.

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