B
Brian Mitchell
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
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