- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Re: equality for accessors and mutators
Wed, 2009-02-04, 07:34
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.
Wed, 2009-02-04, 08:07
#2
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.
Wed, 2009-02-04, 08:37
#3
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:
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.
Wed, 2009-02-04, 08:47
#4
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:
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.
Wed, 2009-02-04, 09:07
#5
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:
--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.
Wed, 2009-02-04, 10:27
#6
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.
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: