From: Brian Mitchell Date: 2004-10-31T06:58:51+09:00 Subject: New Ruby conditional semantics thoughts The basic background was begun by someone asking a question about boolean expressions and _why_ Ruby treats expression results nil and false as a *false* path in a boolean conditional. Some just explained that nil is like empty so it is nice to treat is like false. Someone then pointed out how one could use the methods false? and true? on the object to test its value. This immediately reminded me of smalltalk conditional design and possible ways one could implement something similar in Ruby. For example, here are smalltalk conditionals implemented in Ruby: module Kernel def _if(exp, &block) else_block = callcc { |cont| @condition = cont return exp.true? &block } @condition = nil exp.false? &else_block end def _unless(exp, &block) else_block = cont { |cont| @condition = cont return exp.false? &block } @condition = nil exp.true? &else_block end def _else(&block) @condition.call(block) end end class Object def false? nil end def true? yield end end class FalseClass def false? yield end def true? nil end end class NilClass def false? yield end def true? nil end end ### Test It!### a = nil b = 2 c = false _if(a != b) { puts "a!=b" } _else { puts "a==b" } _unless(b) { puts "b is false" } _else { puts "b is true" } Now this is just for if and unless. case and other conditions could be made also but this is enough to give a basic idea (and save me time). I will explain why this is a good thing. As I explained how this whole thread started, there was a question as to why nil and false were treated as "boolean false". Here we use duck typing (which is right along side most ruby design) rather than just say that these values to this. This also allows change in behavior and more false types to be added to ruby. This will help the with implementation of DSLs using ruby. The implementation of conditionals as methods brings up another pro. conditions can be hooked, changed, and treated like any other ruby internal. This has been usually noted to be a good thing but a good use is hard to come up with... just as the argument goes with callcc ;) . The other good note is the use of blocks. While blocks now must be called (slower? I haven't bench marked yet), the block can be passed around now or even come from a different binding. This is only one way to do conditionals though. If you look at the above method it is actually a three part method. One is to use methods not values to evaluate a boolean, the second is to use a block that is yielded upon, and the third is the implementation of conditionals as methods of Kernel. Ruby currently uses none. Any could be adopted. I would say that the adoption of conditionals implicitly calling false? or true? respectively would be a good thing. the adoption of blocks is a large jump and would probably require some syntax sugar to use regularly. It also break compatibility with current code. Which might not be as great of a thing. As for if as a function... this may or may not be a positive change. I will explain bellow. I do have an idea on how to keep current code compatible and add both the duck typing for booleans and the block calling. The one thing is that conditionals as functions must then be dropped. We would check all object via the boolean methods false? and true? . The conditional would check for truth (now mostly internal -- although true and false would still exist). Truth would be easy to evaluate as false? would return true or false and true? would do like wise respective to the object. If the conditional is to execute then it checks for the use of a block. if so it will yield the block. if no block was given it continues to function as it does. Now you may ask, why check for a block anymore? you could just use my_proc.call. This is a valid point. The only reason that blocks are there is for the illusion that conditions are not controlled by value but rather by behavior of each object. The only reason I can think of to add blocks to conditionals is for syntax sugar. There is one more final method I have to cover. This method allows for all three parts of smalltalk conditionals to be included and still keep *some* backwards compatibility. In the description above... if the conditional was passed a block it would then use the method version. if it was not then it would use the plain old version. Another thought would be convert the non-block version to a block and then call if. The problem with this is scope semantics for variables created within the conditional. As many of you know block variables not present outside of the block (in context) are block local... this is not currently so for variables created within a conditional. This as been posted as a change for the Ruby 2.0 spec but I have not researched this extensively. Anyway... I feel like I am talking too much so I will end it here. This is just food for thought and I have yet to decide if I have the intention to write up an RCR for this. I also should note that I am rather new to Ruby so i may have errors in the above statements ;) . So take it lightly. Cheers, Brian Mitchell