
-----------------------------------
wtd
Sat Aug 27, 2005 3:05 pm

Eiffel and Java, a comparison by RIT
-----------------------------------
http://66.102.7.104/search?q=cache:gPLjkZQbi68J:www.cs.rit.edu/~jeh/eiffel/JvsE.html+eiffel+site:rit.edu&hl=en&client=firefox

Oh, and why they chose Eiffel for intro CS.  :)

http://66.102.7.104/search?q=cache:LHP9doWEYzAJ:www.cs.rit.edu/~jeh/Papers/Eif-joop.html+eiffel+site:rit.edu&hl=en&client=firefox

-----------------------------------
Hikaru79
Sat Aug 27, 2005 10:36 pm


-----------------------------------
Your second link doesn't seem to work :(

-----------------------------------
wtd
Sat Aug 27, 2005 11:04 pm


-----------------------------------
Do a Google search for "eiffel site:rit.edu".  :)

-----------------------------------
rizzix
Sun Aug 28, 2005 10:55 am


-----------------------------------
ha.. that report is soo outdated... and the advantages are not that great... you should check out AspectJ.. ha... makes Eiffel look bad.

-----------------------------------
wtd
Sun Aug 28, 2005 3:41 pm


-----------------------------------
Is it?  

Where's the DbC is Java?  Pre-conditions, post-conditions, class invariants that are inherited, and the ability to give names to the exceptions they throw.

The syntax is simpler.  Tell me that Java has only one way to loop.

test_loop is
   local
      index : INTEGER
   do
      from
         index := 1
      until 
         index >= 10
      loop
         std_output.put_integer(index)
         std_output.put_new_line

         index := index + 1
      end
   end

Where are the externally read-only attributes?  Where's the strict enforcement of encapsulation?

Where are the real generic classes (as opposed to some auto-boxing and Object casting hackery)?

Where's the feature adaptation to resolve name clashes?

Where's the high level of control over feature access (beyond simply public, private and protected)?

Where's the strict separation of procedures and functions?

Can you use any procedure to set up the initial state of an object?

-----------------------------------
rizzix
Sun Aug 28, 2005 4:34 pm


-----------------------------------
oh? yes.. pre, post and a heck a lot more is possible though aspects.. including stuff that even eiffel can't do..

every thing else you mentioned is soo useless and pointless i'm not even going to bother replying.. except for this one.. if what you say is true.. i'll have to simply accept that eiffel has a greater advantage over java in this respect: "Where's the high level of control over feature access (beyond simply public, private and protected)?"

-----------------------------------
wtd
Sun Aug 28, 2005 7:46 pm


-----------------------------------
So this:

public class MyClass {
  private String name;
  public boolean invarName() { return name != null; }

  public void    setName (String n) { name = n; }
  public boolean preSetName (String n) { return n != null; }
  public String  getName () { return name; }
  public boolean postGetName (String result) { return result != null; }

  public String  doIt (int n, String s) {...}
  public boolean preDoIt (int n, String s) {
    return n > 10 && s != null;
  }
  public boolean postDoIt (String result, int n, String s) {
    return result != null;
  }
  ...
}

Is prefereable to:

class 
   MY_CLASS

feature

   name : STRING

   set_name(new_name : STRING) is
      require
         new_name /= Void
      do
         name := new_name
      end

   do_it(n : INTEGER, s : STRING) : STRING is
      require 
         n > 10 and then s /= Void
      do
         ...
      end

invariant
   name /= Void

end

Is a simple syntax pointless?  I know a lot of people struggling with C-like syntaxes who would disagree.

Are externally read-only attributes useless?  Notice I declared my "name" attribute openly, and no harm came of it.  That's because I can't modify it from outside the class.  At the same time, it saved me from having to write a "get" accessor.

I can't do the bad thing and break encapsulation in Eiffel.  It just isn't allowed.

Separation of functions and procedures doesn't matter?  Not having functions capable of mutating the object makes a tremendous amount of sense, and saves much confusion.  It enforces the idea that each routine should do one small job, thus making it easier to isolate the source of errors.  Consider a simple IO example:

class 
   TEST

creation 
   make

feature

   make is
      do
         std_output.put_string("Your name is? ")
         std_input.read_line
         std_output.put_string("Hello " + std_input.last_string + "!")
         std_output.put_new_line
      end

end

The act of reading the line in, and then actually getting what it read become two separate features.

As for feature access... you're right to envy it.  The class ank account example:

class
   BANK_ACCOUNT

