A Ruby-relevant quote from Alan Kay

B

Bill Kelly

Hi,

From: "Csaba Henk said:
What do you mean by "needing" if? Ruby neither needs if, it just has it.

Ah, sorry - I wasn't intending to compare Smalltalk directly
to Ruby here. I got into Smalltalk (albeit briefly) before I'd
heard of Ruby.

From http://en.wikipedia.org/wiki/Smalltalk_programming_language

One surprising feature of Smalltalk is that the traditional
progamming constructs: if-then-else, for, while, etc. are
not built into the language. All of these things are
implemented using objects. For example, decisions are made
by sending an ifTrue: message to a Boolean object, and passing
a fragment of code to execute if the Boolean is True. There
are only three built-in executable constructs:

- sending a message to an object;
- assigning an object to a variable;
- returning an object from a method;

and a few syntactic constructs for declaring literal objects
and temporary variables.

But the above was something that delighted me about Smalltalk
when learning about it. It just seemed like a mark of quality
to me that its designer had been able to derive a useful,
expressive syntax from so few essentials.

(Of course just prior to studying Smalltalk I had unwisely
volunteered to take over a large VB6 project... If only I'd
spent 15 minutes studying the VB6 language reference before
agreeing to that.... Gack! :)
You can define if/then/else type methods for objects with a few line of
Ruby code easily (without using "and", "case" and any other
conditional-like construct, of course, I mean it that way). I made it up
to have

loop {
gets.ifthen {
puts "you fed me a line"
}.else {
puts "bye"; exit
}
}

as valid code.

:) ... Ruby rocks ... :)


Regards,

Bill
 
C

Csaba Henk

From http://en.wikipedia.org/wiki/Smalltalk_programming_language

One surprising feature of Smalltalk is that the traditional
progamming constructs: if-then-else, for, while, etc. are
not built into the language. All of these things are
implemented using objects. For example, decisions are made
by sending an ifTrue: message to a Boolean object, and passing
a fragment of code to execute if the Boolean is True. There
are only three built-in executable constructs:

- sending a message to an object;
- assigning an object to a variable;
- returning an object from a method;

and a few syntactic constructs for declaring literal objects
and temporary variables.

(Of course just prior to studying Smalltalk I had unwisely
volunteered to take over a large VB6 project... If only I'd
spent 15 minutes studying the VB6 language reference before
agreeing to that.... Gack! :)

[ :) ]

What is happening in the above ruby code snippet, is damn exactly the same as
what you've been quoting from the Wikipedia Smalltalk article, isn't it?

(OK, I used "ifthen" and not "ifTrue", but that doesn't make a
difference.)

Cheers,
Csaba
 
S

Steven Shaw

