[comp.lang.eiffel] Interesting problems to try in Eiffel

weiner@novavax.UUCP (Bob Weiner) (01/06/90)

(I assume you're using Eiffel 2.2.)

I should warn readers that ISE's Eiffel has much to recommend it and so
far I'm still apt to promote its use.  But in fairness I present here a
number of example tasks that should be simple but aren't.  Thus, they
either reflect my lack of Eiffel expertness or they reflect weaknesses
in the environment.

* Build a COMPLEX_NUM class.  This is one of the most basic classes needed
for any sort of scientific programming yet missing from the ISE provided
Eiffel libraries.  Naturally you'll want it to be an expanded type just
like an INTEGER.  But you'll find there is no way to define an expanded
class but only expanded instances.  Thus your code will be littered with
declarations of the type 'c: expanded COMPLEX_NUM'.  Very, very
unnerving for such a simple idea.

Though ISE promotes the language as 'the' general programming solution,
judging from the poor organization of their almost non-existent math
libraries, they have done little if any programming in Eiffel in this
area.  (To my mind, there is no reason that math/science programs need
object-orientation any less than any other domain.  Possibly they need
them more judging from the poor Fortran code that floats throughout the
universe.)

* Add an INTEGER to a REAL.  The compiler will tell you something akin
to INTEGER is not a descendent of REAL.  Conceptually, it should be a
specialization of and thus should inherit but the more significant
matter is that this is a simple thing that programmers often do everyday,
yet it does not work as expected.

* Create an SDF (system build configuration file) in directory A and a
class in directory B.  This is often useful as one tries to categorize
classes in the same system below different directories.  Now try to
compile the class from directory B which is where you are likely to be
if you have just edited the class and want to see if it will compile.
You'll find that the compiler can't locate the SDF and so remains unhappy.
'ec' the Eiffel class compiler has no published option with which to
specify the SDF directory as does 'es', the Eiffel system builder.  Is
this the result of two different implementers not speaking to one
another in the same small company?

* Use a smart editor to parse ISE's Eiffel compiler error
messages in order to displaying the offending class line.  It can be
done but is made very difficult by the fact that error messages do not
contain full pathnames but only class names.

* Regression test an Eiffel system using any ISE supplied testing
facilities.  ISE has indicated that their current tools, after a couple
of years of development, do not support this.  How can one talk about
adequate solutions to programming-in-the-large without designing in
regression test support?

* Create a large class library without creating any names that conflict
with ISE library class names.  My belief is that a class definition
which represents a data type to be used in programs should be named
clearly enough to indicate its true nature.  Classes that implement
obviously incomplete abstractions should not take the name of the
abstraction.  Part of the point behind OO is to represent abstractions
in a way that will appear natural to people.  Now look at these ISE
class names and guess what they do: BASIC, CELL, CHAIN, COMMAND,
SCREENMAN, INTERNAL.

My best guess would be:  A grammar class for the BASIC programming
language, a biological or criminal containment unit, a linked set of
units that holds something else down, a guy who fixes screen doors, and
of course an ISE internal class for functions that just would not fit
anywhere else.  All of these guesses are naturally entirely wrong.  Not
much energy is required to come up with better names either.  Naming
conflicts are avoidable if decent naming conventions are used.

* Cast the value of a STRING into an entity name and then assign to it.
This is fairly common in Lisp.  This may break the type system but is
extraodinarily useful in everyday practice.  (How can one justify
elegance in the face of impracticality anyway?)  Suppose one needs a
hundred separate input parameters to initialize a program.  Command line
options are thus impractical.  Instead a file with two fields per line
is created with one parameter per line.  The first is the parameter name
which is the same as a program attribute name and the second is the
parameter value.  The problem is how to assign the value to the actual
program entity with the right name?  Without the aforementioned
capability, one is in for a very long if-then-else structure which is
both inefficient and contrary to the open-closed principles often
eloquently espoused by Dr. Meyer.  A non-mundane use of this facility
would be to create a self-modifying program; a concept that is going to
play an important role in software throughout the next decade and
beyond.

I hope this sparks some interesting messages and also some solutions.
There is way too little traffic in this group so let's here from some
readers.

-- 
Bob Weiner, Motorola, Inc.,   USENET:  ...!gatech!uflorida!novavax!weiner
(407) 364-2087

wright@hsi.UUCP (Gary Wright) (01/09/90)

In article <1721@novavax.UUCP> weiner@novavax.UUCP (Bob Weiner) writes:
>(I assume you're using Eiffel 2.2.)
>

>* Build a COMPLEX_NUM class.  This is one of the most basic classes needed
>for any sort of scientific programming yet missing from the ISE provided
>Eiffel libraries.  Naturally you'll want it to be an expanded type just
>like an INTEGER.  But you'll find there is no way to define an expanded
>class but only expanded instances.  Thus your code will be littered with
>declarations of the type 'c: expanded COMPLEX_NUM'.  Very, very
>unnerving for such a simple idea.

If there were such a thing as an expanded class, how would I create an
instance that was not expanded?  I would need a way to create two
seperate classes, one expanded, one not.  Of course, now I would have
COMPLEX_NUM and EXP_COMPLEX_NUM (or COMPLEX_NUM_REF and COMPLEX_NUM).
A solution, but not any better than the current method in my opinion.
Also, you can't have an inheritance graph below an expanded class.  So
the notion of a class always having expanded instances seems to be of limited
use.  Perhaps a more terse notation for an expanded instance
would help?  Unfortunately, the only thing that comes to mind right now
is notation from C: "c: *COMPLEX_NUM".  Even better would be naming the
class COMPLEX which would allow: "c : *COMPLEX".  Any other ideas out
there?

>* Add an INTEGER to a REAL.  The compiler will tell you something akin
>to INTEGER is not a descendent of REAL.  Conceptually, it should be a
>specialization of and thus should inherit but the more significant
>matter is that this is a simple thing that programmers often do everyday,
>yet it does not work as expected.

I don't think programmers do this everyday.  What they do is use languages
that perform default conversions thus converting the INTEGER to a REAL
before performing the addition.  Are default numeric conversions a good idea?
Do they mix well with other Eiffel concepts?".

From looking at C++, I don't know if I want a language that permits simple 
syntactic structures to result in complex semantics.  Better to have
the conversion written explicitly.  Of course the compiler needs to be smart
about this and avoid the function call overhead.

>
>* Create an SDF (system build configuration file) in directory A and a
>class in directory B.  This is often useful as one tries to categorize
>classes in the same system below different directories.  Now try to
>compile the class from directory B which is where you are likely to be
>if you have just edited the class and want to see if it will compile.
>You'll find that the compiler can't locate the SDF and so remains unhappy.
>'ec' the Eiffel class compiler has no published option with which to
>specify the SDF directory as does 'es', the Eiffel system builder.  Is
>this the result of two different implementers not speaking to one
>another in the same small company?

I agree, some work needs to be done in this area.

>
>* Use a smart editor to parse ISE's Eiffel compiler error
>messages in order to displaying the offending class line.  It can be
>done but is made very difficult by the fact that error messages do not
>contain full pathnames but only class names.

Full pathnames are very annoying if you aren't using a smart editor.
One solution is to have the editor know about Eiffel idea of the
current universe.

>* Create a large class library without creating any names that conflict
>with ISE library class names.  My belief is that a class definition
>which represents a data type to be used in programs should be named
>clearly enough to indicate its true nature.  Classes that implement
>obviously incomplete abstractions should not take the name of the
>abstraction.  Part of the point behind OO is to represent abstractions
>in a way that will appear natural to people.  Now look at these ISE
>class names and guess what they do: BASIC, CELL, CHAIN, COMMAND,
>SCREENMAN, INTERNAL.

Naming in a single namespace is always difficult.  The mapping between
the class name and the file name unfortunately limits the length of class 
identifiers but I really don't expect a single identifier to be
able to unambiguously describe a complete abstraction.  It doesn't
work in any programming language I know about and there have been endless
discussions on the net regarding the use of extremely long identifiers.
Of course you are free to critizise particular name choices but there
will never be a consesus on that!  Perhaps a tool that renames a class
would help.  The problem you have pointed out is much larger than Eiffel.

>* Cast the value of a STRING into an entity name and then assign to it.
>This is fairly common in Lisp.  This may break the type system but is
>extraodinarily useful in everyday practice.  (How can one justify
>elegance in the face of impracticality anyway?)  

Please define type system, useful, elegance and impracticality and then
maybe we can draw some conclusions.  1/2 :-)

>Without the aforementioned
>capability, one is in for a very long if-then-else structure which is
>both inefficient and contrary to the open-closed principles often
>eloquently espoused by Dr. Meyer.  A non-mundane use of this facility
>would be to create a self-modifying program; a concept that is going to
>play an important role in software throughout the next decade and
>beyond.

Again, you have pointed out a problem that is not unique to Eiffel.
Maybe this discussion should be in comp.lang.misc?  A self-modifying
program is not exactly an idea on which a concensus has been reached.
Can you propose a syntax/method to incorporate this capability in
Eiffel?  You say that an if-then-else structure is inefficient, how
does a Lisp run-time system take care of this mapping?  Why can't that
technique be used in Eiffel?  What is the use for this technique in a
commercial application (vs. research, lab application)?  Of course,
simply because a programming technique is "useful", does not mean that
it is wise to include it in every language that comes along.
-- 
Gary Wright                                     ...!uunet!hsi!wright
Health Systems International                    wright@hsi.com

sakkinen@tukki.jyu.fi (Markku Sakkinen) (01/09/90)

In article <1721@novavax.UUCP> weiner@novavax.UUCP (Bob Weiner) writes:
>(I assume you're using Eiffel 2.2.)
>
>I should warn readers that ISE's Eiffel has much to recommend it and so
>far I'm still apt to promote its use.  But in fairness I present here a
>number of example tasks that should be simple but aren't.  Thus, they
>either reflect my lack of Eiffel expertness or they reflect weaknesses
>in the environment.

> [several other examples ...]

>* Cast the value of a STRING into an entity name and then assign to it.
>This is fairly common in Lisp.  This may break the type system but is
>extraodinarily useful in everyday practice.  (How can one justify
>elegance in the face of impracticality anyway?)  Suppose one needs a
>hundred separate input parameters to initialize a program.  Command line
>options are thus impractical.  Instead a file with two fields per line
>is created with one parameter per line.  The first is the parameter name
>which is the same as a program attribute name and the second is the
>parameter value.  The problem is how to assign the value to the actual
>program entity with the right name?  Without the aforementioned
>capability, one is in for a very long if-then-else structure which is
>both inefficient and contrary to the open-closed principles often
>eloquently espoused by Dr. Meyer.  A non-mundane use of this facility
>would be to create a self-modifying program; a concept that is going to
>play an important role in software throughout the next decade and
>beyond.
> [...]

Fortran's NAMELIST input is probably among your favourites ...
There are times when one would like to have this kind
of facilities; I think there have been such questions recently
at least in the Pascal newsgroup. However, it would not be _simple_
to offer such facilities in any compiled language - variable names
in them are generally only compile-time entities; preserving them
at run time would greatly impede efficiency.

More importantly, exposing attribute names to users at run time
would be in glaring contradiction with some of Eiffel's most important
principles: abstraction and encapsulation. Read more carefully about
the open-closed principle and note that the 'closed' part is just
as important as the 'open' part. (I confess that it isn't too easy
to understand what exactly Meyer wants the "open-closed principle"
to imply.)

Concerning your specific example:
If a programme needs a large number of initialisation parameters,
a quick-and-dirty solution to input them will probably not be good
anyway, especially considering later modifications.
Very likely one would need to make several feasibility checks before
accepting the set of initial parameters in the first place.
An object-oriented way to solve the problem could be to design some
classes and operations for handling a more general case,
then specialise to the actual situation.

I would bet that self-modifying programmes draw more interest in the 50's
than they will in the 90's (I think object-orientation offers in a
disciplined way much of the flexibility for which self-modification
would be needed otherwise). At least, they belong more to the domain of
"exploratory programming" than that of "software engineering".
The target of Eiffel is the latter.

Markku Sakkinen
Department of Computer Science
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland

lgm@cbnewsc.ATT.COM (lawrence.g.mayka) (01/10/90)

In article <2645@tukki.jyu.fi> sakkinen@jytko.jyu.fi (Markku Sakkinen) SAKKINEN@FINJYU.bitnet (alternative) writes:
>at least in the Pascal newsgroup. However, it would not be _simple_
>to offer such facilities in any compiled language - variable names
>in them are generally only compile-time entities; preserving them
>at run time would greatly impede efficiency.

Common Lisp is a compiled language that offers such capabilities
for dynamically scoped variables (via SYMBOL-VALUE) and for
instance variables (via SLOT-VALUE).  Retaining the symbol
information costs some space but not, in itself, speed.  I grant,
though, that producing a complete, high-quality Common Lisp
implementation is not "simple."


	Lawrence G. Mayka
	AT&T Bell Laboratories
	lgm@ihlpf.att.com

Standard disclaimer.

sommar@enea.se (Erland Sommarskog) (01/13/90)

Bob Weiner (weiner@novavax.UUCP) writes:
>* Cast the value of a STRING into an entity name and then assign to it.
>...
>Suppose one needs a
>hundred separate input parameters to initialize a program.  Command line
>options are thus impractical.  Instead a file with two fields per line
>is created with one parameter per line.  The first is the parameter name
>which is the same as a program attribute name and the second is the
>parameter value.  The problem is how to assign the value to the actual
>program entity with the right name?  Without the aforementioned
>capability, one is in for a very long if-then-else structure ...

Let's see where is my "A definition of a real programmer"? Ah, here
it is! "A real programmer can write Fortran in any language". Or Lisp,
or Eiffel or whatever his favourite is. However what is a feasible
solution in one language may be completely condemnable in another.
Eiffel is statically typed. What do you do when the name in the
string is not of the right type? Or is completely undefined? And
how do you handle it to give the user an nice error message? And the
most funny cases of them all: suppose the user enters the name of
internal variable which is not supposed to be altered?

A back-on-the-envelope solution to the problem would to be to have
class PARAMETER which inherits some sorting mechanism, probably a
hash table. Probably you have subclasses STRING_PARAMETER,
BOOLEAN_PARAMETER etc. You read the parameter string uses that as
key to get the instance, calls its Assign_value passing the value
string from the file. Assign_value parses the string and assigns
it to the Value feature or gives an error message.

Next is of course the problem retrieving values from this parameter
table which I'm note fluently enough in Eiffel to solve cleanly.
-- 
Erland Sommarskog - ENEA Data, Stockholm - sommar@enea.se
Unix is a virus.

marc@eiffel.UUCP (Jean-Marc Nerson) (01/18/90)

A few posting recently (by Bob Weiner, Erland Sommarskog and Markku
Sakkinen) have raised the question of how to have input
(in the style of NAMELIST in Fortran) consisting of a possibly large
set of values, each characterized by a tag.

Input values may be of various types. The tag uniquely determines the type.

Sample input might look like the following, where each line contains
a <tag, value> pair:

    pressure 32.76
    valve_count 78
    temperature 12.5
    active_valve "SOUTHEAST 2"

where the tag type information (entered separately) might be

    active_valve STRING
    pressure REAL
    temperature REAL
    valve_count INTEGER

Two solutions are suggested below.

Solution 1 is the more complete of the two and makes full use of
the object-oriented method: polymorphism, dynamic binding. 

Solution 2 is less advanced but will also work. It makes use of
the persistence facilities of Eiffel. Its main drawback is that
when you change input values you must change a class text and
recompile a system. Although this system is very small
and compilation will be quick, such an approach is subject
to criticism.

--------------------------------------------------------------
SOLUTION 1:
=-=-=-=-=-=
    This solution is the complete implementation of what Erland Sommarskog
    sketched out in <631@enea.se>:
(...)
>>  A back-on-the-envelope solution to the problem would to be to have
>>  class PARAMETER which inherits some sorting mechanism, probably a
>>  hash table. Probably you have subclasses STRING_PARAMETER,
>>  BOOLEAN_PARAMETER etc. You read the parameter string uses that as
>>  key to get the instance, calls its Assign_value passing the value
>>  string from the file. Assign_value parses the string and assigns
>>  it to the Value feature or gives an error message. (...)

    The solution uses polymorphism, the universal class ANY, and the
Reverse Assignment Attempt (operator "?="). After such an instruction,
there is no need here to test whether the RAA was successful
since the programmer knows exactly what was entered in the hash table.

    To use dynamic binding, the solution relies on a hash table
which associates with each tag an object of the appropriate type.
These objects are shared (altogether, the table includes only one object
of each type).

    If no line beginning with a given tag appears in the input file,
the corresponding value will be set to the type's default.

   The solution achieves one of the major benefits of object-oriented
design: limiting the number of explicit choices between a range of
possibilities. There are explicit choices below, but this is inevitable
since the basic classes must know the list of all possible types
of input (string, real etc.); however there will be NO explicit
choice in the application software. You do not need to change or
recompile any of the basic classes when adding a new tag. Just
record it with its type

    In practice the classes should be adapted to handle handling of
and recovery from erroneous input.

--------------------------------
deferred class INPUT export
    value, read
feature

    value: ANY is
        deferred
        end; -- value

    read (f: FILE) is
        require
            not f.Void
        deferred
        end; -- read

end -- clas INPUT

--------------------------------

class REAL_INPUT export
    repeat INPUT
inherit
    INPUT
feature

    value: REAL;

    read (f: FILE) is
        require
            not f.Void
        do
            f.readreal;
            value := f.lastreal
        end; -- read

end -- class REAL_INPUT

--------------------------------

class STRING_INPUT export
    repeat INPUT
inherit
    INPUT
feature

    value: STRING;

    read (f: FILE) is
        require
            not f.Void
        do
            f.readword;
            value := f.laststring.duplicate
        end; -- read

end -- class STRING_INPUT

--------------------------------

class INT_INPUT export
    repeat INPUT
inherit
    INPUT
feature

    value: INTEGER;

    read (f: FILE) is
        require
            not f.Void
        do
            f.readint;
            value := f.lastint
        end; -- read

end -- class INT_INPUT

--------------------------------

class VALUE
feature

   valve_count: INTEGER;
   pressure, temperature: REAL;
   active_valve: STRING;

   inputs: HTABLE [INPUT, STRING];
      -- Internal Hash table

   init_inputs is
         -- Enter into `inputs' one instance of each
         -- possible type of VALUE.
      local
         pr: REAL_INPUT;
         pi: INT_INPUT;
         ps: STRING_INPUT;
      do
         inputs.Create (100); -- A value big enough to cover
                              -- all possible types.
         pi.Create; inputs.put (pi, "valve_count");
         pr.Create; inputs.put (pr, "pressure");
         pr.Create; inputs.put (pr, "temperature");
         ps.Create; inputs.put (ps, "active_valve");
      end; -- init_inputs

   read_inputs is
         -- Read inputs from input file `.input' in
         -- current directory (no check).
      local
         file: FILE
      do
         from
            file.Create (".input");
            file.open_read; file.readword
         until
            file.end_of_file
         loop
            inputs.item (file.laststring).read (file);
            file.readword
         end;
         file.close
      rescue
         io.putstring ("Input file `.input' corrupted\n")
      end; -- read_inputs

   assign_inputs is
         -- Initialize `inputs' from values read in
         -- `.input' file and stored in hash table `inputs'.
      local
         pr: REAL_INPUT; pi: INT_INPUT; ps: STRING_INPUT
      do
         pi ?= inputs.item ("valve_count");
         valve_count := pi.value;
         pr ?= inputs.item ("pressure");
         pressure := pr.value;
         pr ?= inputs.item ("temperature");
         temperature := pr.value;
         ps ?= inputs.item ("active_valve");
         active_valve := ps.value
      end; -- assign_inputs

end -- class VALUE
--------------------------------

-- Here is the way a class of the application system
-- (for example the root class) will take care
-- of reading the input values.

class APPLICATION
inherit
    VALUE
feature

    Create is
        do
            init_inputs; read_inputs; assign_inputs;
            debug
                io.putstring (out)
            end;
            -- Rest of the application
            -- ...
        end; -- Create

    -- ...
end -- class APPLICATION

-------- .input file ----
pressure     22.2
temperature  12.5
active_valve SOUTHEAST2
valve_count      78
--------------------------

SOLUTION 2
-=-=-=-=-=

   With this solution, you must adapt, compile and execute a
very small system containing a class INPUT. The result of the 
execution is to store information into a file `.init' 
(using the automatic persistence facilities).

   The application system then reads the required values from
file `.init'. 

--------------------------------

class INPUT export
   repeat STORABLE,
   pressure, valve_count, temperature, active_valve
inherit
   STORABLE
feature
   
   pressure, temperature: REAL;
   valve_count: INTEGER;
   active_valve: STRING

end -- class INPUT

--------------------------------

class INIT export
   repeat INPUT 
inherit
   INPUT
feature

   Create is
      do
         pressure := 32.76;
         valve_count := 78;
         temperature := 12.5;
         active_valve := "SOUTHEAST2"
         -- ... Other possible value initializations here ...
      end; -- Create

end -- class INIT

--------------------------------

    -- Root class of initialization system:
    --
class MAKE_INIT feature
    
    init: INIT;
    inputs: INPUT;

    Create is
        do
            init.Create;
                -- Creation of param instance necessary
                -- because we do not want to store an instance of INIT
            inputs.Create;
                -- Field by field copy.
            inputs.copy (init);
            inputs.store_by_name (".init")
        end; -- Create

end -- class MAKE_INIT


--------------------------------

-- Here is the way a class of the application system
-- (for example the root class) will take care
-- of reading the input values.

class APPLICATION
feature
    
    inputs: INPUT;
        -- Input values of the application

    Create is
        do
                -- Very beginning of the application
            inputs.Create;
                -- Get a pre-initialized object values from file '.init'
            inputs.retrieve_by_name (".init");
            inputs := inputs.retrieved;
            -- Rest of the application, using
            -- inputs.pressure, inputs.valve_count, ...
            debug
               io.putstring (inputs.out)
            end;
        end; -- Create

    -- ...

end -- class APPLICATION

[All classes above have been compiled and tested with Eiffel 2.2.]

-- Jean-Marc Nerson
marc@eiffel.com

yost@esquire.UUCP (David A. Yost) (01/19/90)

In article <228@eiffel.UUCP> marc@eiffel.UUCP (Jean-Marc Nerson) writes:
>
>A few posting recently (by Bob Weiner, Erland Sommarskog and Markku
>Sakkinen) have raised the question of how to have input
>(in the style of NAMELIST in Fortran) consisting of a possibly large
>set of values, each characterized by a tag.
>
>Input values may be of various types. The tag uniquely determines the type.
>
>Sample input might look like the following, where each line contains
>a <tag, value> pair:
>
>    pressure 32.76
>    valve_count 78
>    temperature 12.5
>    active_valve "SOUTHEAST 2"
>
>where the tag type information (entered separately) might be
>
>    active_valve STRING
>    pressure REAL
>    temperature REAL
>    valve_count INTEGER
>
>Solution 1 is the more complete of [two given] and makes full use of
>the object-oriented method: polymorphism, dynamic binding. 
>
>--------------------------------------------------------------
>SOLUTION 1:
>=-=-=-=-=-=
>    This solution is the complete implementation of what Erland Sommarskog
>    sketched out in <631@enea.se>:
>(...)
>>>  A back-on-the-envelope solution to the problem would to be to have
>>>  class PARAMETER which inherits some sorting mechanism, probably a
>>>  hash table. Probably you have subclasses STRING_PARAMETER,
>>>  BOOLEAN_PARAMETER etc. You read the parameter string uses that as
>>>  key to get the instance, calls its Assign_value passing the value
>>>  string from the file. Assign_value parses the string and assigns
>>>  it to the Value feature or gives an error message. (...)
>
>    The solution uses polymorphism,

>    To use dynamic binding, the solution relies on a hash table
>which associates with each tag an object of the appropriate type.
>These objects are shared (altogether, the table includes only one object
>of each type).
>
>    If no line beginning with a given tag appears in the input file,
>the corresponding value will be set to the type's default.

I didn't see where you did this in your example.

>   The solution achieves one of the major benefits of object-oriented
>design: limiting the number of explicit choices between a range of
>possibilities.

Please explain.

 - - - -

I liked a lot about your example, and I applaud the posting
of actual practical code examples here.

I noted in your code a couple of instances where
basically the same code was repeated with minor changes
(a red flag to me, which suggests a need for
parameterization of some kind.)
1.  The various forms of the INPUT classes
2.  Having called the routine that read the input, you
    then had to call another routine to extract each
    value by hand from the hash table and copy it to the
    appropriate variable.

I submit the following revised version, checked by the
recently-posted eiffel_scanner, but not tried (I
unfortunately don't currently have access to Eiffel
compiler).  My changes are marked by | in the first
column.  More remarks follow after the example.

|  --------------------------------
|
|  -- CHAR_SOURCE is a generic source from which characters can be read,
|  -- whether from a file, a string, or whatever.
|
|  class CHAR_SOURCE export
|     getstring, getchar, putback
|  feature
|
|     getstring (count: INTEGER): STRING is
|           -- get a string of characters
|        deferred
|        end; -- value
|
|     getchar : STRING is
|           -- get a character
|        deferred
|        end; -- value
|
|     putback (count: INTEGER): STRING is
|           -- put back a string of characters
|           -- onto the beginning of the stream
|        deferred
|        end; -- value
|
|  end -- class CHAR_SOURCE
|
|  --------------------------------
|
|  -- a class inheriting from this one is able to
|  -- consume characters from a CHAR_SOURCE and parse
|  -- them into a valid value for the object.
|
|  class HAS_IN_FEATURE export
|     in
|  feature
|
|     in (f: CHAR_SOURCE) is
|           -- get a string of characters from f
|           -- which represent a valid value for the object
|           -- and set the features of the object accordingly
|        deferred
|        end; -- value
|
|  end -- class HAS_IN_FEATURE
|
|  --------------------------------
|
|  deferred class INPUT [ T -> HAS_IN_FEATURE ] export
|      value, itsname, read
|  feature
|
|     value: T;
|     itsname: STRING;
|
|     Create (str: STRING, x: T, tbl: HTABLE [INPUT, STRING]) is
|        do
|           value := x;
|           itsname := str;
|           tbl.put (Current, str);
|        end;
|
|     read (f: CHAR_SOURCE) is
|        require
|           not f.Void
|        do
|           value := T.in (f);
|        end; -- read
|
|  end -- class INPUT
|
   --------------------------------

   class VALUE
   feature

|     in_valve_count : INPUT [ INT ];
|     in_pressure    : INPUT [ REAL ];
|     in_temperature : INPUT [ REAL ];
|     in_active_valve: INPUT [ STRING ];

|     valve_count : INT    is do Result := in_valve_count .value end;
|     pressure    : REAL   is do Result := in_pressure    .value end;
|     temperature : REAL   is do Result := in_temperature .value end;
|     active_valve: STRING is do Result := in_active_valve.value end;

      inputs: HTABLE [INPUT, STRING];
	 -- Internal Hash table

      init_inputs is
	    -- Enter into `inputs' one instance of each
	    -- possible type of VALUE.
	 local
	 do
	    inputs.Create (100); -- A value big enough to cover
				 -- all possible types.
|           in_valve_count .Create ("valve_count" , 0       , inputs);
|           in_pressure    .Create ("pressure"    , -1.0    , inputs);
|           in_temperature .Create ("temperature" , -1.0    , inputs);
|           in_active_valve.Create ("active_valve", "[none]", inputs);

	 end; -- init_inputs

      read_inputs is
	    -- Read inputs from input file `input' in
	    -- current directory (no check).
	 local
	    file: FILE
	 do
	    from
	       file.Create ("input");
	       file.open_read; file.readword
	    until
	       file.end_of_file
	    loop
	       inputs.item (file.laststring).read (file);
	       file.readword
	    end;
	    file.close
	 rescue
	    io.putstring ("Input file `input' corrupted\n")
	 end; -- read_inputs

   end -- class VALUE
   --------------------------------

   -- Here is the way a class of the application system
   -- (for example the root class) will take care
   -- of reading the input values.

   class APPLICATION
   inherit
       VALUE
   feature

       Create is
	   do
|              init_inputs; read_inputs;
	       debug
		   io.putstring (out)
	       end;
	       -- Rest of the application
	       -- ...
	   end; -- Create

       -- ...
   end -- class APPLICATION

 - - - - -

Notes:

1.  The C library has fprintf and sprintf which do
    the same output formatting to either a file or a
    string, a rudimentary kind of polymorphism.  My
    CHAR_SOURCE class proposed here is the input
    equivalent of that, rendered in an OO context.
    That is, there is a class of object that can supply
    characters.  This could be a file or a string or
    some other data structure.  There should also be
    a CHAR_DESTINATION (or some other name) class to
    serve the same purpose on output.  These would
    greatly facilitate writing reusable code that does
    I/O.  This approach would lead to some rethinking
    of the I/O in the Eiffel library.

2.  I don't know offhand how you would implement the
    'in' feature of class HAS_IN_FEATURE class in my
    example.

3.  There are still two places in my version where
    basically the same code with a name change is
    repeated four times.  Oh, well.

 --dave

yost@esquire.UUCP (David A. Yost) (01/19/90)

In article <1731@esquire.UUCP> yost@esquire.UUCP (David A. Yost) (I) wrote:
>
>  -- a class inheriting from this one is able to
>  -- consume characters from a CHAR_SOURCE and parse
>  -- them into a valid value for the object.
>
>  class HAS_IN_FEATURE export
>     in
>  feature
>
>     in (f: CHAR_SOURCE) is
>           -- get a string of characters from f
>           -- which represent a valid value for the object
>           -- and set the features of the object accordingly
>        deferred
>        end; -- value
>
>  end -- class HAS_IN_FEATURE

>  deferred class INPUT [ T -> HAS_IN_FEATURE ] export
>      value, itsname, read
>  feature
>
>     value: T;
>     itsname: STRING;
>
>     Create (str: STRING, x: T, tbl: HTABLE [INPUT, STRING]) is
>        do
>           value := x;
>           itsname := str;
>           tbl.put (Current, str);
>        end;
>
>     read (f: CHAR_SOURCE) is
>        require
>           not f.Void
>        do
>           value := T.in (f);
>        end; -- read
>
>  end -- class INPUT

>Notes:
>
>1.  The C library has fprintf and sprintf which do
>    the same output formatting to either a file or a
>    string, a rudimentary kind of polymorphism.  My
>    CHAR_SOURCE class proposed here is the input
>    equivalent of that, rendered in an OO context.
>    That is, there is a class of object that can supply
>    characters.  This could be a file or a string or
>    some other data structure.  There should also be
>    a CHAR_DESTINATION (or some other name) class to
>    serve the same purpose on output.  These would
>    greatly facilitate writing reusable code that does
>    I/O.  This approach would lead to some rethinking
>    of the I/O in the Eiffel library.
>
>2.  I don't know offhand how you would implement the
>    'in' feature of class HAS_IN_FEATURE class in my
>    example.

I forgot to mention some other points.

1.  This notion of an "in" function is similar in
    purpose to the recently-added Eiffel "out" feature.

2.  The heirarchy of built-in and library classes
    would have to be changed so that most classes
    inherit it and implement it.  For instance,
    my example assumes that it is implemented in
    INT, REAL, and STRING.

3.  My class INPUT should not have been deferred.

 --dave yost
   yost@esquire.dpw.COM

bertrand@eiffel.UUCP (Bertrand Meyer) (01/20/90)

In <228@eiffel.UUCP> marc@eiffel.UUCP (Jean-Marc Nerson) wrote:
> > The solution achieves one of the major benefits of object-oriented
> >design: limiting the number of explicit choices between a range of
> >possibilities.

In <1731@esquire.UUCP>, yost@esquire.UUCP (David A. Yost) asked:
> Please explain.

The underlying idea is, I believe, one of the major principles of
Eiffel design. The principle may roughly be expressed as follows:

    Whenever a certain software system deals with a set of
    possible cases for a certain property, there should be
    exactly one part of the system which knows about the list
    of choices.

Examples of ``sets of possible cases'' are:

    - The types of external devices in a real-time or process control system.
    - The commands of an editor.
    - The types of input events in a window system.
    - The non-terminals of the grammar in a compiler

etc.

A major plague of traditional programming is the need to write
numerous discrimination instructions (``case... of...'' or
``if.. then... else'') which will select between the available cases.

We cannot hope to remove all knowledge about the set of possible cases
from the system. Clearly, if an editor is to support a certain command
set, *some* part of the editor has to know what commands are available.
(That part is ``closed'' in the sense in which this word is used in 
``Object-Oriented Software Construction'' section 2.3.) Hence
there is AT LEAST ONE component that ``knows''.

In good object-oriented design, there will be AT MOST ONE
such component. Often, this will be an initialization module,
easy to change and not a very ``deep'' component of the architecture.
(A standard technique is to use a table of objects to record
the knowledge about the set of possible choices; each object in the table
is of a different type, a descendant of the same ancestor class.)
Then any other part of the system performing an action
that may have different variants depending
on each case relies on the techniques of genericity, polymorphism and
dynamic binding (with the help of static typing to avoid mistakes)
to achieve implicit rather than explicit discrimination, as in

    command_set.entry(command_code).execute

The major achievement is that full knowledge of the list of cases is
limited to just one module, making evolution and reuse much easier
than if that information was scattered throughout the code.

Many of the examples I know where an Eiffel
solution achieves a real breakthrough - a quantum leap - as
compared to traditional techniques use (among other techniques!)
some variant of this scheme.
   
-- Bertrand Meyer
bertrand@eiffel.com