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

universal access principle, mutable properties and ValueHolder type objects

8 replies
James.Strachan
Joined: 2009-07-08,
User offline. Last seen 2 years 25 weeks ago.

There are lots of use cases where a value isn't really just a value,
there is some kind of Value Holder object in there to manage its
creation/access/update. For example to deal with lazy creation of
mutable properties; detecting when things change using dirty flags for
persistence or whatever.

This only really applies to mutable values - lazy val works great for
immutable values (and we all know immutable rocks :).

So ValueHolder objects are cool and all but they seem to break the
universal access principle for fields/methods.

e.g.

Imagine we want a mutable property which when its accessed is lazily
created using some default value function - otherwise it can be set by
the user. So we use some ValueHolder thingy (there's a few already in
Lift - here's a crude simple example - like mentioned in this
thiread...)
http://old.nabble.com/Request-for-addition-to-the-standard-library%3A-La...

class Lazy[T](thunk: => T) {
private var _value: T = _
private var _evaluated: Boolean = false

def evaluated = _evaluated

def value: T = {
if (!_evaluated) {
_value = thunk
_evaluated = true
}
_value
}

def set(value: T): Unit = _value = value

def apply(): T = value
def apply(v: T): Unit = set(v)
}

now lets use it

class Cheese {
val numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
}

So we can do things like

val c1 = new Cheese()

// lets access it, it'll be lazily created
c1.numberFormat()

val c2 = new Cheese()

// lets set a value first before we access it
c2.numberFormat(someThing)

// lets access it, someThing will be returned
ch.numberFormat()

so we can make the property look like a method call; we can support
"ch.numberFormat() = value" or "ch.numberFormat(value)" for
assignment, we can have nested methods/values to get/set the value
(ch.numberFormat.value or ch.numberFormat.set = something).

However if we want the property to look & act like a regular var/val
then we have to wrap it and make up a new name for a private val....

class Cheese {
private val _numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
def numberFormat = _numberFormat()
def numberformat_(value: NumberFormat) = _numberFormat(value)
}

So it works nicely - we can hide the implementation detail of the
mutable property (Lazy) from the public mutable value of type
NumberFormat; its just a noisy bit of code.

I just wondered if anyone had any wacky tricks for avoiding these 3
boiler plate lines? (or maybe I've missed something obvious :) Its
almost like we could do with something like (pseudo-code here)

class Cheese {
delegate var numberFormat : NumberFormat = new
Lazy({NumberFormat.getNumberInstance(locale)})
}

where "delegate" indicates that the right expression is the object
used to delegate to for the get/set operations rather than just using
a naked field of type "NumberFormat"; so that the code generated
getter/setter methods should do a get or set on the delegate value
holder object. (I guess reusing "lazy var" instead of "delegate"
would avoid another keyword).

I guess having to write the 3 lines of boilerplate to keep the
universal access principle isn't the end of the world (we can also
force the value object into the public APIi); I just wondered if we
could do better.

Thoughts?

Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: universal access principle, mutable properties and ValueHo
Don't know your use-case (if you need to refer to the holder objects and they are stable or not), but something like the following might work for you?

object Lazy{
   implicit def unbox[T](value : Lazy[T]) : T = value.apply
   implicit def box[T](value : => T) : Lazy[T] = apply(value)
   def apply[T](value : => T) : Lazy[T] = new Lazy[T] { lazy val apply = value }
}

trait Lazy[T] {
  def apply() : T
}

import Lazy._

class Foo {
  var name = Lazy(System.currentTimeMillis.toString)
}

val foo = new Foo

val name : String = foo.name
val name = foo.name()
foo.name = "bar"

On Thu, Jan 21, 2010 at 1:01 PM, James Strachan <james.strachan@gmail.com> wrote:
There are lots of use cases where a value isn't really just a value,
there is some kind of Value Holder object in there to manage its
creation/access/update. For example to deal with lazy creation of
mutable properties; detecting when things change using dirty flags for
persistence or whatever.

This only really applies to mutable values - lazy val works great for
immutable values (and we all know immutable rocks :).

So ValueHolder objects are cool and all but they seem to break the
universal access principle for fields/methods.

e.g.

Imagine we want a mutable property which when its accessed is lazily
created using some default value function - otherwise it can be set by
the user. So we use some ValueHolder thingy (there's a few already in
Lift - here's a crude simple example - like mentioned in this
thiread...)
http://old.nabble.com/Request-for-addition-to-the-standard-library%3A-Laziness-p8640806.html