I'm curious about the implementation that makes this work. Could you
please post it. I'm thinking that it's not quite the same as in
Smalltalk - the else clause requires two method invocations (I guess
that's what the special ".else" is and also won't there be an additional
extra object constructed and passed back from the ifthen....come to
think of it I suppose the object doesn't need to be constructed as only
two are required... sorry I'm thinking aloud.

Steve.
 
C

Csaba Henk

Csaba Henk wrote:


I'm curious about the implementation that makes this work. Could you
please post it.

Here you go:

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

module FalseModule
def ifthen
self
end
def else
yield
end
end

class Object
include TrueModule
end

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

(__FILE__ == $0).ifthen {

puts <<-FOO
Write me something funny!
When you finished, start a new line, and send EOF (Ctrl-d)!
FOO

catch:)xit) {
loop {
gets.ifthen {
puts "you fed me a line"
}.else {
puts "bye"; throw :xit
}
}
}

}.else {}
#############
I'm thinking that it's not quite the same as in
Smalltalk - the else clause requires two method invocations (I guess
that's what the special ".else" is and also won't there be an additional
extra object constructed and passed back from the ifthen....come to
think of it I suppose the object doesn't need to be constructed as only
two are required... sorry I'm thinking aloud.

Of course it's not the same as in Smalltalk. In Smalltalk you have real
multiblock methods, here we just fake it. Also, as I know,in Smalltalk
only True and False have ifTrue ... else methods, while in my
implementation each object will have these (of course, it is so because
i wanted it so, not because of some intinsic property of Ruby).

And it's neither thread-safe. I think it could be made thread-safe with
not much effort.

Due to the fact that in Ruby methods have only one real block argument,
you either make up simple one-blocked "ifthen" and "unless" methods,
without an "else" branch, or you have to make up some finalizer, who
does the action in fact (so if your "ifthen" can have an "else" brach,
it must have an "else" branch, or at least a terminating "fi" (in my
implementation you could just alias "else" to "fi")).

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?

Csaba
 
S

Steven Shaw

Csaba said:
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.
 
S

Steven Shaw

I came across what looked like a keyword argument calling syntax via
http://www.zenspider.com/Languages/Ruby/QuickRef.html - I've updated my
code and it's pretty close to have I hoped it could be. I had to use the
'*' notation for optional "rest" arguments so that I could allow for an
optional else block. The "keyword arguments" come through as a single
Hash into the first element of rest.

Here's what it looks like:

result = (i > 10).if Proc.new {
puts "i > 10"
i
}, :else => Proc.new {
puts "i <= 10"
0
};

I discovered that you can use "proc" instead of "Proc.new":

result = (i > 10).if proc{
puts "i > 10"
i
}, :else => proc{
puts "i <= 10"
0
};

Anyone know a way of making this even more concise? Within using the
built-in syntax... :)

Here's the updated code with Csaba's ifthen, and my on_false/unless and
on_true/when methods removed for clarity.

#########
module TrueModule
def if(ifblock, *rest)
ifblock.call
end
end

module FalseModule
def if(ifblock, *rest)
if rest.length == 1 then
keyargs = rest[0]
elseblock = keyargs[:else]
if elseblock
elseblock.call
end
end
end
end

class Object
include TrueModule
end

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

if $0 == __FILE__ then

def test_single_block_if(item)
actual = item.if Proc.new{
puts "TRUE";
1
}
puts "actual = #{actual.inspect}"
# using built-in if
expected = if (item)
puts "TRUE"
1
end
puts "expected = #{expected.inspect}"
if actual != expected
puts "FIXME: single-block if failed"
end
end

def test_multi_block_if(item)
actual = item.if Proc.new {
puts "item is true"
1
}, :else => Proc.new {
puts "item is false"
0
};
puts "actual = #{actual.inspect}"
# using built-in if & then
expected = if item
puts "item is true"
1
else
puts "item is false"
0
end
puts "expected = #{expected.inspect}"
if actual != expected
puts "FIXME: multi-block if failed"
end
end

def test_single_block_if_not(item)
actual = (not item).if Proc.new{ puts "FALSE"; 0 }
puts "actual = #{actual.inspect}"
# using built-in if not
expected = if (not item)
puts "FALSE"
0
end
puts "expected = #{expected.inspect}"
if actual != expected
puts "FIXME: multi-block if failed"
end
end

# try multiblock if.
true.if Proc.new {
puts "yep"
}, :else => Proc.new {
puts "nah"
}
false.if Proc.new {
puts "yep"
}, :else => Proc.new {
puts "nah"
}

# try singleblock if.
true.if Proc.new {
puts "yep"
}
false.if Proc.new {
puts "never executes"
}

[1, 0, "Hello", false, true, nil].each { |item|
puts "--- #{item.inspect} ---"
test_single_block_if(item)
test_single_block_if_not(item)
test_multi_block_if(item)
}
end

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

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

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

Csaba Henk

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

In TrueModule #ifthen stores the block passed to it. #else is
where the block is called actually.

It's possible that between the calls to #ifthen and #else a call occurs
to #ifthen in another thread, and the proper block gets overwritten.

It's easy to get around this, though.

