
-----------------------------------
wtd
Sat Jul 14, 2007 3:53 pm

Work In Progress - Eiffel Whirlwind
-----------------------------------
Theory Warning!

Eiffel is a purely object-oriented, statically-typed, manifestly-typed programming language.

As it is purely object-oriented, everything is an object.  Everything exists within a class.  

All things within classes are "features" and are either variables, constants, or routines.  Routines are either functions, which return a new object and make no changes to the class, or procedures which return nothing, and do have side-effects on the class.

And that's enough theory for now.

The Simplest Possible Class

class
   HELLO_WORLD
end

Class names are always all-caps, and have words separated by underscores.

Let's Add a Feature

class
   HELLO_WORLD
feature
   Message : STRING is "Hello, world!"
end

Here our feature is a constant string.  Because it's a constant, it's capitalized.

Let's Add Another

class
   HELLO_WORLD
feature
   Message : STRING is "Hello, world!"
   Surly_message : STRING is "Hey stupid world..."
end

Let's Add a Routine

class
   HELLO_WORLD
feature
   Message : STRING is "Hello, world!"
   Surly_message : STRING is "Hey stupid world..."
   
   say_hello is
      do
         io.put_line(Message)
      end
end

A Variable

class
   HELLO_WORLD
feature
   Message : STRING is "Hello, world!"
   Surly_message : STRING is "Hey stupid world..."
   
   name : STRING
   
   say_hello is
      do
         io.put_line(Message)
      end
end

Initializing That Variable

We need a creation routine.  They're just like regular routines.  Except that in their case they also get a mention in the create clause.  The "make" routine is the most commonly used name.

class
   HELLO_WORLD
create
   make
feature
   name : STRING
   
   make(initial_name : STRING) is
      do
         name := initial_name
      end
   
   say_hello is
      do
         io.put_line("Hello, " + name + "!")
      end
end

This is Just a Class

We have yet to create a program.  A program needs an entry point.  

And entry point in an Eiffel program is just a creation routine.  By default the compiler will choose the "make" routine, but we can tell it to use any creation routine as the entry point.  That will be useful, since "make" in the above does very little in terms of output.

class
   HELLO_WORLD
create
   make, entry_point
feature
   name : STRING
   
   make(initial_name : STRING) is
      do
         name := initial_name
      end
      
   entry_point is
      do
         say_hello
      end
   
   say_hello is
      do
         io.put_line("Hello, " + name + "!")
      end
end

There's a problem, though.

When entry_point is executed, nothing has actually been assigned to "name" meaning that it remains Void.

class
   HELLO_WORLD
create
   make, entry_point
feature
   name : STRING
   
   make(initial_name : STRING) is
      do
         name := initial_name
      end
      
   entry_point is
      do
         make("Foo")
      
         say_hello
      end
   
   say_hello is
      do
         io.put_line("Hello, " + name + "!")
      end
end

Now, we can compile with:

$ se c -clean hello_world entry_point

And run it with:

$ ./hello_world

Which results in:

Hello, Foo!

The "-clean" option removes all of the intermediate files the SmartEiffel compiler creates as it compiles the code to C, and then uses a C compiler to generate the final executable.

Another Class

class
   NAME
create 
   make
feature
   first_name, last_name : STRING
   
   make(f, l : STRING) is
      do
         first_name := f
         last_name := l
      end
      
   full_name : STRING is
      do
         Result := first_name + " " + last_name
      end
     
end

Using It

class
   HELLO_WORLD
create
   make
feature
   name : NAME
   
   make is
      do
         create name.make("Foo", "Bar")
         
         say_hello
      end
   
   say_hello is
      do
         io.put_line("Hello, " + name.full_name + "!")
      end
end

Compile with:

$ se c -clean hello_world

Inhertitance and Some Polymorphism

class
   NAME
create 
   make
feature
   first_name, last_name : STRING
   
   make(f, l : STRING) is
      do
         first_name := f
         last_name := l
      end
      
   full_name : STRING is
      do
         Result := first_name + " " + last_name
      end
     
end

class
   FORMAL_NAME
inherit
   NAME
      redefine full_name end
create 
   make_with_title
feature
   title : STRING
   
   make_with_title(t, f, l : STRING) is
      do 
         make(f, l)
         
         title := t
      end
      
   full_name : STRING is   
      do
         Result := title + " " + Precursor
      end
end

class
   HELLO_WORLD
create
   make
feature
   name : NAME
   
   make is
      do
         create { FORMAL_NAME } name.make_with_title("Foo", "Bar")
         
         say_hello
      end
   
   say_hello is
      do
         io.put_line("Hello, " + name.full_name + "!")
      end
end

