Eiffel for Rubyists

by Francesco Ferreri (modified: 2008 Aug 22)

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 = Array.new(['Eiffel', 'Ruby', 'Python', 'C++', 'Perl', 'Java']) 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:

feature 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:

4.times do |t| puts "Hello, world!" end 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:

feature 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:

times(4, agent (num: INTEGER) do 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:

File.open("myfile.rb", "r") do |infile| 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:

class 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:

feature 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 !

Comments
  • Franck Arnaud (8 years ago 19/11/2008)

    integer intervals and file line processing

    upto can be done in Eiffel with integer intervals:

    (1 |..| 5).do_all (agent ...)

    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.

  • Daniel Furrer (8 years ago 27/5/2009)

    closure...

    Although generally somewhat more verbose, some patterns known from more functional languages can sometimes lead to very elegant solutions in Eiffel as well.

    What I found a bit disappointing is that an inline agent can not capture local variables of the function it is being defined in though. (or am I missing something? Is this intended behaviour?)

    create_function: FUNCTION[ANY, TUPLE[INTEGER], INTEGER] local x: INTEGER do .... compute some value for x ... Result := agent (a: INTEGER) do Result := a + x end end

    • Manu (8 years ago 27/5/2009)

      You can by simply provide the value. This was a conscious decision from the ECMA specification no to allow reuse of the parent locals because the semantic could become complex (e.g. what happens if the parent scope is gone?). The solution is to write your code as:

      create_function: FUNCTION [ANY, TUPLE [INTEGER], INTEGER] local x: INTEGER do .... compute some value for x ... Result := agent (local_x, a: INTEGER) do Result := a + local_x end (x, ?) end

  • Colin Adams (8 years ago 27/5/2009)

    Same as call agents

    You aren't missing anything.

    Inline agents are functionally identical to call agents, but they ensure that your code is less readable, and less maintainable.

    Colin Adams

  • david.anderson (7 years ago 27/8/2009)

    Reference Guide

    Hi, Where can I See the full reference guide for eiffel? Thanks!

    • Manu (7 years ago 27/8/2009)

      Most of the EiffelStudio documentation is at http://docs.eiffel.com and it includes a language description. Or you can go to the ECMA website and fetch the PDF of the Eiffel standard.

      • david.anderson (7 years ago 28/8/2009)

        The only reference guide found on this page was this one: http://docs.eiffel.com/book/method/quick-reference-eiffel-language Which contains nothing.

        Is there any online full docs, like the Java Docs? I'm trying to learn eiffel, and this wold be very helpful

        Thanks!

        • Jocelyn Fiat (4 years ago 3/6/2013)

          See pages from http://docs.eiffel.com/book/method/quick-reference-eiffel-programming-language