Smalltalk's favorite Boolean method


#1

Hi,

For a long time I have been programming Smalltalk.
Recently, Ruby “got me” and I was wondering how the
famous ifTrue:ifFalse: could be added to the System.

1 = 2
ifTrue:
[Transcript show: “true!” ]
ifFalse:
[Transcript show: “false!”]

So at least the following works but I was hoping that it would have the
same elegance. “proc” was needed to create a real block instance.


def true.ifTrue_ifFalse(trueBlock , falseBlock)
trueBlock.call
end

def false.ifTrue_ifFalse(trueBlock , falseBlock)
falseBlock.call
end

(1==2).ifTrue_ifFalse(
proc { p “true!”},
proc { p “false!”}
)

(1!=2).ifTrue_ifFalse(
proc { p “true!”},
proc { p “false!”}
)


#2

On Apr 11, 2006, at 8:13 AM, Ernest Micklei wrote:

[Transcript show: “false!”]

So at least the following works but I was hoping that it would have
the
same elegance. “proc” was needed to create a real block instance.

Here’s an alternate syntax that does away with the need to call proc():

class Object
def true?
yield self if self
self
end
def false?
yield self unless self
self
end
end
=> nil

(1 == 2).true? { puts “True!” }.false? { puts “False!” }
False!
=> false

(1 != 2).true? { puts “True!” }.false? { puts “False!” }
True!
=> true

Hope that gives you some fresh ideas.

James Edward G. II


#3

James Edward G. II removed_email_address@domain.invalid writes:

=> nil

(1 == 2).true? { puts “True!” }.false? { puts “False!” }
False!
=> false

(1 != 2).true? { puts “True!” }.false? { puts “False!” }
True!
=> true

Hope that gives you some fresh ideas.

hears 1000 functional programmers whine about return values getting
lost


#4

On Apr 11, 2006, at 10:24 AM, Christian N. wrote:

end

hears 1000 functional programmers whine about return values
getting lost

I didn’t say I liked it. I just said it got rid of the need to
call proc() twice. A boring if statement is all I need. :wink:

James Edward G. II


#5

Christian N. wrote:

hears 1000 functional programmers whine about return values getting
lost

Just so all those functional programmers don’t lose their return values:


class Object
def true?
TrueValue.new(yield)
end
def false?
TrueValue.new(nil)
end
end

class FalseClass
def true?
FalseValue.new(nil)
end
def false?
FalseValue.new(yield)
end
end

class NilClass
def true?
FalseValue.new(nil)
end
def false?
FalseValue.new(yield)
end
end

class BooleanValue
attr_reader :value
def initialize(val)
@value = val
end
def true?
self
end
def false?
self
end
end

class TrueValue < BooleanValue
def true?
TrueValue.new(yield)
end
end

class FalseValue < BooleanValue
def false?
FalseValue.new(yield)
end
end

require ‘test/unit’
class TestTrueFalse < Test::Unit::TestCase
def test_false_results
assert_equal nil, false.true? { no }.value
assert_equal yes, false.true? { no }.false? { yes }.value
assert_equal yes, false.false? { yes }.value
assert_equal yes, false.false? { yes }.true? { no }.value
end

def test_nil_results
assert_equal nil, nil.true? { no }.value
assert_equal yes, nil.true? { no }.false? { yes }.value
assert_equal yes, nil.false? { yes }.value
assert_equal yes, nil.false? { yes }.true? { no }.value
end

def test_true_results
assert_equal yes, true.true? { yes }.value
assert_equal yes, true.true? { yes }.false? { no }.value
assert_equal nil, true.false? { no }.value
assert_equal yes, true.false? { no }.true? { yes }.value
end

def test_object_results
assert_equal yes, “hi”.true? { yes }.value
assert_equal yes, “hi”.true? { yes }.false? { no }.value
assert_equal nil, “hi”.false? { no }.value
assert_equal yes, “hi”.false? { no }.true? { yes }.value
end

def test_true_true_chains
check_chain(true, :true?)
check_chain(“hi”, :true?)
assert_equal nil, nil.true? { no }.true? { no }.value
assert_equal nil, false.true? { no }.true? { no }.value
end

def test_false_false_chains
assert_equal nil, “hi”.false? { no }.false? { no }.value
assert_equal nil, true.false? { no }.false? { no }.value
check_chain(nil, :false?)
check_chain(false, :false?)
end

def check_chain(value, method)
i = 0
assert_equal 3,
value.
send(method) { i += 1 }.
send(method) { i += 2 }.value
assert_equal 3, i
end

def yes
:yes
end

def no
fail “Should never execute this”
end
end


– Jim W.


#6

On Apr 11, 2006, at 2:25 PM, Jim W. wrote:

Christian N. wrote:

hears 1000 functional programmers whine about return values getting
lost

Just so all those functional programmers don’t lose their return
values:

[Massive amount of code snipped]

Me thinks Jim W. is ether really smart or just has too much free
time.
:slight_smile:

Jim F.


#7

What about:

case (1 == 2)
when true:
p ‘true’
when false:
p ‘false’
end

If you don’t like the ternary operator the above syntax is pretty clear.

Roy


#8

Roy S. wrote:

What about:

case (1 == 2)
when true:
p ‘true’
when false:
p ‘false’
end

If you don’t like the ternary operator the above syntax is pretty clear.

Ah Roy … You are spoiling the fun by being practical (insert big wink
here).

Actually the point of the exercise is to demonstrate that if/then/else
can be completely implemented in terms of polymorphic method sends
without using an if statement (or ternary operator) in that
implementation. It is kind of a theoretical point that conditional
operations can be implemented with polymorphism (just as polymorphism
can be implemented with condition primitives).

No one is advocating that this code be used in preference to Ruby’s
natural if statement. We are just showing that it can be done fairly
easily. (If you want a real challenge, try implementing the same in
Java … same rules, no if statements allowed in the implementation. I
managed it once, but I had to resort to using a Hash table for lookups).

The same can be done with looping constructs. Here’s an implementation
of a “while” loop done entirely with polymorphic method calls. (Since
while is a reserved word, I’m using ‘repeat’ instead)

Example Usage:

i = 0
repeat { i < 10 }.do { puts i; i += 1 }

Implementation:

alias repeat proc

class Proc
def do(&block)
call.false? { return nil }
block.call
self.do(&block)
end
end

Much simplier than the if thing, mainly because you generally don’t
worry about return values from the while loop. Notice the use of tail
recursion. If Ruby were properly tail recursive, that implementation
would be adequate. But since we face the possibility of statck
overflow, here’s a (slower) version without the overflow problem:

class Proc
def do_cc(&block)
goto = nil
callcc { |cc| goto = cc }
call.false? { return nil }
block.call
goto.call
end
end

All in fun and games.


– Jim W.