Alternate initializers or alternate class?

Jan Molic recently contacted me about a slighlty modified version of
his OrderedHash class that I distribute in Facets, the Dictionary
class. There are a few variations of this class initself and Jan
originally provided these via a subclass. I, on the other, thought
alternate initializers would be better.

Which is the best approach?

Ex.

class AutoOrderHash < OrderedHash

 def new(*args)
    super(*args){ |h,k| h[k] = self.class.new }
 end

end

vs.

class OrderHash

 def self.auto(*args)
    new(*args){ |h,k| h[k] = self.class.new }
 end

end

Thanks,
T.

On 7/15/06, [email protected] [email protected] wrote:

class AutoOrderHash < OrderedHash

 def self.auto(*args)
    new(*args){ |h,k| h[k] = self.class.new }
 end

end

Thanks,
T.

I generally find it easier to remember what general kind of thing I
want (i.e. class) than what specific subtype of thing I want
(alternative initializer). For example, the Date class has a whole
load of alternative initializers which I usually have to look up. I
find it easier to wrap those in specific classes.

Using different classes, you have two pieces of information to
correlate:
a) class name
b) signature of initializer

Using alternative initializers, you have three:
a) class name
b) method name of alternative initializer
c) alternative initializer signature

Just my tuppence worth.

Regards,
Sean

On Jul 14, 2006, at 7:58 PM, [email protected] wrote:

class AutoOrderHash < OrderedHash

 def self.auto(*args)
    new(*args){ |h,k| h[k] = self.class.new }
 end

end

Thanks,
T.

The second implemented in terms of the first?

class OrderHash
def self.auto(*args)
AutoOrderHash.new(*args)
end
end

Yes, please have your cake and eat it too. :slight_smile:

Logan C. wrote:

The second implemented in terms of the first?

class OrderHash
def self.auto(*args)
AutoOrderHash.new(*args)
end
end

Yes, please have your cake and eat it too. :slight_smile:

Hmmm… I like the “taste” of that :wink:

Thanks,
T.

[email protected] wrote:

class AutoOrderHash < OrderedHash

 def self.auto(*args)
    new(*args){ |h,k| h[k] = self.class.new }
 end

end

I actually favor a third approach; keyword argument options.

OrderHash.new(:auto? => true)

I think Rails has shown us that such option hashes are extremely
powerful and flexible.

Just my $.02
Daniel

On 7/15/06, [email protected] [email protected] wrote:

Yes, please have your cake and eat it too. :slight_smile:

Hmmm… I like the “taste” of that :wink:

Thanks,
T.

+1 - having cake and eating it is a thing devoutly to be wished for :wink:

Regards,
Sean

On 7/15/06, Daniel S. [email protected] wrote:

class OrderHash

I think Rails has shown us that such option hashes are extremely
powerful and flexible.

Just my $.02
Daniel

I’m a big proponent of keyword arguments (which we implement in the
current version of Ruby using option hashes). However, I’m not sure
it’s appropriate for this usage, i.e. selecting which kind of object
is created.

You would have to select which alternative initializer to call in
OrderHash.initialize and extract each different set of arguments
depending on which alternative initializer you’re calling. It would
add unnecessary complexity and overhead. It also suffers from the
cognitive load of having to remember three things (class, selector,
which options go with which selector) instead of two (class,
initializer signature). Also, if you add another kind of OrderedHash,
you have to change OrderHash#initialize.

I’m all for keyword ~options~ in initializers which modify some aspect
of the newly created object. I’m just not sure you want to have the
initializer return different classes of object depending on those
options.

BTW, the keyword arguments technique has a long history before Rails
came along :wink:

Regards,
Sean

Sean O’Halpin wrote:

Ex.

OrderHash.new(:auto? => true)
it’s appropriate for this usage, i.e. selecting which kind of object

I’m all for keyword ~options~ in initializers which modify some aspect
of the newly created object. I’m just not sure you want to have the
initializer return different classes of object depending on those
options.

BTW, the keyword arguments technique has a long history before Rails
came along :wink:

Who said anything about multiple classes?

class OrderHash
def initialize(*args)
options = args.shift.to_hash if args.last.respond_to? :to_hash
if options and options.has_key? :auto?
do_something_with{|hsh, key| hsh[key] = self.class.new}
end
end
end

I haven’t yet read through the OrderHash/Dictionary code, so it might be
better to put it in ::new

Cheers,
Daniel

Sean O’Halpin wrote:

+1 - having cake and eating it is a thing devoutly to be wished for :wink:

Well, it seemed all well and good but now I find myself having to
create Auto versions of every other subclass of Hash. I have a few, so
that start’s to get ugly. hmm…

T.

On 7/15/06, Daniel S. [email protected] wrote:

vs.

current version of Ruby using option hashes). However, I’m not sure
you have to change OrderHash#initialize.

I’m all for keyword ~options~ in initializers which modify some aspect
of the newly created object. I’m just not sure you want to have the
initializer return different classes of object depending on those
options.

BTW, the keyword arguments technique has a long history before Rails
came along :wink:

Who said anything about multiple classes?

Fair enough - I was thinking about the original implementation and
Logan’s creative solution.

class OrderHash
def initialize(*args)
options = args.shift.to_hash if args.last.respond_to? :to_hash

Don’t you think this is rather fragile? What if you want to initialize
with an object that responds to :to_hash but isn’t a set of options?
Also, this will fail if no args are passed (minor point).

   if options and options.has_key? :auto?
     do_something_with{|hsh, key| hsh[key] = self.class.new}
   end

Plus similar tests for every other alternative. This doesn’t seem to
me to be good design for the reason I gave earlier - if you add new
specialised kinds of hash, you’ll have to change (or completely
replace) this initializer. Don’t you think it would be a cleaner
design to simply create a new subclass? Isn’t this what inheritance is
for? Even reopening the OrderedHash class to add a new alternative
initializer is cleaner.

 end

end

A third point is that having an interface like this means you can’t
use OrderedHash and AutoOrderedHash as simple drop-in replacements for
Hash. The same goes for the alternative initializer approach.

Cheers,
Sean

On 7/16/06, [email protected] [email protected] wrote:

You don’t have to write the code yourself! :slight_smile:

def make_auto(*classes)
classes.each do |klass|
new_class = Class.new klass do
def initialize(*args)
super(*args){ |h,k| h[k] = self.class.new }
end
end
Object.const_set(“Auto#{klass}”, new_class)
end
end

class OrderedHash < Hash

do something here

end

make_auto Hash, OrderedHash

ah = AutoHash.new
aoh = AutoOrderedHash.new

p ah[:a]
p aoh[:b]
END
{}
{}

Regards,
Sean