rick@tetrauk.UUCP (Rick Jones) (11/30/90)
This newsgroup seems to have been very quiet recently - is Frank Caggiano the only person (apart from me) writing Eiffel code? Just to liven things up, here is an idea for discussion. (For the record, I thought Frank's stuff on X resource management looked good and is a much needed facility. I've saved it for later since I'm not doing any X windows work right now.) (BTW, did my posting on an enhanced exception class finally make to the net? Our feeder site had some serious disk problems recently and a lot of stuff got lost. I would have expected some comment on the article & I haven't received any - could anyone who's seen the article let me know. If I get no replies I will try and post it yet again.) This article concerns the use (or lack of) the Void keyword in Eiffel. I believe it would be useful if the language supported Void as a "stand-alone" keyword, representing of course a void reference. While the language currently has .Void and .Forget as pseudo features representing test and assignment against a void value, it doesn't have a construct to pass a literal void reference as an argument to a routine. I now have a number of classes with features which will accept a void argument and still behave sensibly; the pupose of the parameter is to supply optional information. A particular example is an exception object which can be initialised with arbitrary comment text at the point of creation: local excep: MSG_EXCEP ; code: INTEGER ; do ... if <some condition> then excep.Create (code, "Report this error") ; ... When used in a context in which no information is relevant, it is necessary to declare a local string variable to provide a syntactically valid void reference: local excep: MSG_EXCEP ; code: INTEGER ; null_str: STRING ; do ... if <some condition> then excep.Create (code, null_str) ; ... (I specifically want a void reference, not a blank string) It would be far cleaner to be able to write: excep.Create (code, Void) ; A Void of course conforms to any class type, so it is syntactically valid anywhere an object reference is required. This raises the interesting issue of forms such as: obj_ref := Void equivalent to obj_ref.Forget if obj_ref = Void then equivalent to if obj_ref.Void then These would clearly be valid, and as alternate forms are they preferable to the current style of pseudo features? My personal opinion is that they are, since I find the pseudo feature concept in Eiffel one of its less elegant characteristics. Given the opportunity, I would in fact replace the following as well: a.Create (params) a := Create A_CLASS (params) a.Clone (b) a := b.Clone In the latter case, the feature deep_clone which is in ANY works the same way as the right-hand version, making the different clone operations incompatible in the current version. In fact if deep_clone were called "duplicate", it would I believe support the "duplicate" feature of the STRING class without any redefinition. Does anyone else have a view on this? Does anyone care? Is there anyone out there? Is the Eiffel tower still standing? -- Rick Jones Tetra Ltd. Maidenhead, Was it something important? Maybe not Berks, UK What was it you wanted? Tell me again I forgot rick@tetrauk.uucp -- Bob Dylan
tom@eds.com (Tom H. Meyer) (12/04/90)
In article <1037@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes: >It would be far cleaner to be able to write: > > excep.Create (code, Void) ; > >A Void of course conforms to any class type, so it is syntactically valid >anywhere an object reference is required. > >This raises the interesting issue of forms such as: > > obj_ref := Void equivalent to obj_ref.Forget > > if obj_ref = Void then equivalent to if obj_ref.Void then I have often run into this problem and wished for exactly this solution. Oh, well. So much for sparking a controversy :-) >... Given the opportunity, I would in fact replace the following >as well: > > a.Create (params) a := Create A_CLASS (params) I dislike this on the grounds that it would *require* that 'Create' be a keyword. I hope to see the day when classes can have more than one creation routine and this seems to disallow it. Since you asked for some net traffic and we're talking about programming, I would like feed back on how some others out there would do the following: Suppose I have some class which which has a list of generic objects and my application needs to examine every item on the list and perform different actions based on the actual type of the items on the list. My solution is a sequence of reverse assignment attempts to, first, determine the actual type of the object, and, second, have a local object of the correct type to access the specific object's features. I've heard that this is poor style but I've never been able to see a better way to do it. Supposedly, there is some way of designing the classes so that this isn't necessary. Can anyone give me an example? tom meyer, EDS Research | If I don't see you in the future ...uunet!tantalum!tom | I'll see you in the pasture 505.345.2563
rsw@cs.brown.EDU (Bob Weiner) (12/04/90)
In article <4106@tantalum.UUCP> tom@eds.com (Tom H. Meyer) writes: > > Since you asked for some net traffic and we're talking about programming, > I would like feed back on how some others out there would do the following: > > Suppose I have some class which which has a list of generic objects and > my application needs to examine every item on the list and perform > different actions based on the actual type of the items on the list. > > My solution is a sequence of reverse assignment attempts to, > first, determine the actual type of the object, and, second, have a > local object of the correct type to access the specific object's features. > > I've heard that this is poor style but I've never been able to see a > better way to do it. Supposedly, there is some way of designing the classes > so that this isn't necessary. Can anyone give me an example? > The key question is whether you know at class design time whether or not objects of the class type will be used on the list. Even if you don't, you could still define a somewhat generalized protocol (varying numbers of parameters are still a problem) in which all of your non-generic classes have an arbitrary 'action' procedure. Then when you pop an object off the list into an entity of type ANY, simply invoke 'entity.action(params)'. Eiffel's dynamic binder will do the rest. My guess is that your problem is more complicated than this, so you will have to elaborate some more. Your problem does demonstrate a weakness of current object-oriented languages that I have recently been investigating. I call it the 'context conditional' problem. Dynamic binding solved the 'typecase conditional' problem, allowing one to work with an open ended set of types all of whose features are properly invoked at runtime, without the need to modify completed source code in order to add a type. But today's widely-used OO languages have no mechanism for dynamically binding features based upon contexts, where a context is defined as some set of constraints on the program state at runtime. Thus, one still needs code of the form: if var1 = 10 and flag1 = true then obj.action1 elseif var1 > 10 and flag2 = false then obj.action2 ... endif This yields a similar problem as one had with embedded typecase statements. That is, every time a new constraint type is added one must modify code possibly spread throughout a number of classes. The solution I am working on involves multiple class protocols called views coupled with contextually linked constraints. Details are too lengthy and not yet complete enough to go into here, but I wanted to mention these ideas as possible long term solutions to the sort of problem you are now faced with. -- Bob Weiner rsw@cs.brown.edu
pw@tnoibbc.UUCP (Peter Willems) (12/05/90)
>In article <1037@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes: >>A Void of course conforms to any class type, so it is syntactically valid >>anywhere an object reference is required. >> >>This raises the interesting issue of forms such as: >> obj_ref := Void equivalent to obj_ref.Forget >> if obj_ref = Void then equivalent to if obj_ref.Void then I seem to remember that Bertrand Meyer introduced for this same issue the idea of a class NONE. Conceptually class NONE inherits from all other classes and therefore conforms to any class type. Of course there is no real difference with the Void concept, it is slightly more elegant I think. Probably this enhancement will be part of Eiffel 3.0. -- Peter Willems : TNO - IBBC INTERNET : pw@tnoibbc : PO-box 49 DOMAIN : pw@ibbc.tno.nl : 2600 AA Delft FAX : +31 15 843990 : the Netherlands VOICE : +31 15 842032
tom@eds.com (Tom H. Meyer) (12/05/90)
In article <RSW.90Dec4075159@tahiti.cs.brown.EDU> rsw@cs.brown.EDU (Bob Weiner) writes: >The key question is whether you know at class design time whether or not >objects of the class type will be used on the list. Even if you don't, >you could still define a somewhat generalized protocol (varying numbers >of parameters are still a problem) in which all of your non-generic >classes have an arbitrary 'action' procedure. Then when you pop an >object off the list into an entity of type ANY, simply invoke >'entity.action(params)'. Eiffel's dynamic binder will do the rest. > >My guess is that your problem is more complicated than this, so you will >have to elaborate some more. Indeed. I've encountered this problem several places but the following should illustrate the problem more clearly: (btw, I cannot distribute code I write for my employer over the net so I hope a verbal description will suffice.) The problem domain is a relational database query compiler. In particular, we are writing an SQL compiler so I will use SQL examples. Consider two queries: select a from T This query will retrieve all the values of the field named 'a' from table T. (I apologize if you already know all this, sorry to be a bore) select a + b - 2.0 from T Similarly, this query will perform the expression on each tuple and return those values, one per tuple of T. Somewhere in the grammar for SQL there is something like: e-expr :: e-expr | binary-e-expr | literal | attribute meaning that an elementary expression can be several things including such disparate things as arbitrarialy complex sub-expressions or a simple attribute for a table. Attributes contain lots of information about their location within the tuple, where their null bit is, and other similar things that are totally unrelated to, say, a binary e-expr. The reverse is also true. This makes it difficult to come up with a really tight generic class. The actual classes for the base cases in such a grammar tree look extremely different. When I'm compiling an e-expr, I have a 'incoming' list of e-exprs that I need to deal with and take appropriate actions for each one. Unfortunately, a single generic 'action' feature isn't good enough. For example, literal e-expr's have a value. In the previous query there is a literal e-expr of (say) type real with a value of 2.0. During constant folding, you need to know the actual value so you must, somewhere, know explicitly that this is a real. It could have been a double, boolean, integer, character, etc. and you must know it's actual type to deal with it. While constant folding of numeric types is fairly generic (not completely), it is quite different from folding the other types. I cannot envision writing generic constant folding code that wasn't extremely arcane and artificle. IMHO, that's letting the tail wag the dog. Actually, I can't see how to do it at all but perhaps that's just my own inexperience. There are many similar, even more difficult examples but they are too detailed for such a discussion. I don't know how compelling you found this example but I hope that it, at least, gave a clearer picture of the sorts of problems you can encounter. Perhaps the kernel issue here to me is one of letting the programmer deal with the paradigm they are most comfortable with. If writing generic, typeless code results in bizzare gyrations and a lack of clarity, it is the wrong tool for the job. I do not believe in the "one tool solves all" theory. If it's more natural to deal with explict types, fine. Thus, my reverse assignments. I also acknowledge this could result from my programming background rather than a real limitation of Eiffel. If this still isn't detailed enough, I'll try to spend some time to come up with some real code that illustrates the problem. tom meyer, EDS Research | If I don't see you in the future ...uunet!tantalum!tom | I'll see you in the pasture
rick@tetrauk.UUCP (Rick Jones) (12/05/90)
>In article <1037@tetrauk.UUCP> I wrote: >> >> a.Create (params) a := Create A_CLASS (params) In article <4106@tantalum.UUCP> tom@ozmium.UUCP (Tom H. Meyer) writes: > >I dislike this on the grounds that it would *require* that 'Create' be a >keyword. I hope to see the day when classes can have more than one >creation routine and this seems to disallow it. Of course Create currently IS a keyword in effect. The idea of multiple creates is interesting, but if you needed them, would they not be differentiated by their parameters? This raises the totally separate subject of routines overloaded on signature, something which Eiffel does not support at all and which I've had occasion to wish for. If this were to be added, the big question as I see it is: should the binding of routine selection be static or dynamic? Eiffel's general approach is that everything is dynamic, but my guess is that dynamic binding of routine selection would be an horrendous overhead. However, static binding would probably adequately solve all the practical needs for such a facility. This is not something I've tried to examine in depth - perhaps it could be a topic for a NICE technical commitee? Going back to Creation (in the non-biblical sense), a technique I have used a few times when object creation is non-trivial is to have an "object generator" class. This class offers one or more functions which return newly created objects. This allows different routines to create objects of the same type, and can invisibly create descendant objects of the type actually declared. In this sort of circumstance, there only needs to be one generator object in the whole system, and so it can be provided as a once function in a "definitions" class (this is an actual use of the "global object" concept I posted recently). I suspect this ends up being similar to Objective-C's "factory objects"; that's just a guess as I haven't actually used Objective-C. On your topic of differentiating generic objects, I can't add much to the discussion with Bob Weiner. I will say that in my work the cleanest solution has often only come after several iterations and re-thinks, and in several cases what I've ended up doing is a lot different from what I expected to do when I started. Much lateral thinking is called for to get it right. However, I think perhaps I agree with the sentiment that OO is not the optimal paradigm for expression parsing - or else nobody's worked out the best way to do it yet! It's not my area of expertise so I shall refrain from commenting further. Keep posting! -- Rick Jones Tetra Ltd. Maidenhead, Was it something important? Maybe not Berks, UK What was it you wanted? Tell me again I forgot rick@tetrauk.uucp -- Bob Dylan
rsw@cs.brown.EDU (Bob Weiner) (12/07/90)
In article <4144@tantalum.UUCP> tom@eds.com (Tom H. Meyer) writes: > Indeed. I've encountered this problem several places but the following > should illustrate the problem more clearly: > > The problem domain is a relational database query compiler. In particular, > we are writing an SQL compiler so I will use SQL examples. > > Somewhere in the grammar for SQL there is something like: > > e-expr :: e-expr | binary-e-expr | literal | attribute > > meaning that an elementary expression can be several things including such > disparate things as arbitrarialy complex sub-expressions or a simple > attribute for a table. > Now my question becomes whether you want to dispatch on a single type or multiple types at certain points. If just a single type, then I see no problem. Define types of COMPOSITE_E_EXPR, BINARY_E_EXPR, LITERAL_E_EXPR, and ATTRIBUTE_E_EXPR, which then have subtypes. In the case of the literal, you could have INTEGER_E_EXPR, etc. Again, each of these could have an 'action' procedure that takes a parameter, even the operation to be applied within the expression. If you want to dispatch on multiple types, e.g. an optimized selection procedure when one does an AND selection of data based upon two fields, F1 and F2, then I would say that Eiffel does not provide a simple mechanism to accomodate you. C++ and CLOS make this easier through function overloading and generic functions respectively. > Attributes contain lots of information about their location within the tuple, > where their null bit is, and other similar things that are totally unrelated > to, say, a binary e-expr. The reverse is also true. This makes it difficult > to come up with a really tight generic class. The actual classes for the > base cases in such a grammar tree look extremely different. > But that is the whole point of the 'action' feature, so you can perform quite different actions selected by type. > When I'm compiling an e-expr, I have a 'incoming' list of e-exprs that I need to > deal with and take appropriate actions for each one. Unfortunately, a single > generic 'action' feature isn't good enough. For example, literal e-expr's > have a value. In the previous query there is a literal e-expr of (say) type > real with a value of 2.0. During constant folding, you need to know the > actual value so you must, somewhere, know explicitly that this is a real. No, the dispatch mechanism, whatever it may be, must know this in order to select the appropriate operation. You can choose to use the dynamic binding mechanism or produce a more limited more optimized dispatcher yourself, though I would do that only as a last resort. > It could have been a double, boolean, integer, character, etc. and you > must know it's actual type to deal with it. While constant folding of > numeric types is fairly generic (not completely), it is quite different from > folding the other types. I cannot envision writing generic constant > folding code that wasn't extremely arcane and artificial. My point is not to write procedures that deal with arbitrary constants or expressions but more simply and elegantly to use dynamic binding to achieve specificity of operation. Meyer deals with this issue in Object-oriented Software Construction. > > Perhaps the kernel issue here to me is one of letting the programmer deal > with the paradigm they are most comfortable with. If writing generic, > typeless code results in bizzare gyrations and a lack of clarity, it is > the wrong tool for the job. Or the design may simply be a poor one. But I agree, let's not use wrenches to clear our pipes. OO design is about building the proper tools to solve domain-specific problems. SQL is an example of a language that does not provide this flexibility. If one's database model does not fit well into a linear table format, there are still many SQL professionals who will cram it in to one. Now, of course, many people are working on object-oriented databases to overcome this precise limitation. > If this still isn't detailed enough, I'll try to spend some time to > come up with some real code that illustrates the problem. > This would probably help. I still feel I haven't understood the essence of your problem. My knowledge of SQL is quite minimal, I assure you. -- Bob Weiner rsw@cs.brown.edu
tom@eds.com (Tom H. Meyer) (12/07/90)
In article <1045@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes: >>In article <1037@tetrauk.UUCP> I wrote: >>> >>> a.Create (params) a := Create A_CLASS (params) > >In article <4106@tantalum.UUCP> tom@ozmium.UUCP (Tom H. Meyer) writes: >> >>I dislike this on the grounds that it would *require* that 'Create' be a >>keyword. I hope to see the day when classes can have more than one >>creation routine and this seems to disallow it. > >Of course Create currently IS a keyword in effect. The idea of multiple >creates is interesting, but if you needed them, would they not be >differentiated by their parameters? Not necessarily. It's easy for me to envision two different creation routines that had identical interfaces. There's also a question of implicit information here. I usually oppose implicit information. In this case we have, "all creation routines are named 'Create' but you tell them apart by their arguments." I'd rather see all the creation routines given complete liberty with their name and, perhaps, add some additional keyword to identify this function to the compiler as a (additional) creation routine. Perhaps other_create (...) is create_proceedure require ... do ... end; >Going back to Creation (in the non-biblical sense), a technique I have used a >few times when object creation is non-trivial is to have an "object generator" >class. This class offers one or more functions which return newly created >objects. This allows different routines to create objects of the same type, >and can invisibly create descendant objects of the type actually declared. In >this sort of circumstance, there only needs to be one generator object in the >whole system, and so it can be provided as a once function in a "definitions" >class (this is an actual use of the "global object" concept I posted recently). >I suspect this ends up being similar to Objective-C's "factory objects"; >that's just a guess as I haven't actually used Objective-C. Sure. That's actually a better solution than what I had in mind. Since it's fairly straight forward to get the functionality, perhaps it's better to not clutter up the language with the additional features. >On your topic of differentiating generic objects, I can't add much to the >discussion with Bob Weiner. I will say that in my work the cleanest solution >has often only come after several iterations and re-thinks, and in several >cases what I've ended up doing is a lot different from what I expected to do >when I started. Much lateral thinking is called for to get it right. I certainly can see that. OOP is rather different is style and feel to the other paradigms and it takes getting used to. There is a raging debate right now about how to do object oriented design, capture the objects, etc. tom meyer, EDS Research | If I don't see you in the future ...uunet!tantalum!tom | I'll see you in the pasture