Csaba Henk wrote:
>>I'm curious about the implementation that makes this work. Could you
>>please post it.
> 
> 
> Here you go:

<snipped code>

Thanks for that. At first I was confused as to why you returned rv in 
the else block of TrueModule and also why you didn't just call the block 
in the ifthen of the TrueModule. Then I realised that you wanted to have 
the correct return value just like the builtin if..then..else..end 
syntax. Very nice.

What is the threading problem? It looks ok to me... :-)

It's annoying that ifthen doesn't work when there is no else block and 
that calling else directly on true fails but on false it works.

I didn't know about calling a block with [] rather than .call. That's 
cool but I prefer .call for now. Neither about sending :include message 
to classes - that's not something I would have thought of.

I came up with some code to do the unless and when methods for single 
argument versions of if and if not. I also came up with if_then a two 
argument version where I simply pass in two blocks as normal arguments 
(rather than as the implicit block argument). The result calling side 
syntax is a bit ugly:

     result = 10.if_else Proc.new {
       ...
     }, Proc.new {
       ...
     };

It's starting to remind me of doing blocks/closures in Java (but not yet 
*that* ugly). When Ruby get's keyword arguments it will look a little 
better:

     result = 10.if Proc.new {
       ...
     }, else: Proc.new {
       ...
     };

Then the 1-arg if and the 2-arg if-then-else can be done with a single 
method without too much trouble. It would be nice to be able to elide 
the Proc.new calls and have something like:

     result = 10.if {
       ...
     }, else: {
       ...
     };

And seriously nice if you didn't have to use commas to separate 
arguments when not using "()" style calling syntax.

Here's the modified code for the curious:

##########
module TrueModule
   def ifthen &b
     @bl = b
     self
   end
   def else
     rv, @bl = @bl.call, nil
     rv
   end

   def if_else(ifblock, elseblock)
     ifblock.call
   end
end

module FalseModule
   def ifthen
     self
   end
   def else
     yield
   end
   def if_else(ifblock, elseblock)
     elseblock.call
   end
end

module JustTrueModule
   # 'when' is the same as 'on_true' (if with no else)
   def on_true
     yield
   end
   def when
     yield
   end

   # 'on_false' is the same as 'on_false' (unless)
   def on_false
     nil
   end
   def unless
     nil
   end
end

module JustFalseModule
    def on_true
      nil
    end
    def when
      nil
    end
    def on_false
      yield
    end
    def unless
      yield
    end
end

class Object
   include TrueModule
   include JustTrueModule

   def if_else(ifblock, elseblock)
     if self then
       ifblock.call
     else
       elseblock.call
     end
   end
end

[NilClass, FalseClass].each { |c|
   c.send :include, FalseModule
   c.send :include, JustFalseModule
}

if __FILE__ == $0
   [1, 0, "Hello", false, true, nil].each { |item|
     puts "--- #{item.inspect} ---"

     # using on_true
     result = item.on_true {
       puts "TRUE";
       1
     }
     puts "result = #{result.inspect}"
     # using when
     result = item.when {
       puts "TRUE";
       1
     }
     puts "result = #{result.inspect}"
     # using built-in if
     result = if (item)
       puts "TRUE"
       1
     end
     puts "result = #{result.inspect}"

     # using ifthen
     result = item.ifthen {
        puts "item is true"
        1
     }.else {
        puts "item is false"
        0
     }
     puts "result = #{result.inspect}"
     # using built-in if & then
     result = if item
       puts "item is true"
       1
     else
       puts "item is false"
       0
     end
     puts "result = #{result.inspect}"
     # using if_else
     result = item.if_else(Proc.new {
       puts "item is true"
       1
     }, Proc.new {
       puts "item is false"
       0
     });
     puts "result = #{result.inspect}"

     # using on_false
     result = item.on_false { puts "FALSE"; 0 }
     puts "result = #{result.inspect}"
     # using unless
     result = item.unless { puts "FALSE"; 0 }
     puts "result = #{result.inspect}"
     # using built-in if not
     result = if (not item)
       puts "FALSE"
       0
     end
     puts "result = #{result.inspect}"
   }
end

if __FILE__ == $0
   require "runit/testcase"
   require 'runit/cui/testrunner'
   require 'runit/testsuite'

   class Testing_class < RUNIT::TestCase
       def test1
         result = 1.on_true { 10 }
         assert_equal(result, 10);
       end
       def test2
         result = false.on_true { 10 }
         assert_equal(result, nil);
       end
       def test3
         result = nil.on_true { 10 }
         assert_equal(result, nil);
       end
   end

   RUNIT::CUI::TestRunner.run(Testing_class.suite)
end
##########

 > But, the point is:
 >
 > * no stock conditional constructs were used in the above code;
 >
 > * this ifthen/else are really methods, you can override them, wrap
 >    them, or
 > whatever you want.
 >
 > This latter thingy is what the Smalltalkers are proud of, ain't it?

I'm not a proud Smalltalker. I think both Smalltalk and Ruby are great 
(the Smalltalk image thing makes me uncomfortable though). I just wanted 
to see the code 'cause I thought I'd learn something from it :-). I did 
- thanks.

Steve.
--
Steven Shaw http://c2.com/cgi/wiki?StevenShaw