- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
universal access principle, mutable properties and ValueHolder type objects
Thu, 2010-01-21, 13:02
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?
Thu, 2010-01-21, 13:57
#2
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>
--
Kevin Wright
mail/google talk: kev.lee.wright@googlemail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda
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
Thu, 2010-01-21, 19:07
#3
Re: universal access principle, mutable properties and ValueHol
On 10-01-21 04:01 AM, James Strachan wrote:
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
ec6e67fd1001210401o73bd205am597afc607a6dba1a [at] mail [dot] gmail [dot] com" type="cite">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. :)So ValueHolder objects are cool and all but they seem to break the universal access principle for fields/methods.
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
Thu, 2010-01-21, 19:17
#4
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
Thu, 2010-01-21, 19:27
#5
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:
--
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
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
Thu, 2010-01-21, 19:37
#6
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
Sun, 2010-01-24, 17:07
#7
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>
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
Sun, 2010-01-24, 20:07
#8
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
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:
--
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