Smalltalk's favorite Boolean method

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!"}
)

--
Posted via http://www.ruby-forum.com/.

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.

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 Gray II

···

On Apr 11, 2006, at 8:13 AM, Ernest Micklei 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.

Roy

Ernest Micklei wrote:

···

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!"}
)

James Edward Gray II <james@grayproductions.net> writes:

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.

*hears 1000 functional programmers whine about return values getting lost*

···

James Edward Gray II

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

Roy Sutton 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 Weirich

--
Posted via http://www.ruby-forum.com/\.

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 Gray II

···

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

James Edward Gray II <james@grayproductions.net> writes:

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.

*hears 1000 functional programmers whine about return values getting lost*

Christian Neukirchen 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 Weirich

--
Posted via http://www.ruby-forum.com/\.

[Massive amount of code snipped]

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

Jim Freeze

···

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

Christian Neukirchen wrote:

*hears 1000 functional programmers whine about return values getting
lost*

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