Deferred Features

class
   GREETABLE
feature
   greetable_name : STRING is 
      deferred
      end
      
   greeting : STRING is
      do
         Result := "Hello, " + greetable_name + "!"
      end
      
   greet is
      do
         greet_on(io)
      end
      
   greet_on(output : OUTPUT_STREAM) is
      do
         output.put_line(greeting)
      end
end

And now to rewrite our previous code to take advantage of this.

class
   NAME
inherit
   GREETABLE
create 
   make
feature
   first_name, last_name : STRING
   
   make(f, l : STRING) is
      do
         first_name := f
         last_name := l
      end
      
   greetable_name, full_name : STRING is
      do
         Result := first_name + " " + last_name
      end
end

class
   FORMAL_NAME
inherit
   NAME
      redefine greetable_name, full_name end
create 
   make_with_title
feature
   title : STRING
   
   make_with_title(t, f, l : STRING) is
      do 
         make(f, l)
         
         title := t
      end
      
   greetable_name, full_name : STRING is   
      do
         Result := title + " " + Precursor
      end
end

class
   HELLO_WORLD
create
   make
feature   
   make is
      local
         name : NAME
      do
         create { FORMAL_NAME } name.make_with_title("Mr.", "Foo", "Bar")
         
         name.greet
      end
end

Building Made Easier

Instead of typing out the SmartEiffel compiler command each time, let's use an ACE file named "hello_world.ace".

system
   "hello_world"
root
   HELLO_WORLD(hello_world_cluster):make
default
   collect(yes);
cluster
   standard: "${path_lib}/loadpath.se"
   hello_world_cluster: "."
generate
   clean(yes);
end

First we define the name of the system being created.

Then we specify the root class and the creation routine to use as the entry point.

Then we set a series of defaults.  Here the only one I've bothered with is turning garbage collection on.

Next we define clusters.  Clusters allow us to group classes, and tell the compiler where to look for classes.  The "standard" cluster simply points to the standard library, with the help of an environment variable.

The hello_world_cluster points to the current directory.

Within the generate clause I can set a series of options for the generated C code.  I tell it to clean up after the build.

I can run this with:

$ se c hello_world.ace
$ ./hello_world
Hello, Mr. Foo Bar!
$

Making Choices

class
   NAME
inherit
   GREETABLE
      redefine greeting end
create 
   make
feature
   first_name, last_name : STRING
   
   make(f, l : STRING) is
      do
         first_name := f
         last_name := l
      end
      
   greeting : STRING is
      do
         if first_name = last_name then
            Result := "What a stupid name..."
         else
            Result := Precursor
         end
      end
      
   greetable_name, full_name : STRING is
      do
         Result := first_name + " " + last_name
      end
end

Iterating

class
   HELLO_WORLD
create
   make
feature   
   make is
      local
         name : NAME
         iter : ITERATOR[CHARACTER]
      do
         create { FORMAL_NAME } name.make_with_title("Mr.", "Foo", "Bar")
         
         from
            iter := name.first_name.get_new_iterator
         until 
            iter.is_off
         loop
            put_char_on_line(iter.current)
            
            iter.next
         end
      end
      
   print_char_on_line(ch : CHARACTER) is
      do
         io.put_character
         io.put_new_line
      end
end

Streamlining with Agents

class
   HELLO_WORLD
create
   make
feature   
   make is
      local
         name : NAME
      do
         create { FORMAL_NAME } name.make_with_title("Mr.", "Foo", "Foo")
         
         name.greet
         
         name.first_name.do_all(agent io.put_character(?))
      end
end



Polymorphism Bites Back

So with NAME and FORMAL_NAME we saw some polymorphism.  Even if we specified that a variable was a NAME, and assigned a FORMAL_NAME to it (via creation or otherwise), the FORMAL_NAME version of full_name would be used.

This is really really good.  But...

What about the title feature?  NAME has no such thing.  As such, anything we have guaranteed only to be a name cannot be guaranteed to have a title feature.  

class
   HELLO_WORLD
create
   make
feature   
   make is
      local
         name : NAME
         another_name : FORMAL_NAME
      do
         create { FORMAL_NAME } name.make_with_title("Mr.", "Foo", "Foo")
         
         name.greet
         
         another_name ?= name
         
         io.put_line(another_name.title)
      end
end

The ?= operator serves the same purpose as :=, but evaluates whether or not it will succeed at run-time, rather than compile-time.

-----------------------------------
Clayton
Sat Jul 14, 2007 7:31 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Awesome stuff wtd! Definitely something I have to check out. Quick query, does indentation have anything to do with how code is seperated (like Python par example)?

