Work In Progress - Eiffel Whirlwind
Author |
Message |
wtd
|
Posted: Sat Jul 14, 2007 3:53 pm Post subject: 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
code: | class
HELLO_WORLD
end |
Class names are always all-caps, and have words separated by underscores.
Let's Add a Feature
code: | 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
code: | class
HELLO_WORLD
feature
Message : STRING is "Hello, world!"
Surly_message : STRING is "Hey stupid world..."
end |
Let's Add a Routine
code: | 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
code: | 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.
code: | 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.
code: | 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.
code: | 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:
code: | $ se c -clean hello_world entry_point |
And run it with:
Which results in:
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
code: | 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
code: | 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:
code: | $ se c -clean hello_world |
Inhertitance and Some Polymorphism
code: | 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 |
code: | 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 |
code: | 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
code: | 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.
code: | 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 |
code: | 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 |
code: | 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".
code: | 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:
code: | $ se c hello_world.ace
$ ./hello_world
Hello, Mr. Foo Bar!
$ |
Making Choices
code: | 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
code: | 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
code: | 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.
code: | 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. |
|
|
|
|
 |
Sponsor Sponsor

|
|
 |
Clayton

|
Posted: Sat Jul 14, 2007 7:31 pm Post subject: 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
|
Posted: Sat Jul 14, 2007 7:33 pm Post subject: RE:Work In Progress - Eiffel Whirlwind |
|
|
Nope. Just makes it look nice. |
|
|
|
|
 |
Clayton

|
Posted: Sat Jul 14, 2007 7:34 pm Post subject: 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
|
Posted: Sat Jul 14, 2007 11:51 pm Post subject: RE:Work In Progress - Eiffel Whirlwind |
|
|
Yes. |
|
|
|
|
 |
rdrake

|
Posted: Tue Jul 17, 2007 2:34 am Post subject: 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!
code: | 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:
code: | se c -clean vector3d.e entry_point |
|
|
|
|
|
 |
wtd
|
Posted: Tue Jul 17, 2007 2:37 am Post subject: RE:Work In Progress - Eiffel Whirlwind |
|
|
Excellent! |
|
|
|
|
 |
Aziz

|
Posted: Tue Jul 17, 2007 8:24 am Post subject: RE:Work In Progress - Eiffel Whirlwind |
|
|
It seems very verbose to me, though interesting. What did you mean by manifestly-typed, wtd? |
|
|
|
|
 |
Sponsor Sponsor

|
|
 |
wtd
|
Posted: Tue Jul 17, 2007 1:54 pm Post subject: 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

|
Posted: Tue Jul 17, 2007 2:10 pm Post subject: 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
|
Posted: Tue Jul 17, 2007 5:34 pm Post subject: 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.
code: | Result := a + b + c |
code: | Result := a +
b +
c |
|
|
|
|
|
 |
Aziz

|
Posted: Tue Jul 17, 2007 5:38 pm Post subject: 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
|
Posted: Tue Jul 17, 2007 5:42 pm Post subject: RE:Work In Progress - Eiffel Whirlwind |
|
|
code: | class
FOO
feature
baz : STRING is
do
Result := "Foo"
end
end |
code: | class
BAR
inherit
FOO
redefine baz end
feature
baz : STRING is
do
Result := "Bar"
end
end |
code: | class
TEST
create
make
feature
make is
local
f : FOO
do
create { BAR } f
io.put_line(f.baz)
end
end |
|
|
|
|
|
 |
Aziz

|
Posted: Tue Jul 17, 2007 6:05 pm Post subject: RE:Work In Progress - Eiffel Whirlwind |
|
|
Running test would output "Bar". |
|
|
|
|
 |
|
|