Trans
9/7/2008 5:01:00 AM
On Sep 6, 1:27=A0pm, "Robert Klemme" <shortcut...@googlemail.com> wrote:
> Sorry, somehow I seem to have forgotten to send this earlier.
>
> 2008/9/5 Trans <transf...@gmail.com>:
>
>
>
>
>
> > On Sep 5, 7:54 am, "Robert Klemme" <shortcut...@googlemail.com> wrote:
> >> Hi,
>
> >> I just had a use case where I wanted to have several counters and not
> >> store them in a Hash because of the nicer syntax of OpenStruct.
> >> Currently, the code has to do
>
> >> counters =3D OpenStruct.new
> >> ...
> >> counters.foo ||=3D 0
> >> counters.foo +=3D 1
>
> >> For obvious reasons I'd like to get rid of the initialization.
>
> >> The suggestion would be to do this: if the argument to
> >> OpenStruct#initialize is not a Hash use it as default value which is
> >> returned for undefined properties. As far as I can see this won't
> >> break existing code since the default is nil. =A0With the change one
> >> could do
>
> >> counters =3D OpenStruct.new 0
> >> ...
> >> counters.foo +=3D 1
>
> >> We could go even further and copy the Hash approach by also allowing a
> >> block which is invoked when a property is accessed for the first time.
> >> =A0Block argument would be the symbol of the property and the return
> >> value would be used to initialize the property. =A0Then you could do
>
> >> data =3D OpenStruct.new {[]}
> >> ...
> >> data.animals << "cat" << "dog"
>
> >> or even
>
> >> data =3D OpenStruct.new do |sym|
> >> =A0 case sym
> >> =A0 when :animals : []
> >> =A0 when :dictionary : {}
> >> =A0 end
> >> end
>
> >> In other words: declarative lazy initialization.
>
> >> Again, existing code would not be affected.
>
> >> What do others think?
>
> > I recently submitted a patch that allowed OpenStruct to take a self
> > yielding block, e.g you could do:
>
> > =A0data =3D OpenStruct.new do |o|
> > =A0 o.animals =3D []
> > =A0 o.dictionary =3D {}
> > =A0end
>
> > I don't really see that much use for complex lazy initialization as
> > you suggest.
>
> I don't view it so much as complex initialization but rather
> declarative lazy initialization because it saves you the effort of
> writing all those getter methods.
>
> > Though, I can see the Hash block form being useful. Maybe
> > that would be a better use of the block. Actually, both could be
> > supported if we differentiate on the arity of the block. With two
> > args:
>
> > =A0OpenStruct.new{ |o, k| o[k] =3D [] }
>
> > Note, my patch also add #[] and #[]=3D, which are more important
> > changes.
>
> Yes, that's a good idea!
Glad you agree. Hell of a lot faster then send(key) and
send("#{key}=3D", val).
Mention it on ruby-core!
> > Robert would you like to update my patch to support the Hash block
> > notation and resubmit it?
>
> I would rather not want to have two interpretations for the block
> because this can easily lead to confusion and subtle bugs can creep in
> when accidentally having the wrong arity.
That was my first though too, but then I considered it a bit more and
think it makes enough sense. If we are asking for just the OpenStruct
object, ie. OpenStruct.new{ |o| ... } then clearly we are interested
in working with the object. If we ask for the key as well, ie.
OpenStruct.new{ |o, k| ... } then it is also clear we are instead
interested in doing something with a key. It's really not any
different in principle from other method interfaces, like using one or
two arguments with #slice.
T.