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