Sorry, somehow I seem to have forgotten to send this earlier.

2008/9/5 Trans <transfire / 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 = OpenStruct.new
>> ...
>> counters.foo ||= 0
>> counters.foo += 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.  With the change one
>> could do
>>
>> counters = OpenStruct.new 0
>> ...
>> counters.foo += 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.
>>  Block argument would be the symbol of the property and the return
>> value would be used to initialize the property.  Then you could do
>>
>> data = OpenStruct.new {[]}
>> ...
>> data.animals << "cat" << "dog"
>>
>> or even
>>
>> data = OpenStruct.new do |sym|
>>   case sym
>>   when :animals : []
>>   when :dictionary : {}
>>   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:
>
>  data = OpenStruct.new do |o|
>   o.animals = []
>   o.dictionary = {}
>  end
>
> 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:
>
>  OpenStruct.new{ |o, k| o[k] = [] }
>
> Note, my patch also add #[] and #[]=, which are more important
> changes.

Yes, that's a good idea!

> 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.  Also, the initialization
you present is already possible with a Hash provided as argument.

But I can provide a patch and then we see what others on ruby-core think.

Cheers

robert


-- 
use.inject do |as, often| as.you_can - without end