feature { TELLER }

   balance : INTEGER

end

The balance becomes available only to objects of the TELLER class.

-----------------------------------
wtd
Sun Aug 28, 2005 8:45 pm


-----------------------------------
Another semantic simplicitythat Eiffel brings to bear is that there are no unatural breaks in execution.  There are no gotos, breaks, continues, or returns.

This eliminates another huge class of errors by making it easier to trace the flow of execution.

Also, where is Java's ability to create value types?  Consider:

bar.e
class 
   BAR

creation
   make

feature

   foo, baz : INTEGER

   make is
      do
         foo := 1
         baz := 2
      end

   inc is
      do
         foo := foo + 1
         baz := baz + 1
      end

end

test.e
class 
   TEST

creation 
   make

feature
   
   inc(input : BAR) is
      do
         input.inc
      end

   make is
      local 
         b : BAR
      do
         create b.make
        
         std_output.put_integer(b.foo) ; std_output.put_character(',')
         std_output.put_integer(b.baz) ; std_output.put_new_line

         inc(b)

         std_output.put_integer(b.foo) ; std_output.put_character(',')
         std_output.put_integer(b.baz) ; std_output.put_new_line
      end

end

This outputs:

1,2
2,3

And now, compare that to:

bar.e
expanded class 
   BAR

feature

   foo, baz : INTEGER

   make is
      do
         foo := 1
         baz := 2
      end

   inc is
      do
         foo := foo + 1
         baz := baz + 1
      end

end

test.e
class 
   TEST

creation 
   make

feature
   
   inc(input : BAR) is
      do
         input.inc
      end

   make is
      local 
         b : BAR
      do
         b.make
        
         std_output.put_integer(b.foo) ; std_output.put_character(',')
         std_output.put_integer(b.baz) ; std_output.put_new_line

         inc(b)

         std_output.put_integer(b.foo) ; std_output.put_character(',')
         std_output.put_integer(b.baz) ; std_output.put_new_line
      end

end

Which prints:

1,2
1,2

How do I accomplish this in Java - creating objects which are handled by value rather than by reference?

-----------------------------------
wtd
Sun Aug 28, 2005 9:03 pm


-----------------------------------
Let's consider another syntactic and semantic simplicity.  Exception handling occurs only at the feature-level.  That is, you cannot have arbitrary exception handling blocks.  

This further encourages concise, focused features, rather than lengthy methods.

Additionally, with Eiffel's exception handling you have the "retry" instruction which allows you to "fix" variables, and then run the feature again.  

Imagine if you have a queue class, and you try to add something, but there isn't room for it.  Your exception handler could grow the queue by one slot, then retry the method.

-----------------------------------
wtd
Sun Aug 28, 2005 10:09 pm


-----------------------------------
Oh yes, and no overloaded features.  You can't have something like:

class
   TEST

feature

   foo : STRING is
      do 
         Result := "foo"
      end

   foo(n : INTEGER) : STRING is
      local
         counter : INTEGER
      do
         from
            Result := ""
            counter := 0
         until 
            counter > n
         loop
            Result := Result + "foo"
            counter := counter + 1
         end
      end

end

Seems to be a nice way of reducing confusion.

-----------------------------------
rizzix
Mon Aug 29, 2005 3:33 pm


-----------------------------------
So this.... wait.. i'll get back to you on this.. i'll show you how it's actually done in java.. using aspects..

Are externally read-only attributes useless?  Notice I declared my "name" attribute openly, and no harm came of it.  That's because I can't modify it from outside the class.  At the same time, it saved me from having to write a "get" accessor. No it's best to access all properties through accessors.. properties are but private members of a class... that's how we program in java.. that's our philosophy.. you could argue that we have such a philosophy because we do not have read-only attributes... maybe.. but we see advantages in creating accessors since later on as the code matures.. we could come across a situation where you now need to do some sort of error checking or maybe more than just that.. maybe notify oberserver etc... then as you can see it's just a matter of modifying an accessor and implementing the necessary code.. the details are hidding from the client code... and there's no need to inform the clients that accessing data through the read-only attributes is now "deprecated".. it all happens behind the scenes..


Separation of functions and procedures doesn't matter?  Not having functions capable of mutating the object makes a tremendous amount of sense, and saves much confusion.  It enforces the idea that each routine should do one small job, thus making it easier to isolate the source of errors.  Consider a simple IO example:

class 
   TEST

creation 
   make

