From: Eric Mahurin Date: 2005-08-09T04:19:45+09:00 Subject: Re: polymorphism and/or named parameters: the ruby way? --- Joel VanderWerf wrote: > Eric Mahurin wrote: > > --- "Berger, Daniel" wrote: > ... > >>class Foo > >> attr_accessor :bar, :baz, :zap > >> def initialize > >> yield self if block_given? > >> end > >>end > >> > >>foo = Foo.new do |f| > >> f.bar = "hello" > >> f.baz = 5 > >> f.zap = "world" > >>end > > > > > > What's the advantage of this over: > > > > foo = Foo.new > > foo.bar = "hello" > > foo.baz = 5 > > foo.zap = "world" > > One advantage is you can construct and configure an object > without > assigning to a local variable. This can be useful if you are > constructing a bunch of objects in #map or in literal hashes > or arrays > or (this is a bit far-fetched, but why not) parameter lists. > And it is > nice not to add a symbol to the namespace. I particularly > like the fact > that if you are doing several of these, possibly even in > different > methods, you can use the same |f| parameter and cut and paste > between > the assignments: > > def make_foos > foo1 = Foo.new do |f| > f.bar = "BAR" > end > > foo2 = Foo.new do |f| > f.bar = "ZAP" > end > end > > That has a nice standardized look to me, especially when you > are > populating many fields in several different methods. > > > Is it method chaining? You want this thing to return foo? > You > > could just end this statement list with "foo", or you could > use > > "ensure": > > > > begin > > foo = Foo.new > > ensure > > foo.bar = "hello" > > foo.baz = 5 > > foo.zap = "world" > > end > > Danger Will Robinson. An exception may happen in Foo.new.... > > > This begin..ensure..end expression returns foo (the result > > before ensure) not "world" as you might expect. Great for > > doing post operations after you calculate a return value > > (equivalent of C's i++ is another example). > > You can use an else clause to return a value from a > begin..end block, > but only if there is at least one rescue clause (or else you > get a warning). > > p( > begin > a = [] > a << 1 > a << 2 > rescue > else > a > end > ) > > # ==> [1, 2] The exception issue bothers me too with this ensure trick. But, this else doesn't really help. I was wanting something that appplied some post operations after you determine the return value. For example, the equivalent to the C expression a[i++] is the following using the ensure "operator": a[begin i ensure i+=1 end] I've benchmarked various ways of doing this and this method is the clear winner. Functionally, I guess something like this would be most flexible (no performance advantage like "ensure"): class Object def post yield(self) self end end Then you could do these types of things: a[i.post{i+=1}] foo = Foo.new.post do |f| f.bar = "hello" f.baz = 5 f.zap = "world" end With this, no need for the "yield(self) if block_given?" in initialize. This very useful little method is a nice thing to have in your bag-o-tricks. ____________________________________________________ Start your day with Yahoo! - make it your home page http://www.yahoo.com/r/hs