class Lazy[T](thunk: => T) {
 private var _value: T = _
 private var _evaluated: Boolean = false

 def evaluated = _evaluated

 def value: T = {
   if (!_evaluated) {
     _value = thunk
     _evaluated = true
   }
   _value
 }

 def set(value: T): Unit =  _value = value

 def apply(): T = value
 def apply(v: T): Unit = set(v)
}


now lets use it

class Cheese {
 val numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
}


So we can do things like

val c1 = new Cheese()

// lets access it, it'll be lazily created
c1.numberFormat()

val c2 = new Cheese()

// lets set a value first before we access it
c2.numberFormat(someThing)

// lets access it, someThing will be returned
ch.numberFormat()


so we can make the property look like a method call; we can support
"ch.numberFormat() = value" or "ch.numberFormat(value)" for
assignment, we can have nested methods/values to get/set the value
(ch.numberFormat.value or ch.numberFormat.set = something).


However if we want the property to look & act like a regular var/val
then we have to wrap it and make up a new name for a private val....

class Cheese {
 private val _numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
 def numberFormat = _numberFormat()
 def numberformat_(value: NumberFormat) = _numberFormat(value)
}

So it works nicely - we can hide the implementation detail of the
mutable property (Lazy) from the public mutable value of type
NumberFormat; its just a noisy bit of code.

I just wondered if anyone had any wacky tricks for avoiding these 3
boiler plate lines? (or maybe I've missed something obvious :) Its
almost like we could do with something like (pseudo-code here)

class Cheese {
 delegate var numberFormat : NumberFormat = new
Lazy({NumberFormat.getNumberInstance(locale)})
}

where "delegate" indicates that the right expression is the object
used to delegate to for the get/set operations rather than just using
a naked field of type "NumberFormat"; so that the code generated
getter/setter methods should do a get or set on the delegate value
holder object.  (I guess reusing "lazy var" instead of "delegate"
would avoid another keyword).

I guess having to write the 3 lines of boilerplate to keep the
universal access principle isn't the end of the world (we can also
force the value object into the public APIi); I just wondered if we
could do better.

Thoughts?

--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://fusesource.com/



--
Viktor Klang
| "A complex system that works is invariably
| found to have evolved from a simple system
| that worked." - John Gall

Blog: klangism.blogspot.com
Twttr: twitter.com/viktorklang
Code: github.com/viktorklang
Kevin Wright
Joined: 2009-06-09,
User offline. Last seen 49 weeks 3 days ago.
Re: universal access principle, mutable properties and ValueHo
It looks like you need the autoproxy plugin!
http://www.artima.com/weblogs/viewpost.jsp?thread=275135http://www.artima.com/weblogs/viewpost.jsp?thread=275588 http://github.com/scala-incubator/autoproxy-plugin

The exact notation is @proxy (soon to be @mixin) and not `delegate`, but otherwise it's pretty much what you describe


Disclaimer: the thing is still pre-alpha, and has some problems when faced with singletons and a few other scenarios, but I do aim to have any issues resolved for the 2.8 Scala release.


2010/1/21 James Strachan <james.strachan@gmail.com>
There are lots of use cases where a value isn't really just a value,
there is some kind of Value Holder object in there to manage its
creation/access/update. For example to deal with lazy creation of
mutable properties; detecting when things change using dirty flags for
persistence or whatever.

This only really applies to mutable values - lazy val works great for
immutable values (and we all know immutable rocks :).

So ValueHolder objects are cool and all but they seem to break the
universal access principle for fields/methods.

e.g.

Imagine we want a mutable property which when its accessed is lazily
created using some default value function - otherwise it can be set by
the user. So we use some ValueHolder thingy (there's a few already in
Lift - here's a crude simple example - like mentioned in this
thiread...)
http://old.nabble.com/Request-for-addition-to-the-standard-library%3A-Laziness-p8640806.html

class Lazy[T](thunk: => T) {
 private var _value: T = _
 private var _evaluated: Boolean = false

 def evaluated = _evaluated

 def value: T = {
   if (!_evaluated) {
     _value = thunk
     _evaluated = true
   }
   _value
 }

 def set(value: T): Unit =  _value = value

 def apply(): T = value
 def apply(v: T): Unit = set(v)
}


now lets use it

class Cheese {
 val numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
}


So we can do things like

val c1 = new Cheese()

// lets access it, it'll be lazily created
c1.numberFormat()

val c2 = new Cheese()

// lets set a value first before we access it
c2.numberFormat(someThing)