-----------------------------------
wtd
Sat Jul 14, 2007 7:33 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Nope.  Just makes it look nice.

-----------------------------------
Clayton
Sat Jul 14, 2007 7:34 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
so under the create clause nothing to do with indentation then? Just as long as you have a comma in between each word I'm guessing?

-----------------------------------
wtd
Sat Jul 14, 2007 11:51 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Yes.

-----------------------------------
rdrake
Tue Jul 17, 2007 2:34 am

Re: Work In Progress - Eiffel Whirlwind
-----------------------------------
For the benefit of those interested, the following sample was created by myself with (massive) help from wtd.  It shows some overriding of methods, hiding of methods, and probably some other stuff too.  Enjoy!

class
  VECTOR3D
inherit
  ANY
    redefine out, print_on, is_equal end
create
  make, entry_point
feature
  x, y, z : REAL
  
  make (x_init, y_init, z_init : REAL) is
    do
      x := x_init
      y := y_init
      z := z_init
    end
    
  entry_point is
    local
      v, v1 : VECTOR3D
    do
      create v.make(1, 2, 3)
      create v1.make(3, 2, 1)
      v.magnitude.print_on(io)
      v.normalize(v1).print_on(io)
      v.cross_product(v1).print_on(io)
      v.is_equal(v1).print_on(io)
    end
    
  normalize (vector : VECTOR3D) : VECTOR3D is
    do
      create Result.make(vector.x / magnitude, vector.y / magnitude, vector.z / magnitude)
    end
    
  dot_product (vector : VECTOR3D) : REAL is
    do
      Result := x * vector.x + y * vector.y + z * vector.z
    end
    
  cross_product (vector : VECTOR3D) : VECTOR3D is
    do
      create Result.make(vector.y * z - vector.z * y, vector.z * x - vector.x * z, vector.x * y - vector.y * x)
    end
  
  out : STRING is
    do
      Result := "(" + x.to_string + ", " + y.to_string + ", " + z.to_string + ")"
    end
  
  print_on (output : OUTPUT_STREAM) is
    do
      output.put_line(out)
    end
    
  is_equal(other : VECTOR3D) : BOOLEAN is
    do
      Result := x.is_equal(other.x) and then y.is_equal(other.y) and then z.is_equal(other.z)
    end
feature {VECTOR3D}
  magnitude : REAL is
    do
      Result := (x ^ 2 + y ^ 2 + z ^ 2).sqrt
    end
end

Naturally you compile it with this:
se c -clean vector3d.e entry_point

-----------------------------------
wtd
Tue Jul 17, 2007 2:37 am

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Excellent!

-----------------------------------
Aziz
Tue Jul 17, 2007 8:24 am

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
It seems very verbose to me, though interesting. What did you mean by manifestly-typed, wtd?

-----------------------------------
wtd
Tue Jul 17, 2007 1:54 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Types have to be explicitly declared, as in Java, C++, etc.  This is as opposed to something like O'Caml.

As for verbosity, Bertrand Meyer's perspective, I believe, was that one would have verbose design documents to explain away terse code, so he felt it better to just have code that was as self-documenting as possible.

-----------------------------------
Aziz
Tue Jul 17, 2007 2:10 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Is inherit ANY the likes of deriving from a base object class?

Also, could you expand on the 'create' routines, etc?

EDIT: What would you do if you had a long line? I take it line breaks are the statement separators.

-----------------------------------
wtd
Tue Jul 17, 2007 5:34 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Yes, ANY is the root of the inheritance hierarchy.  You automatically inherit from ANY, but sometimes it needs to be explicit so that you can redefine, rename or undefine features from ANY.

Create routines are routines which may be used when an object is created in order to give it an initial state.  Create routines may be used elsewhere in the program as well.

You need do nothing to continue long lines.

Result := a + b + c

Result := a +
          b +
          c

-----------------------------------
Aziz
Tue Jul 17, 2007 5:38 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
What about polymorphism? Do virtual methods need to be defined explicitly, such as in C#, or or they more like Java? And how would this be handled when you undefine a feature?

-----------------------------------
wtd
Tue Jul 17, 2007 5:42 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
class
   FOO
feature
   baz : STRING is
      do 
         Result := "Foo"
      end
end

class 
   BAR
inherit
   FOO
      redefine baz end
feature
   baz : STRING is
      do
         Result := "Bar"
      end
end

class
   TEST
create
   make
feature
   make is
      local
         f : FOO
      do
         create { BAR } f

         io.put_line(f.baz)
      end
end

-----------------------------------
Aziz
Tue Jul 17, 2007 6:05 pm

RE:Work In Progress - Eiffel Whirlwind
-----------------------------------
Running test would output "Bar".