feature

   make is
      do
         std_output.put_string("Your name is? ")
         std_input.read_line
         std_output.put_string("Hello " + std_input.last_string + "!")
         std_output.put_new_line
      end

end

The act of reading the line in, and then actually getting what it read become two separate features. Nope! I just fail to see the point to this... functions that return "void" or nothing.. are just as good.. and it's feels more consistent...

-----------------------------------
rizzix
Mon Aug 29, 2005 3:39 pm


-----------------------------------
Another semantic simplicitythat Eiffel brings to bear is that there are no unatural breaks in execution.  There are no gotos, breaks, continues, or returns.

This eliminates another huge class of errors by making it easier to trace the flow of execution. yes and prevents some really cool algorithms from being implemented..

Also, where is Java's ability to create value types?...  funny you bring that up... we java programmers are actually greatful  we can't do this.. look at the complications involved if you do implement such a feature: the absolute necessity of implementing an = operator (or equivalent) and the so called copy-constuctor... all the annoying "have-to-follow" rules  present in the c++ lang..  just because it has the ability to create value types... either way.. there's abosolutly no point to it!

-----------------------------------
rizzix
Mon Aug 29, 2005 3:45 pm


-----------------------------------
Let's consider another syntactic and semantic simplicity.  Exception handling occurs only at the feature-level.  That is, you cannot have arbitrary exception handling blocks.  

This further encourages concise, focused features, rather than lengthy methods. care to elaborate? i don't understand.

Additionally, with Eiffel's exception handling you have the "retry" instruction which allows you to "fix" variables, and then run the feature again.

Imagine if you have a queue class, and you try to add something, but there isn't room for it.  Your exception handler could grow the queue by one slot, then retry the method. such kind or error handling take place at the function level (of a class etc)... and recursive functions allow for similar functionality...

-----------------------------------
rizzix
Mon Aug 29, 2005 3:49 pm


-----------------------------------
Oh yes, and no overloaded features.  You can't have something like:.... oh yes there's a good side to this allright... it does reduce confusion.. but sometimes overloading can be very usefull...

for ex consider:class Line {
    void setPoint(Point p) { .. }
    void setPoint(int x, int y) { .. }
} the overloaded method (the one that takes two int) simplifies coding.. creating the point for you when you don't already have one.. but this is a poor example.. there are better ones.. i just currently can't think of one..

-----------------------------------
wtd
Mon Aug 29, 2005 4:07 pm


-----------------------------------
Are externally read-only attributes useless?  Notice I declared my "name" attribute openly, and no harm came of it.  That's because I can't modify it from outside the class.  At the same time, it saved me from having to write a "get" accessor. No it's best to access all properties through accessors.. properties are but private members of a class... that's how we program in java.. that's our philosophy.. you could argue that we have such a philosophy because we do not have read-only attributes... maybe.. but we see advantages in creating accessors since later on as the code matures.. we could come across a situation where you now need to do some sort of error checking or maybe more than just that.. maybe notify oberserver etc... then as you can see it's just a matter of modifying an accessor and implementing the necessary code.. the details are hidding from the client code... and there's no need to inform the clients that accessing data through the read-only attributes is now "deprecated".. it all happens behind the scenes..

Why would you ever want to know when an access of that data occurs?  Surely the only time you care to know such things is when you actually change the state of an object?

Since Eiffel requires a procedure for modifying (as opposed to simply reading) the state of an object, that gives you ample opportunity to add extra logic.

Separation of functions and procedures doesn't matter?  Not having functions capable of mutating the object makes a tremendous amount of sense, and saves much confusion.  It enforces the idea that each routine should do one small job, thus making it easier to isolate the source of errors.  Consider a simple IO example:

class 
   TEST

creation 
   make

feature

   make is
      do
         std_output.put_string("Your name is? ")
         std_input.read_line
         std_output.put_string("Hello " + std_input.last_string + "!")
         std_output.put_new_line
      end

end

The act of reading the line in, and then actually getting what it read become two separate features. Nope! I just fail to see the point to this... functions that return "void" or nothing.. are just as good.. and it's feels more consistent...

Ah, but the problem is that a method which returns some value can also make changes to the state of the object.  This is bad, since it means a programmer can build methods which perform several operations, rather than short, concise, focused routines that make tracing errors easier.  :)

For instance, in my very simple example, if there's an error reading the input line, it will occur there, before any output is attempted.

A more Java/C-ish way of doing this might look like:

std_output.put_string("Hello, " + std_input.read_line + !")

-----------------------------------
wtd
Mon Aug 29, 2005 4:16 pm


-----------------------------------
Another semantic simplicitythat Eiffel brings to bear is that there are no unatural breaks in execution.  There are no gotos, breaks, continues, or returns.

This eliminates another huge class of errors by making it easier to trace the flow of execution. yes and prevents some really cool algorithms from being implemented..

How so?  Does it prevent short-circuiting searches?  For instance, if I have an array of SQUARE objects which have an "occupied" attribute, I might write a "is_board_empty" routine like so:

is_board_empty : BOOLEAN is
   local
      board_iterator : ITERATOR[SQUARE]
      occupied_square_found : BOOLEAN
   do
      board_iterator := board_squares.get_new_iterator
      occupied_square_found := False

      from
         board_iterator.start
      until
         board_iterator.is_off or else occupied_square_found
      loop
         occupied_square_found := board_iterator.item.occupied

         board_iterator.next
      end

      Result := not occupied_square_found
   end


Also, where is Java's ability to create value types?...  funny you bring that up... we java programmers are actually greatful  we can't do this.. look at the complications involved if you do implement such a feature: the absolute necessity of implementing an = operator (or equivalent) and the so called copy-constuctor... all the annoying "have-to-follow" rules  present in the c++ lang..  just because it has the ability to create value types... either way.. there's abosolutly no point to it!

Really?  Haven't you ever wanted pass-by-value rather than pass-by-reference for things other than simple types?

For instance, that Point class you mention.  Wouldn't it be nice to have it be mutable, but be able to pass it to a method without worrying about changes to it propogating beyond that method call?

-----------------------------------
rizzix
Mon Aug 29, 2005 6:28 pm


-----------------------------------
Why would you ever want to know when an access of that data occurs?  Surely the only time you care to know such things is when you actually change the state of an object? ah.. bad example on my part.. how about when you want to implement lazy evaluation? ;)

Since Eiffel requires a procedure for modifying (as opposed to simply reading) the state of an object, that gives you ample opportunity to add extra logic.yes, a mutator.. 


Ah, but the problem is that a method which returns some value can also make changes to the state of the object.  This is bad, since it means a programmer can build methods which perform several operations, rather than short, concise, focused routines that make tracing errors easier.  :)

For instance, in my very simple example, if there's an error reading the input line, it will occur there, before any output is attempted.

A more Java/C-ish way of doing this might look like:

std_output.put_string("Hello, " + std_input.read_line + !")so basically you're talking about immutable objects in java? yes.. i already know that java has no core language-feature that supports immutability out-of-the-box... infact this was an ongoing debate @ the java.net forums.. a debate concerning the best way to implement immutability in java.. they came up with some interesting solutions... the only problem is that it usually required boiler-plate code... they revised it and proposed a method of using Annotations to implement immutability.. we have to just wait and  see how it goes..

-----------------------------------
rizzix
Mon Aug 29, 2005 6:33 pm


-----------------------------------
How so?  Does it prevent short-circuiting searches?  For instance, if I have an array of SQUARE objects which have an "occupied" attribute, I might write a "is_board_empty" routine like so:...ehm... what are you trying to prove.. anyways.. my sudoku code uses the "continue" expression... kinda useful there.. but it does not take full advantage of it.. if i can dig up some code i've written a couple of years ago.. i might be able to show you some nifty algorithms (and by "algorithims" don't think I necessarily mean "generic" algorithms) that i used to optimise somecode.. and they make great use of those constructs..


Really?  Haven't you ever wanted pass-by-value rather than pass-by-reference for things other than simple types?

For instance, that Point class you mention.  Wouldn't it be nice to have it be mutable, but be able to pass it to a method without worrying about changes to it propogating beyond that method call? ehm.. no... never had to.. don't need to.. never will need to..

-----------------------------------
wtd
Mon Aug 29, 2005 6:41 pm


-----------------------------------
The key issue to my mind, is that Java lacks a number of useful compile-time restrictioins to ensure safety, and yet is not as flexible as something like Ruby or Python.

It's the worst of both worlds.  :)

-----------------------------------
wtd
Mon Aug 29, 2005 6:45 pm


-----------------------------------
Let's consider another syntactic and semantic simplicity.  Exception handling occurs only at the feature-level.  That is, you cannot have arbitrary exception handling blocks.  

This further encourages concise, focused features, rather than lengthy methods. care to elaborate? i don't understand.