// lets access it, someThing will be returned
ch.numberFormat()


so we can make the property look like a method call; we can support
"ch.numberFormat() = value" or "ch.numberFormat(value)" for
assignment, we can have nested methods/values to get/set the value
(ch.numberFormat.value or ch.numberFormat.set = something).


However if we want the property to look & act like a regular var/val
then we have to wrap it and make up a new name for a private val....

class Cheese {
 private val _numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
 def numberFormat = _numberFormat()
 def numberformat_(value: NumberFormat) = _numberFormat(value)
}

So it works nicely - we can hide the implementation detail of the
mutable property (Lazy) from the public mutable value of type
NumberFormat; its just a noisy bit of code.

I just wondered if anyone had any wacky tricks for avoiding these 3
boiler plate lines? (or maybe I've missed something obvious :) Its
almost like we could do with something like (pseudo-code here)

class Cheese {
 delegate var numberFormat : NumberFormat = new
Lazy({NumberFormat.getNumberInstance(locale)})
}

where "delegate" indicates that the right expression is the object
used to delegate to for the get/set operations rather than just using
a naked field of type "NumberFormat"; so that the code generated
getter/setter methods should do a get or set on the delegate value
holder object.  (I guess reusing "lazy var" instead of "delegate"
would avoid another keyword).

I guess having to write the 3 lines of boilerplate to keep the
universal access principle isn't the end of the world (we can also
force the value object into the public APIi); I just wondered if we
could do better.

Thoughts?

--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://fusesource.com/



--
Kevin Wright

mail/google talk: kev.lee.wright@googlemail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda

Alex Cruise
Joined: 2008-12-17,
User offline. Last seen 2 years 26 weeks ago.
Re: universal access principle, mutable properties and ValueHol
On 10-01-21 04:01 AM, James Strachan wrote:
ec6e67fd1001210401o73bd205am597afc607a6dba1a [at] mail [dot] gmail [dot] com" type="cite">
So ValueHolder objects are cool and all but they seem to break the
universal access principle for fields/methods.
  
The way Lift works around this issue is by defining the Pascal-syntax assignment operator on the field object.  I think it would be nice to find a way around this, but properties in Scala are a bit, uh... Well... Not ideal. :)

e.g. (from memory, not even compiled):

class Field[T] {
    var value[T] = _

    def apply: T = value

    def :=(newval: T): Unit = {value = newval }
}

class Cheese
  object numberFormat extends Field[String]
}

val camembert = new Cheese

camembert.numberformat := "Oh yes, it is very French!"

-0xe1a
Stephen Haberman
Joined: 2009-07-17,
User offline. Last seen 42 years 45 weeks ago.
Re: universal access principle, mutable properties and ValueHol

> class Cheese
> object numberFormat extends Field[String]
> }

This is a tangent, but I've always been curious why lift uses "object"
instead of val, e.g.:

class Cheese {
val numberFormat = new Field[String]
}

AFAIK, they can both do overriding of default behavior, e.g.:

class Cheese {
val numberFormat = new Field[String] {
override def changeDefault() = ...
}
}

So is it just a style thing, or am I missing something?

Thanks,
Stephen

David Pollak
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
Re: universal access principle, mutable properties and ValueHo
Lift's use of object is historical... it was not possible via reflection to determine certain properties about vals where one could determine those properties on objects.

A few months ago, we loosened the restrictions (Lift's Mapper treats object, val, and lazy val the same way), but we have not promoted the use of this flexibility as there are problems with initialization order and val, there are problems with traits and lazy val.  These issues have been fixed in 2.8 and once we verify that the fixes are in place, we'll promote the use of alternatives.

On Thu, Jan 21, 2010 at 10:13 AM, Stephen Haberman <stephen@exigencecorp.com> wrote:

> class Cheese
>    object numberFormat extends Field[String]
> }

This is a tangent, but I've always been curious why lift uses "object"
instead of val, e.g.:

class Cheese {
 val numberFormat = new Field[String]
}

AFAIK, they can both do overriding of default behavior, e.g.:

class Cheese {
 val numberFormat = new Field[String] {
   override def changeDefault() = ...
 }
}

So is it just a style thing, or am I missing something?

Thanks,
Stephen




--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics
Stephen Haberman
Joined: 2009-07-17,
User offline. Last seen 42 years 45 weeks ago.
Re: universal access principle, mutable properties and ValueHo

