After several months programming with Ruby scripting language, and related libraries, I got quite used to the intensive use of iterators and blocks (closures) that give Ruby programs a particular coding flavour. Switching back to Eiffel, I wondered how to reproduce this coding style in Eiffel programs: apart from discussing about this being an healthy practice or not, let's see some funny examples !
Simple iterators
One of the most frequently used iterators in Ruby is the each method in container classes like Array:
languages.each do |lang|
puts "I like " + lang
end
this fragment of code basically gives:
I like Eiffel I like Ruby I like Python I like C++ I like Perl I like Java
The same result can be achieved in Eiffel by means of the do_all iterator of ARRAY class; the basic step is to substitute Ruby blocks (the portion do |varbind| ... end) with Eiffel inline agents:
languages: ARRAY[STRING] is
--
once
Result := <<"Eiffel", "Ruby", "Python", "C++", "Perl", "Java" >>
end
test is
--
do
languages.do_all (agent (lang: STRING) do
print("I like " + lang + "%N")
end)
end
Integer class iterators
Other common Ruby iterators can be found in the Integer class:
1.upto(5) do |n| puts "going up: #{n}" end
10.downto(5) do |n| puts "going down: #{n}" end
The output of these three instructions is the following:
Hello, world! Hello, world! Hello, world! Hello, world! going up: 1 going up: 2 going up: 3 going up: 4 going up: 5 going down: 10 going down: 9 going down: 8 going down: 7 going down: 6 going down: 5
In Eiffel, INTEGER class is not equipped with such iterators, one could think of an INTEGER_ITERATOR class that implements them or, alternatively, write the following features that make the first integer argument explicit:
times (n: INTEGER; action: PROCEDURE[ANY,TUPLE]) is
-- repeat `action' for `n' times
local
l_times: INTEGER
do
from
l_times := 0
until
l_times = n
loop
action.call ([l_times])
l_times := l_times + 1
end
end
upto (min,max: INTEGER; action: PROCEDURE[ANY,TUPLE]) is
--
require
min_lt_max: min <= max
local
l_index: INTEGER
do
from
l_index := min
until
l_index > max
loop
action.call([l_index])
l_index := l_index + 1
end
end
downto (max,min: INTEGER; action: PROCEDURE[ANY,TUPLE]) is
--
require
min_lt_max: min <= max
local
l_index: INTEGER
do
from
l_index := max
until
l_index < min
loop
action.call([l_index])
l_index := l_index - 1
end
end
Keeping in mind the Ruby example, these features should be called as follows:
print("time: " + num.out + "%N")
end)
upto(1,5, agent (num: INTEGER) do
print("going up: " + num.out + "%N")
end)
downto(10,5, agent (num: INTEGER) do
print("going down: " + num.out + "%N")
end)
Reading a file the Ruby way
And now a more advanced example, in Ruby a text file can be read as follows:while (line = infile.gets)
puts line
end
end
The open method in class File takes as argument a block operating on the file itself, it also takes care of closing the file after block execution, indeed a neat and compact style (but maybe not so explicit: should it be called open_do_something_and_close ?).
So, let's mimic this behaviour in Eiffel:
RUBY_FILE
feature
open (name: STRING; action: PROCEDURE[ANY,TUPLE]) is
--
local
l_file: PLAIN_TEXT_FILE
do
create l_file.make_open_read (name)
action.call ([l_file])
l_file.close
end
end
class RUBY_FILE exports the open feature as seen for the File class in Ruby, an usage example is the following:
test_ruby_file is
--
local
l_ruby_file: RUBY_FILE
do
create l_ruby_file
l_ruby_file.open ("myfile.rb", agent (f: PLAIN_TEXT_FILE) do
from
f.start
until
f.after
loop
f.readline
print (f.last_string + "%N")
end
end)
end
have fun with Eiffel and Ruby !




integer intervals and file line processing
upto can be done in Eiffel with integer intervals:
it doesn't currently allow descending but we're not too far...
For reading files I'd rather see an iterator like API rather, where the processing agent takes a line, than having to iterate lines by hand: solves 2 problems in one go.