module TrueModule
def ifthen &b
@blh ||= {}
@blh[Thread.current] = b
self
end
# rewrite other methods accordingly
...
end

I'm using this technique in a general multiblock framework (soon to be
announced).
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.

Yes, of course, what I put together is quite rudimentary. And defining
#when/#on_true and the counterpart methods is quite handy.
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.

That's the Tim Toady part of Ruby :).
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 {
...
};

Why to wait for "native" keyword arguments? And why not just "proc"?

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

is not uglier imho.

If there is problem with the current
sugared-hash-as-keyword-container approach, then that's not it's
ugliness [I mean I don't think it's ugly], but the lack of reflection.

These faked multiblocks can be used as a keyword mechanism with reflection.
The only drawback is that you have to use some kind of finalizer method
to express that there are no more components. Make that short, and it's
liveable with. It's definitely better than writing out the procs.

In the present case, we can tune the code to make

foo.if{ ... }.fi
foo.else{ ... }
foo.if{ ... }.else{ ... }

work (both with false/nil and the rest of the world), or, if we put more
emphasis on syntactical consistency, we can rather have

foo.else{ ... }.fi
foo.if{ ... }.else{ ... }.fi

for the latter two.
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: {
...
};

I don't think the above would be essentially uglier. ".fi" is just three
characters, it's not a perversion to think of it as a delimiter...
And seriously nice if you didn't have to use commas to separate
arguments when not using "()" style calling syntax.

Hm, your code give me an idea. If ruby had real keyword args, we could
allow the omission of "proc" for keyword arguments without breaking the
present block semantics... that's interesting, as we couldn't introduce
a general "omit proc in method argument" solution without either hacking
the semantics of block arg passing, or putting syntactical cruft
elsewhere, which is then quite beating the purpose.

And... you don't even need proper keyword args for this. With the same
logic, you could just let "proc" being omitted within hash literals.

That is, make

{ 1 => {|i| i+1} }

be the same as

{ 1 => proc{|i| i+1} }

of current ruby...
Here's the modified code for the curious:
Thanks!


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.

I didn't imply you are a Smalltalker, be it a good or a bad thing (I'd
guess it's the former). I spoke of Smalltalk(ers) in general. Maybe
someone more involved with Smalltalk could then tell us whether she or
he is proud of these properties of the Smalltalk conditional :)

Cheers,
Csaba
 
S

Steven Shaw

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


In TrueModule #ifthen stores the block passed to it. #else is
where the block is called actually.

It's possible that between the calls to #ifthen and #else a call occurs
to #ifthen in another thread, and the proper block gets overwritten.

It's easy to get around this, though.

module TrueModule
def ifthen &b
@blh ||= {}
@blh[Thread.current] = b
self
end
# rewrite other methods accordingly
...
end

I'm using this technique in a general multiblock framework (soon to be
announced).

I see. I didn't realise that the blh variable was a global for some
reason (though the @ should have given it away). So your solution turns
this global into a thread local. Nice.

The advantage with my solution is that you don't need the global at all.
But do you end up with the uglier syntax...
That's the Tim Toady part of Ruby :).

TMTOWTDI pronouced as "Tim Toady". lol - I hadn't seen that before :).
Why to wait for "native" keyword arguments? And why not just "proc"?

Yes, I found proc - mentioned in an earlier post you must have missed. I
do prefer it over Proc.new because it's more concise.
Hm, your code give me an idea. If ruby had real keyword args, we could
allow the omission of "proc" for keyword arguments without breaking the
present block semantics... that's interesting, as we couldn't introduce
a general "omit proc in method argument" solution without either hacking
the semantics of block arg passing, or putting syntactical cruft
elsewhere, which is then quite beating the purpose.

And... you don't even need proper keyword args for this. With the same
logic, you could just let "proc" being omitted within hash literals.

That is, make

{ 1 => {|i| i+1} }

be the same as

{ 1 => proc{|i| i+1} }

of current ruby...

Yes, if that's possible it would be nice.

Thanks Csaba and sorry for the late followup.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top