> Lift's use of object is historical... it was not possible via reflection to
> determine certain properties about vals where one could determine those
> properties on objects.
>
> A few months ago, we loosened the restrictions (Lift's Mapper treats object,
> val, and lazy val the same way), but we have not promoted the use of this
> flexibility as there are problems with initialization order and val, there
> are problems with traits and lazy val. These issues have been fixed in 2.8
> and once we verify that the fixes are in place, we'll promote the use of
> alternatives.

Nice--thanks for the good (and quick) explanation.

- Stephen

Rüdiger Keller
Joined: 2010-01-24,
User offline. Last seen 42 years 45 weeks ago.
Re: universal access principle, mutable properties and ValueHo
Hello Kevin,

let me begin by saying that I think your autoproxy plugin is really wonderful. I was glad if it became part of the language itself.

Back to the topic. If I understand the workings of your plugin correctly, it currently safes you to write "def numberFormat = _numberFormat()" from James' example repeated below.

class Cheese {
  private val _numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
  def numberFormat = _numberFormat()
  def numberformat_(value: NumberFormat) = _numberFormat(value)
}

Do you think it would be possible to extend your plugin to allow something like James suggested (repeated slightly modified below)?

class Cheese {
  @delegate var numberFormat : NumberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
}
In my opinion this would be really nice, as it allows writing custom "property handlers" of all kinds with minimal overhead, e.g. for something like the "bind" functionality of JavaFX.

Do you think such an extension is feasible and worth the effort?

Perhaps the community can give some feedback whether there is actually demand for such a feature?

Regards,
Ruediger


2010/1/21 Kevin Wright <kev.lee.wright@googlemail.com>
It looks like you need the autoproxy plugin!
http://www.artima.com/weblogs/viewpost.jsp?thread=275135http://www.artima.com/weblogs/viewpost.jsp?thread=275588 http://github.com/scala-incubator/autoproxy-plugin

The exact notation is @proxy (soon to be @mixin) and not `delegate`, but otherwise it's pretty much what you describe


Disclaimer: the thing is still pre-alpha, and has some problems when faced with singletons and a few other scenarios, but I do aim to have any issues resolved for the 2.8 Scala release.
--
Kevin Wright

mail/google talk: kev.lee.wright@googlemail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda


Kevin Wright
Joined: 2009-06-09,
User offline. Last seen 49 weeks 3 days ago.
Re: universal access principle, mutable properties and ValueHo


On 24 January 2010 16:05, Rüdiger Keller <ruediger.keller@googlemail.com> wrote:
Hello Kevin,

let me begin by saying that I think your autoproxy plugin is really wonderful. I was glad if it became part of the language itself.

Back to the topic. If I understand the workings of your plugin correctly, it currently safes you to write "def numberFormat = _numberFormat()" from James' example repeated below.

class Cheese {
  private val _numberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
  def numberFormat = _numberFormat()
  def numberformat_(value: NumberFormat) = _numberFormat(value)
}

Do you think it would be possible to extend your plugin to allow something like James suggested (repeated slightly modified below)?

class Cheese {
  @delegate var numberFormat : NumberFormat = new Lazy({NumberFormat.getNumberInstance(locale)})
}
In my opinion this would be really nice, as it allows writing custom "property handlers" of all kinds with minimal overhead, e.g. for something like the "bind" functionality of JavaFX.

Do you think such an extension is feasible and worth the effort?

Perhaps the community can give some feedback whether there is actually demand for such a feature?

Regards,
Ruediger

The exact format for the plugin is:
class Foo {
  @mixin var numberFormat : NumberFormat = ...
}
Which will give you all methods defined in NumberFormat (even if the actual instance is a subclass of NumberFormat).  If NumberFormat contains the methods numberFormat() and numberFormat_=(x), then you have exactly what you want.

 

2010/1/21 Kevin Wright <kev.lee.wright@googlemail.com>
It looks like you need the autoproxy plugin!
http://www.artima.com/weblogs/viewpost.jsp?thread=275135http://www.artima.com/weblogs/viewpost.jsp?thread=275588 http://github.com/scala-incubator/autoproxy-plugin

The exact notation is @proxy (soon to be @mixin) and not `delegate`, but otherwise it's pretty much what you describe


Disclaimer: the thing is still pre-alpha, and has some problems when faced with singletons and a few other scenarios, but I do aim to have any issues resolved for the 2.8 Scala release.
--
Kevin Wright

mail/google talk: kev.lee.wright@googlemail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda





--
Kevin Wright

mail/google talk: kev.lee.wright@googlemail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda

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