Computer Science Canada

Eiffel and Java, a comparison by RIT

Author:  wtd [ Sat Aug 27, 2005 3:05 pm ]
Post subject:  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. Smile

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

Author:  Hikaru79 [ Sat Aug 27, 2005 10:36 pm ]
Post subject: 

Your second link doesn't seem to work Sad

Author:  wtd [ Sat Aug 27, 2005 11:04 pm ]
Post subject: 

Do a Google search for "eiffel site:rit.edu". Smile

Author:  rizzix [ Sun Aug 28, 2005 10:55 am ]
Post subject: 

ha.. that report is soo outdated... and the advantages are not that great... you should check out AspectJ.. ha... makes Eiffel look bad.

Author:  wtd [ Sun Aug 28, 2005 3:41 pm ]
Post subject: 

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.

code:
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?

Author:  rizzix [ Sun Aug 28, 2005 4:34 pm ]
Post subject: 

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)?"

Author:  wtd [ Sun Aug 28, 2005 7:46 pm ]
Post subject: 

So this:

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

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

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

code:
class
   BANK_ACCOUNT

feature { TELLER }

   balance : INTEGER

end


The balance becomes available only to objects of the TELLER class.

Author:  wtd [ Sun Aug 28, 2005 8:45 pm ]
Post subject: 

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
code:
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
code:
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
code:
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
code:
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?

Author:  wtd [ Sun Aug 28, 2005 9:03 pm ]
Post subject: 

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.

Author:  wtd [ Sun Aug 28, 2005 10:09 pm ]
Post subject: 

Oh yes, and no overloaded features. You can't have something like:

code:
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.

Author:  rizzix [ Mon Aug 29, 2005 3:33 pm ]
Post subject: 

wtd wrote:
So this....
wait.. i'll get back to you on this.. i'll show you how it's actually done in java.. using aspects..

wtd wrote:
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..


wtd wrote:
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:

code:
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...

Author:  rizzix [ Mon Aug 29, 2005 3:39 pm ]
Post subject: 

wtd wrote:
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..

wtd wrote:
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!

Author:  rizzix [ Mon Aug 29, 2005 3:45 pm ]
Post subject: 

wtd wrote:
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.

wtd wrote:
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...

Author:  rizzix [ Mon Aug 29, 2005 3:49 pm ]
Post subject: 

wtd wrote:
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:
code:
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..

Author:  wtd [ Mon Aug 29, 2005 4:07 pm ]
Post subject: 

rizzix wrote:
wtd wrote:
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.

rizzix wrote:
wtd wrote:
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:

code:
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. Smile

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:

code:
std_output.put_string("Hello, " + std_input.read_line + !")

Author:  wtd [ Mon Aug 29, 2005 4:16 pm ]
Post subject: 

rizzix wrote:
wtd wrote:
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:

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



rizzix wrote:
wtd wrote:
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?

Author:  rizzix [ Mon Aug 29, 2005 6:28 pm ]
Post subject: 

wtd wrote:
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? Wink

wtd wrote:
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..


wtd wrote:
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. Smile

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:

code:
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..

Author:  rizzix [ Mon Aug 29, 2005 6:33 pm ]
Post subject: 

wtd wrote:
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..


wtd wrote:
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..

Author:  wtd [ Mon Aug 29, 2005 6:41 pm ]
Post subject: 

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. Smile

Author:  wtd [ Mon Aug 29, 2005 6:45 pm ]
Post subject: 

rizzix wrote:
wtd wrote:
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 QUEUE[T] class' "add" feature.

code:
add(new_item : T) is
   require
      new_item /= Void
   do
      internal_storage.append(new_item)
   rescue
      internal_storage.resize(1)

      retry
   end


[quote="rizzix"]
rizzix wrote:
wtd wrote:
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.

Author:  rizzix [ Mon Aug 29, 2005 6:55 pm ]
Post subject: 

wtd wrote:
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..

wtd wrote:
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? hmm
Java:
void 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 Wink and narrowing down the catch expression to catch only that out of bounds exception will guarantee you no stack problems..

Author:  rizzix [ Mon Aug 29, 2005 7:06 pm ]
Post subject: 

wtd wrote:
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. Smile
Or you could think of it as the best compromise. Wink

Author:  wtd [ Mon Aug 29, 2005 7:09 pm ]
Post subject: 

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. Smile

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. Wink

Author:  rizzix [ Mon Aug 29, 2005 7:14 pm ]
Post subject: 

wtd wrote:
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.. Confused

code:
OUTER_LOOP:
for (...)
    INNER_LOOP:
    for (...) {
        .
        .
        if (something)
            break OUTER_LOOP;
    }

Author:  wtd [ Mon Aug 29, 2005 8:19 pm ]
Post subject: 

Nifty. Just out of curiosity, is there a "goto" that will go to a specific case in a "switch" statement?

Author:  rizzix [ Mon Aug 29, 2005 9:41 pm ]
Post subject: 

no. Razz

although goto (and const) are reserved keywords... they are not used in the language..

Author:  wtd [ Tue Aug 30, 2005 11:51 pm ]
Post subject: 

Some code, to demonstrate a bit of the fun of agents.

code:
class
   ARRAY_TEST   

creation
   make

feature
   
   make is
      do
         int_array := {ARRAY[INTEGER] 1, <<1, 2, 3, 4, 5, 6>>}
         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. Smile


: