comp.lang.ada
 help / color / mirror / Atom feed
From: "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de>
Subject: Re: Ada DB bindings and APQ
Date: Mon, 20 Dec 2004 21:01:34 +0100
Date: 2004-12-20T21:01:34+01:00	[thread overview]
Message-ID: <w0s4k17d9dyl$.nnr4qrtuu42h.dlg@40tude.net> (raw)
In-Reply-To: Yttxd.834$jT5.675@read1.cgocable.net

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



  reply	other threads:[~2004-12-20 20:01 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-12-12 17:42 Ada DB bindings and APQ Dmitry A. Kazakov
2004-12-14  7:25 ` Warren W. Gay VE3WWG
2004-12-14 17:37   ` Dmitry A. Kazakov
2004-12-14 18:29     ` Georg Bauhaus
2004-12-14 19:45       ` Dmitry A. Kazakov
2004-12-14 21:06         ` Georg Bauhaus
2004-12-15  8:19           ` Ole-Hjalmar Kristensen
2004-12-15 17:20           ` Dmitry A. Kazakov
2004-12-16 13:28             ` Georg Bauhaus
2004-12-17 13:23               ` Dmitry A. Kazakov
2004-12-14 23:26         ` Brian May
2004-12-15 17:43           ` Dmitry A. Kazakov
2004-12-15 21:54             ` Brian May
2004-12-15  4:05     ` Warren W. Gay VE3WWG
2004-12-15 18:26       ` Dmitry A. Kazakov
2004-12-16  2:53         ` Warren W. Gay VE3WWG
2004-12-18 16:43           ` Dmitry A. Kazakov
2004-12-18 20:36             ` Warren W. Gay VE3WWG
2004-12-18 22:21               ` Dmitry A. Kazakov
2004-12-19  0:53                 ` Warren W. Gay VE3WWG
2004-12-19 12:21                   ` Dmitry A. Kazakov
2004-12-20  5:33                     ` Warren W. Gay VE3WWG
2004-12-20 20:01                       ` Dmitry A. Kazakov [this message]
2004-12-20 20:54                         ` Warren W. Gay VE3WWG
2004-12-14 22:40   ` Brian May
2004-12-15  3:23     ` Warren W. Gay VE3WWG
2004-12-15 15:01       ` Georg Bauhaus
2004-12-17  4:31         ` Brian May
2004-12-15 10:48   ` Brian May
2004-12-16  1:40     ` Brian May
2004-12-16  3:10       ` Warren W. Gay VE3WWG
2004-12-17  4:55         ` Brian May
2004-12-17 11:13           ` Warren W. Gay VE3WWG
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox