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 <d26sfn01vqs / enews3.newsguy.com>,
Phil Tomson <ptkwt / aracnet.com> wrote:
>In article <3anjpeF6d91alU1 / individual.net>,
>Robert Klemme <bob.news / gmx.net> wrote:
>>
>>"Phil Tomson" <ptkwt / aracnet.com> schrieb im Newsbeitrag 
>>news:d24b8d02fq1 / enews4.newsguy.com...
>>> In article <3alo9bF6d2vo8U1 / individual.net>,
>>> Robert Klemme <bob.news / gmx.net> 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.
>
>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


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. ;-)


Phil