There is no equivalent to the "try...catch...finally" blocks.  Let's consider an imaginary QUEUEadd(new_item : T) is
   require 
      new_item /= Void
   do
      internal_storage.append(new_item)
   rescue
      internal_storage.resize(1)

      retry
   end 

Additionally, with Eiffel's exception handling you have the "retry" instruction which allows you to "fix" variables, and then run the feature again.

Imagine if you have a queue class, and you try to add something, but there isn't room for it.  Your exception handler could grow the queue by one slot, then retry the method. such kind or error handling take place at the function level (of a class etc)... and recursive functions allow for similar functionality...

Yes, I could use an if to detect the potential problem before it becomes a problem, but that's adding extra code that's always evaluated, when in fact the need for it is likely to be the... umm... exception, rather than the rule.

As for recursion... I could call "add" recursively, but if I still throw the exception, that makes the call-stack more complex and tracing problems more difficult.

-----------------------------------
rizzix
Mon Aug 29, 2005 6:55 pm


-----------------------------------
Yes, I could use an if to detect the potential problem before it becomes a problem, but that's adding extra code that's always evaluated, when in fact the need for it is likely to be the... umm... exception, rather than the rule. that's ehm.. ironic.. 

As for recursion... I could call "add" recursively, but if I still throw the exception, that makes the call-stack more complex and tracing problems more difficult.complex? hmmvoid add(T new_item) { 
   if (new_item == null) return;
   try { 
       internal_storage.append(new_item);
   } catch (Exception e) {
       internal_storage.resize(1);
       add(new_item);
   }
} c'mon tell me that was simple ;)  and narrowing down the catch expression to catch only that out of bounds exception will guarantee you no stack problems..

-----------------------------------
rizzix
Mon Aug 29, 2005 7:06 pm


-----------------------------------
The key issue to my mind, is that Java lacks a number of useful compile-time restrictioins to ensure safety, and yet is not as flexible as something like Ruby or Python.

It's the worst of both worlds.  :) Or you could think of it as the best  compromise.  :wink:

-----------------------------------
wtd
Mon Aug 29, 2005 7:09 pm


-----------------------------------
I suppose in this case one of the important questions is:  is simple control flow important?  Certainly it works, and works well.  Functional programmers will be happy to tell you that not being able to "break", "return" or "continue" is a very good thing. 

Java lets you write good code, but it does very little to encourage it.  :)

As a sidenote, Ada95 expands on the ideas of control flow you'd find in C-ish languages and makes them really useful, since you can actually name loops, and then continue, or break out of a very specific loop.

I don't mind it here because it's taing an idea and really implementing it well.  It's probably a bad idea, but if you're gonna do something, don't do it halfway.  ;)

-----------------------------------
rizzix
Mon Aug 29, 2005 7:14 pm


-----------------------------------
As a sidenote, Ada95 expands on the ideas of control flow you'd find in C-ish languages and makes them really useful, since you can actually name loops, and then continue, or break out of a very specific loop. ehm.. yea you can do that in java as well..  :?

OUTER_LOOP:
for (...) 
    INNER_LOOP:
    for (...) {
        .
        .
        if (something)
            break OUTER_LOOP;
    }

-----------------------------------
wtd
Mon Aug 29, 2005 8:19 pm


-----------------------------------
Nifty.  Just out of curiosity, is there a "goto" that will go to a specific case in a "switch" statement?

-----------------------------------
rizzix
Mon Aug 29, 2005 9:41 pm


-----------------------------------
no. :P

although goto (and const) are reserved keywords... they are not used in the language..

-----------------------------------
wtd
Tue Aug 30, 2005 11:51 pm


-----------------------------------
Some code, to demonstrate a bit of the fun of agents.

class 
   ARRAY_TEST	

creation
   make

feature 
   
   make is
      do
         int_array := {ARRAY[INTEGER] 1, }
         create str_array.with_capacity(6, 1)
         
         int_array.do_all(agent str_array_add(?))

         if int_array.for_all(agent {INTEGER} < 10) then
            print_str_array
         end
      end

   int_array : ARRAY[INTEGER]
   str_array : ARRAY[STRING]

   str_array_add (new_value : INTEGER) is
      do
         str_array.add_last(new_value.to_string)
      end

   print_str_array is
      local
         iter : ITERATOR[STRING]
      do
         from
            iter := str_array.get_new_iterator
            iter.start
         until
            iter.is_off
         loop
            std_output.put_string(iter.item)
            std_output.put_new_line

            iter.next
         end
      end

end

Write the equivalent in Java.  :)
