From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,ee0dc912649d50d4 X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news1.google.com!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: Ada DB bindings and APQ Date: Mon, 20 Dec 2004 21:01:34 +0100 Organization: cbb software GmbH Message-ID: References: <1km3c584awura$.y7djkir1ozya$.dlg@40tude.net> <4KOvd.54$jT5.8@read1.cgocable.net> <5s8i4q5psxsn.ap1vrcl5pf02$.dlg@40tude.net> <18ns1q1fkc6st.15vfm6brappgj$.dlg@40tude.net> <5w0xd.649$Wt5.249397@read2.cgocable.net> <1tusn81lxo8m0$.67it7z7w00ov.dlg@40tude.net> <1f24uc05urd9n$.1xgzrog11d8pu.dlg@40tude.net> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Trace: individual.net KWagaWyz7wPvBDufTXBXaw5Bwkv9Yg6qYhWgXaDNsmT1AwCwU= User-Agent: 40tude_Dialog/2.0.12.1 Xref: g2news1.google.com comp.lang.ada:7100 Date: 2004-12-20T21:01:34+01:00 List-Id: On Mon, 20 Dec 2004 00:33:31 -0500, Warren W. Gay VE3WWG wrote: > Dmitry A. Kazakov wrote: >>>>Introducing Connect, you >>>>add complexity and various sources of errors. I can understand where >>>>Connect comes from: Ada has no proper constructors with parameters for >>>>limited objects. This is the reason for handles (they are non-limited). But >>>>your objects are not limited, so there is no reason for creating >>>>half-backed objects: >>> >>>Look, it has nothing to do with Ada or "half baked" objects ("baked" >>>is what I think you meant). It is all about control. If we took your >>>view, you would never allow a Rewind operation on a file. >> >> Exactly, because not every device would allow it. It would be a bad design >> to allow rewind that throws Use_Error. A proper design would be to derive a >> class of files which can be rewound from the base class of files. > > So Ada's Rewind operation is faulty in your view? Ada 83 didn't have that elaborated type inheritance of Ada 95. I'm almost sure that a possibility to have several File_Types was considered that time. > C's fseek() is faulty in your view? C'mon now. Really. Yes, in my view In_File, Out_File should be not modes but [sub]types of File_Type. After all we have that for in/out/inout parameters: Where is any difference? Logically, there is no one. BTW, according to your point of view, shouldn't File_Type and File_Operation be two independent types? Open is called on File_Type. Read/Write are on File_Operation. Disagree? (:-)) > We're all big boys here, and can figure out when Rewind is > proper and when its not. I support Ada and "its ways", but > let's not expect a "rubber room" in which we can never do > something harmful. If that ever happens, then I think it is > probably safe to say that we've wound up at the Asylum. The Ada's way is to allow harmful things only when prohibiting them would be too expensive. I see nothing expensive in eliminating a type. (:-)) >>>You would have to destruct it and recreate it. >> >> Yes, because the file was a socket! > > Whether it is a socket or a named pipe, anonymous pipe or microkernel > message queue, doesn't matter to the user. That is a physical > detail. What matters is the logical view. The same physical thing may support different views. In OO terms: the same thing may implement many interfaces. But it is a very bad idea to implement interfaces partially. Again, in OO terms it is called LSP violation. If we know for sure that we cannot rewind a file, it is awful to say, well, we can, but the result is an error. > This detail however, does sometimes poke through to the > user, for example, when specifying say the URI for connecting > to the database, but otherwise how the connection is made is > imaterial (the "how" does put requirements on how the connection > is specified, but otherwise the details are not important). > > But back to the orignal point, why this means that I have > to do a construct/destruct just to connect or not? Because the object is unusable when not connected. > Here's another reason why this requirement is not > a good thing (TM): > > Since (at present) the Connection_Type object includes the > connection environment as well, every time you construct the > object new, you also have to reconfigure/specify all of that > connection information. Now if you add parsing of the URI > (instead), it starts to add some significant overhead > (admittedly not a lot, compared to the time required to > establish a connection - but still this is wasted work being > repeated each time). From a design perspective, this > is bad, because you unnecessarily repeat processing what > need only be done once (many applications only ever deal > with one database/schema). This is in fact one argument in > favour of the environment object, but let's not flog that > horse.. You are talking about *re*connecting. It is another thing. Provide a method for that, if this option is essential. >>>An object goes through states, sometimes many of them. I see nothing >>>half baked about being connected or not. >> >> Do not forget separation of implementation and interface. There are logical >> and physical states, there are interface and implementation objects. They >> need *not* to be same. You are trying to throw everything in one cauldron. > > No, I disagree. The user need not understand all of the > specifics whether they are physical, or Sybase or MySQL > specific API issues (APQ hides this ugliness). What the > APQ API _does_ support is the simple idea that the user's > object is "connected" or "not connected". Where is the > cauldron there? Because "connection" and "query" are implementation details. The only real thing is a DB. Consider a DB which does not use server/client model. Consider a DB fully integrated into OS, with access authentication based on application rights. You might be always connected to it. >>>What IS half baked about it >>>is that it combines Environment & connection! By putting them in >>>one object, I multiply the number of states and methods for a >>>combined object. >> >> These states are of no user interest. I do not need an unconnected >> connection. Really, it is like inedible food. > > If I am using the Booch components (for example), what you > are suggesting is that I should not have a Map object, if it > is empty ("inedible food"). After all, according to your > logic, what can you do with an empty Map object? > You would only have a Map object created, when it > contains at least one object. But writing code to work this > way is more work, and pointless without a special requirement > to do so. This is a good example. Thank you. The decision is not based on object states, which are internal and of no interest. [ BTW the best possible design is stateless objects (i.e. pure undistinguishable values, a la functional programming) ] The decision is based on the public interface: which methods are applicable. If we consider maps, then we will see that everything what can be done with a non-empty map can be also done with an empty one. Mathematically: an empty pair-set is a set. In OO terms: LSP holds. This is the reason WHY no empty maps are needed as a separate [sub]type. Now consider one particular method: function Size (X : Map) return Natural; Should we change Natural to Positive above, for whatever reason, THEN Size will cease to be a method of Map and *only* then we will have to have General_Map with no Size defined and its specialization Non_Empty_Map with Size. > There is nothing wrong with objects having defined states. Just > because an object is empty or disconnected, is not an argument > by itself against the existence of the object as you are > insisting it is. There might be other requirements that make > this a good idea, but this argument by itself does not decide > the case IMHO. See above. The most important argument *for* having unusable objects are 1. A language deficiency to provide constructors with parameters. 2. Questionable design of containers, which is closely related to 1. Usually, if you want to have a container of objects you have to provide a kind of Null_Object to initialize unused slots. That Null_Object could be that unconnected one. There might be better ways than though. >> If you compare the time required to connect with the time required to >> initialize an interface object you will find that the latter is negligible. > > Time and overhead is not the only factor. At the point where > you want to connect to the database, you might not have > access to the very parameters that you need to supply (this > often happens in GUI callbacks). It is irrelevant. If you have to pass construction parameters then you have to do it. There is no reason for storing them into the object. > An unconnected object can preconfigure all of the parameters > necessary for a connect operation to be done at some other > point in time. By your linking of these two "actions", you > now force the user to drag around all of this extra baggage, > in order to affect a successful "connect". In effect, you've > made the API more difficult for some users. Quite the opposite. You have packed connection parameters where they do not belong to. It is like to pack file name, path, OS version, time stamp etc into File_Type! > OO programming borrows from some of our life experiences. Philosophy! He, he. BTW, as for that infamous OO-religion: God programmed the world in the OO way. Well, I am an atheist! >>>That way I don't have to force the user to use types that I dream up. >> >> [ Ada 2005 will finally have multiple inheritance restricted to interfaces. >> That will solve the problem without ugly generics. ] > > I don't see how MI is going to fix this problem, but no matter. > >> Look, you describe the interface of a DB type and user have to implement >> it. Note that it is not you, who forces the user, but a very real thing, >> the DB. There *must* be a mapping between DB and user types. Your "dreamed >> up" types just embody that mapping. > > You've missed the point. In Ada I have the choice between > using something like Integer or a type that I define: > > type My_Int is range 0..5; > > Your approach is to force the user to derive their type from > something that APQ provides (or something standard Ada > provides). Not to derive, but to implement. > For example, APQ could say that if you write an > integer out to a blob that you have to use APQ.Blob_Integer or > some such. > > type My_Int is new APQ.Blob_Integer range 0..5; -- eg > > But if I were the programmer, I would not like > this much, because I'd want to use My_Int instead (maybe it > is defined by a 3rd party package). But you cannot use My_Int because, for example, the DB might support no safe conversion to/from it. Again, DB is the real thing here! Remember my classification? It is B-bindings. So My_Int is imaginary. If you want to fix My_Int, then that should be A-bindings. Though it would be difficult to do, because Ada does not support classes for all types. We could imagine in Ada 2100 (:-)), something like dispatching user-defined attributes. Then our children could write: declare I : My_Int := ... begin Append (Q, I'APQ_Value); BTW this requires multiple dispatch, because Append dispatches on Q, and so determines the expected type of I'APQ_Value, which in turn dispatches on both the result (the type expected by DB) and the type of I (the type provided by the user). >> User-defined type conversions might do it very easy and elegant, but that >> is another story. > > I hate unnecessary use of "conversions". They are a great source > of error. It is a myth caused by automatic conversions of PL/1 and C. There is nothing wrong with conversions. After all when you derive one type from another you implicitly define conversions. Inheritance and conversions are equivalent. > This is why APQ goes out of its way to provide generic > procedures so that programs don't have to be written with > conversions all over the place. It reminds me too much of casting > C types all over the place -- it strips the compiler of much of > its type safety. Right, the conversions have to be applied as necessary. Explicit conversions are as bad as ones automatically generated at compiler's/language designer's will. The compiler should invent nothing, but being told it should be intelligent enough to understand it at one dash. If I say in some scope that Unbounded_String is convertible to String (=Unbounded_String is an in-subtype of String), then within this scope everywhere a String is expected a value of Unbounded_String should be acceptable as well. It is stone-safe. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de