From: Robert Klemme Date: 2005-03-28T16:59:49+09:00 Subject: Re: User defined loops (applicable to DSLs) "Phil Tomson" schrieb im Newsbeitrag news:d26uh30st5@enews1.newsguy.com... > We've been having this discussion about defining your own while loop in a > thread about Haskell and I thought I'd change the subject in case people > who had no interest in Haskell were avoiding it ;-) > > In article , > Phil Tomson wrote: >>In article <3anjpeF6d91alU1@individual.net>, >>Robert Klemme wrote: >>> >>>"Phil Tomson" schrieb im Newsbeitrag >>>news:d24b8d02fq1@enews4.newsguy.com... >>>> In article <3alo9bF6d2vo8U1@individual.net>, >>>> Robert Klemme wrote: >>>>> >>>>> >>>>>I'm surprised about "retry", too. His analysis sounds all very >>>>>resonable - >>>>>only that "break" does not work for me but "return" does: >>>>> >>>>>>> def my_while(cond) >>>>>>> break unless cond >>>>>>> yield >>>>>>> retry >>>>>>> end >>>>>=> nil >>>>>>> i = 0 >>>>>=> 0 >>>>>>> my_while i < 10 do >>>>>?> puts i >>>>>>> i += 1 >>>>>>> end >>>>>0 >>>>>1 >>>>>2 >>>>>3 >>>>>4 >>>>>5 >>>>>6 >>>>>7 >>>>>8 >>>>>9 >>>>>LocalJumpError: unexpected break >>>>> from (irb):2:in `my_while' >>>>> from (irb):7 >>>> >>>> >>>> Hmmm... break works find for me in 1.8.2. Time to upgrade? >>> >>>Yeah, maybe. >>> >>>>>Btw, can anybody think of a way to make my_while return the result of >>>>>the >>>>>last block evaluation? It seems impossible because the return occurs >>>>>before >>>>>the yield... >>>>> >>>> >>>> >>>> How about: >>>> >>>> def my_while(cond) >>>> return @ret unless cond >>>> @ret = yield >>>> retry >>>> end >>> >>>Not thread safe. And I think also not nesting safe. >>> >> >>Yes, I know. Did you see my later post? I don't think it made it from >>the newsgroup to the mailing list. Yes, I saw it - but too late. :-) >>Here's the code to make it nestable and threadsafe: >> >> class Whiler >> def initialize >> @ret = nil >> end >> >> def while(cond) >> break @ret unless cond >> @ret = yield >> retry >> end >> end >> >> outer = Whiler.new >> inner = Whiler.new >> >> i=0 >> outer.while i<5 do >> puts "i: #{i}" >> j=0 >> inner.while j<5 do >> puts " j: #{j}" >> j+=1 >> end >> i+=1 >> end >> >>A bit cumbersome, perhaps, but it is definately nestable and should be >>threadsafe as well. >> > > Actually, I figured out a way to make it much more natural: > > Change Whiler to accept an optional condition in it's constructor like > so: > > class Whiler > def initialize(cond=nil) > @ret = nil > @cond = cond > end > def while(cond=@cond) > break @ret unless cond > @ret = yield > retry > end > end > > > Define the my_while top-level method like so: > > def my_while(cond,&block) > Whiler.new(cond).method(:while).call(&block) > end > > > Now the user of my_while doesn't need to know that a Whiler class even > exists. Usage example: > > i = 0 > my_while(i<10) do > puts i > j=0 > my_while(j<10) do > puts " j: #{j}" > j+=1 > end > i+=1 > end > This is nice! > I think there are some implications for DSLs (Domain Specific Languages) > in this example. > The my_while method as defined above (the last one) hides some Ruby > language details from the user of my_while so that my_while can appear > like a > natural looping construct. I've created DSLs for people who had > no idea they were using Ruby underneath; this sort of thing is > necessary so they don't feel as though they are obliged to learn > Ruby to use the DSL. > > Another example of why Ruby is so good for creating DSLs. ;-) +2 Kind regards robert