A key viewpoint expressed by HughDarwen and ChrisDate in TheThirdManifesto (and repeated by the latter author in recent editions of AnIntroductionToDatabaseSystems):
When mapping "objects" into a RelationalDatabase (for some definition of "object"), objects should be mapped into what are known as domains in the RelationalModel. Treating a relation/table as a "class" and treating a record/row as an "object" (or as an isomorphism of an "object") is, in their view, a fundamental mistake which they call the FirstGreatBlunder.
But the FirstGreatBlunder page says:
The first great blunder, is the treatment of relations (in the RelationalDatabase sense) and classes (in the ObjectOriented sense) as equivalent.
This is not at all the same thing. What do DarwenAndDate? actually say? In particular, where do they say that tuples cannot have behaviour and act polymorphically? This does not appear to be any such proscription in the paper version of TheThirdManifesto (http://citeseer.ist.psu.edu/darwen95third.html).
On the other hand, see FirstGreatBlunderRefuted.
"... treating a record/row as an "object" (or as an isomorphism of an "object") is, in their view, a fundamental mistake which they call the FirstGreatBlunder."
Do they say why? Clearly, if you're going to use an object-oriented language at all, then both records/rows and relations/tables must be objects (unless they are built-in, which doesn't seem sensible; or not first-class, which is definitely not sensible).
[Only if you think every datum in a program must be an object,
I was postulating an object-oriented language.
and then a tuple is an object in only a trivial sense - it's data, and has no behavior other than trivial data-like trivial actions such as copy/construction/attribute-selection.
Nevertheless it is an object. And even if mutable object state is problematic (I don't think it is), there is no reason why it can't be an arbitrary ValueObject; a row doesn't have to be a [primitive] tuple.
And what's not sensible about having database concepts built-in? (See below for more give-and-take on this.)
Part of what makes database programming more difficult than it needs to be is the fact that the concepts are not built in to most languages.
They should be built-in to standard libraries, IMHO - not languages.
But you can't build relational language into a library. Imagine putting relational into the C language. Not possible! C language is closed and finished. Putting it into a C library how? All you can do with C is something like mysql_query() functions, or call_tutorial_dee("strings here") which deal with dumb strings, not relational functionailty built into C itself. I suppose some languages that support domain specific language extensions might be able to build it into the libraries. There are also products (crap?) on the market like MicrosoftLinq that seems to try to address the problem, but likely fails at actually implementing relational properly.
[Making "rows" something other than tuples leaves you with a model that is not the relational model, so you would be inventing a new data model, and presumably an algebra to go with it. The point behind TheThirdManifesto is that the relational model has proven to be a very good and consistent model for working with data, with a complete and well-understood set of primitive operations, but that implementations to date have been lacking in their fidelity to the model. The authors also try to reconcile to some extent the object oriented models popular for application programming with the relation model - without doing violence to the latter.]
Yes relational is great for working with data, but objects work with data too! The advantage of OOP and procedural programming is you can modify the object data, or in C the STRUCT, and then run code tightly integrated with that data. With a database, the data is further away from you, not like a struct or object. You may be thinking: aha! separation of concerns! Well, let me ask: why is it people still use INI/XML files instead of SQLite or oracle databases? Because the database is too far away to be useful. It's still not built into your tool. SQLite comes close as being a replacement for INI/XML files, but is SQLite a replacement for associative arrays, hashlists, and lists? Not really, we still use lists and associative arrays, and even regular arrays. The databases are too hard to integrate with our tools, they are too separated and far away. What could be done, is merging the database right into the code, so that people stop reinventing the database with lists, associative arrays, hashlists, INI files, XML files. It would be nice to just use relations and create an INI file automatically, without actually worrying about the code that stores the physical INI file.
[Along the same lines, since the relational model is well-defined and stable, there is no particular reason to prefer libraries over built-in primitives, although either approach can work. In many languages, though, it's hard or impossible to write libraries that provide the same convenience of use that built-in concepts do, hence the preference for concepts being built-in; for other languages, the choice is a non-issue. By all means, let tuples be represented by objects, so long as those objects have the characteristics required by the relational model of tuples.]
I don't see there as being any contradiction between "a complete implementation of the relational model", and letting tuples have polymorphic behaviour.
What DarwenAndDate? mean in the above is that arbitrary objects (things like EmployeeId? or Currency, etc.) ought to be domains; and that there ought not be a canned isomorphism between any arbitrary object and some relation or tuple. Some ObjectDatabase products create such an isomorphism, and declare that if I have a class that looks like this in the code:
class PersonsName? {
string prefix; // Mr, Mrs, etc
string surname;
string givenName;
string middleName;
string suffix; // Jr., III, Esquire, etc.
};
there must be a table somewhere whose columns correspond to the data fields in the above class. This is what Darwen and Date are objecting to.
Consider the following approach: model tables as indexable multisets of objects. In a standard library, provide implementations of these multisets (ordered for efficiency), with associated relational algebra operations. Wrap the objects that are put into these multisets in such a way that changes can be detected and any table invariants maintained.
There is no need to model different kinds of collection with inheritance, since multisets are general enough. The value of the object-oriented features of the language are in providing multiset implementations with different characteristics (persistence, transaction support, interfacing with other systems, etc.), and in modelling behaviour associated with the rows/objects.
(Alternatively, use sets and relations in place of multisets and multirelations. This choice is independent of the ObjectVsRelational? issues.)
What, if anything, is wrong with this approach? I really want to know, because I intend to use it.
In a pure-OO language, it might make sense to model relations and tuples as objects - but of type Relation and type Tuple. A Relation object would act like any other collection class in the language (Array, List, Dict) and have methods for iteration, access, and (possibly) mutation (insert, delete, etc.). In addition, it might have methods to support the RelationalAlgebra - join, project, select, etc. Likewise, tuple/record might be a class (database tuples are little different than the standard associative container found in most languages - whether its called "map" (C++), "hash" (Perl), "dict", or something else) with appropriate operations.
But having class PersonsName? inherit from class Relation is, to DarwenAndDate?, a no-no.
Why would it? PersonsName? is a row object; Relation would be a special case of a table - a table of (domain, codomain) pairs with no duplicates, to be precise.
[That's fine, but in a sense it's an implementation detail. To conform to the essentials of TheThirdManifesto, you would consider only the parts of your object model that correspond to relational concepts - relation, tuple, attribute, etc. Consider whether the operations you provide for these classes fulfill the requirements of the TheThirdManifesto - that's enough to keep anyone busy for a year or two. I'm not sure what you'd do with your "Table" base class and other descendants thereof - perhaps invent new operations on them that you find useful - but that would have little or nothing to do with the portion of your system that is concerned with the relational model. I suspect that they would likely add little of use that you couldn't get out of a complete implementation of the relational model - but you'd have to convince yourself of that by studying the RM in more depth.]
Can anyone give an example of arbitrary objects being domains, preferably in SQL?
Not in SQL; the fact that SQL has a crippled type system is one of the many SqlFlaws. TutorialDee and other "advanced" database languages, OTOH, do support what Darwen and Date propose.
Ah, that wasn't clear. So before we can apply DarwenAndDate?'s advice, we have to convince the powers that be to stop using SQL. Piece of cake. :-)
That's why they called their book the TheThirdManifesto, and not TheThirdHumbleSuggestion? or TheThirdPoliteRecommendation?. :) What they propose is a somewhat radical transformation to key parts of the IT infrastructure; a proposal bordering on LetsBlowUpTheUniverse. (Which isn't to say that they don't have some good ideas.)
- ChrisDate explicitly disagrees with that interpretation: "Note that we are definitely interested in evolution, not revolution. By contrast, consider this quote from the ODMBG book [24.13]: '[Object DBMSs] are a revolutionary rather than an evolutionary development ' (italics added [by ChrisDate]. We do not believe the marketplace is ready for revolution, nor do we think it needs or ought to be -- which is one reason why The Third Manifesto [3.3] is very specifically evolutionary, not revolutionary, in nature." [footnote, first page of chapter 25, "Object/Relational Databases", in "An Introduction to Database Systems", 7th edition, C. J. Date, copyright 2000.
- The "radical" transformation they suggest is actually pretty common-sensical: that relational databases should support "domains" (AbstractDataTypes) thoroughly, e.g. by allowing user-definition of abstract type names, along with supported operations and their implementation, and then e.g. allowing those operations to be used in query language WHERE clauses in queries and updates. This is their remedy for the FirstGreatBlunder, and they claim that Object/Relational Databases would then be a subset in power of such an ADT-supporting relational database. One may or may not agree, but (A) it's persuasive that ADTs should be supported, and (B) Date is too-often disagreed with prior to being properly understood (present company excepted). -- DougMerritt
I agree that they have good ideas. I just wish they had good ideas I could use today.
[You can, with some effort or expense. See for instance http://www.alphora.com/.]
I can't use that today. It only works on .NET. This reminds me of Catalysis. I wish the authors would spend their energy writing software I can use instead of manifestos.
[Yes, that criticism seems to be frequently made in one form or another. But I see the Manifesto as a step in that direction, just like many theoretical research papers or doctoral theses. People are using it as a basis for new implementations, and maybe some of the ideas will need to be revisited by them or by others as experience accrues. These things take time.]
There is also DuroProject? and RelProject
Likewise, use of TableInheritance and the like to implement polymorphism is an extension of this particular "blunder".
This viewpoint has quite a few interesting consequences:
- As the only mutable thing allowed in the RelationalModel are RelationalVariables, any such object ought to be an immutable ValueObject. This eliminates the need to introduce object identity into the RelationalModel; and also prevents database invariants from being violated by object mutators being invoked "behind the database's back".
There are other ways to prevent database invariants from being violated by object mutators; for example, use a HandleBodyPattern to ensure that the database always "knows" when an object has been mutated.
[The relational model defines database constraints declaratively; they're much simpler to define this way.]
Yes, but preventing invariants from being violated is an implementation-level problem. Using a HandleBodyPattern to detect mutation solves that problem, while being transparent to both the object and users of the table. The effect of mutating an object is then just the same as if the row was updated in an RDBMS.
[That's fine, but this isn't a discussion about implementation, but rather about the facilities presented to a programmer that uses a relational database system - about the logical model that the programmer uses. Issues of implementation are an entirely different ball of wax.]
The implication was that it was necessary to use immutable ValueObjects to prevent database invariants from being violated by object mutators invoked "behind the database's back". This is purely an implementation issue. In the logical model, invoking a mutator method is just like a record update. The database maintains its invariants somehow; the programmer doesn't care how. I mentioned one way to implement this simply as an existence proof that it is possible.
- The primary organizing principle remains relations and tuples - other collections can be attribute values (but must remain immutable). (It should be noted that Darwen and Date advocate allowing relations themselves to be attribute values; though most RDBMS products, and StructuredQueryLanguage, do not).
- Individual objects can be polymorphic and be encapsulated (no need to expose the attributes of a particular object to the world; as objects aren't rows/records they aren't subject to ad-hoc queries).
"[Making "rows" something other than tuples leaves you with a model that is not the relational model, so you would be inventing a new data model, and presumably an algebra to go with it.]"
No new model or algebra is required. You can treat an object as a tuple by using no-argument methods in place of field selectors; unlike a primitive tuple, however, the fields can be computed rather than stored, and update methods can be used instead of updating the tuple fields directly.
[In many languages, this would be impractical or at least very cumbersome, unless the concepts were built-in. Relational expressions (queries) generate new relations (thus giving the algebra the important characteristic of closure, which is missing in SQL), with new tuple "types" -- i.e. tuples that are made up of new, possibly unique groupings of attributes.
Query expressions thus act as type generators. In some languages, this can indeed be done; in others, it can be done only with great difficulty (e.g. templates in C++). In statically typed languages, you have the added complication that the implied type of a particular attribute in such a derived relation need not be exactly the same as its declared type in the input relations.]
Yes, this does map more easily to dynamic OO languages than to languages where all types need to be known at compile-time. OTOH it has been done in C++; there was an article recently in C/C++ User's Journal about a system fairly similar to this.
[Also, it must be possible to reference the attributes of a tuple in the context of a relational expression, where you are not addressing a particular tuple's value but rather are using the reference as a placeholder to represent the values of all tuples of a referenced relation. (Consider something as simple as SQL's WHERE clause.) Treating tuple types as unique classes with attribute setters and getters would make this very difficult, or would at least require a very different syntax for attribute references in relational expressions.]
That's not difficult in any language with FirstClass closures.
[Applying the closures would be difficult in the general case. In a networked DBMS, they'd be supplied by the client, but would have to run on the server in order to work at all efficiently. Not impossible, but it seems to add a lot of complexity -- for what benefit? The relational model already includes an extension operation, which serves as the basis for calculating attributes.]
- Another possibility is to use method selectors which are applied using reflection (Smalltalk "perform:", etc.). Anyway these are implementation details.
- The benefit is that when accessing a database from an object-oriented language, it's natural to use object methods to compute attributes. If you use a separate extension mechanism to do that, it is effectively duplicating part of the functionality of objects (but less flexibly, since interface polymorphism is much more difficult).
- [Building these mechanisms into an RDBMS duplicates the functionality already there, so that argument cuts both ways. The argument that "it's natural" is weak; within the context of the relational model, extension of a relation with a calculated attribute is natural and well-defined. If you're really stuck on OO as the only viable programming paradigm, then there's not much to discuss.]
- My interest is in multiparadigm systems, and so I view support for OO as essential. It's certainly not the only viable programming paradigm, but in a multiparadigm system it makes sense to define the "things" that are first-class values as actors, closures or objects (these are equivalent to each other for the purposes of the discussion on this page, so I'll just say "object" from now on). Relations are not equivalent to objects; they can't support an arbitrary interface.
- [(How deep can the asterisks go???) Well, D&D do describe a way in which the relational model can support objects.
- You mean "domains are object classes"? That is quite limited, since it doesn't support polymorphic behaviours for objects that are best modelled as a composite of several fields. OTOH it does present some of the same implementation problems (for example objects mutating without the knowledge of the database).
- I'm not certain that they got all the details right, but I'm unconvinced that polymorphic tuples make sense. More on this below. I'm also not sure what you mean by "arbitrary interface" for relations; ...]
- By "arbitrary interface" I just mean an application-defined collection of method signatures. Although quite a lot of things -- including almost any kind of collection -- can sensibly be modelled as relations (hence the value of the relational paradigm), it wouldn't make sense to model, say, a USB port as a relation.
- [... I would think that modelling relations as polymorphic objects would be no more difficult (and perhaps less so) than doing the same for tuples. Could you expand on that a bit?]
- Yes, relations can be modelled in terms of objects; it's the reverse that is problematic. Viewing relations as objects doesn't conflict with anything in TheThirdManifesto (at least the paper; I haven't read the book), AFAICS.
- Given that objects are strictly more general than relations, constructing objects in terms of relations is not an option. So relations must be constructed in terms of objects. (The same applies to many other concepts from other paradigms; constraints or monads, for example, also need to be constructed in terms of objects, not vice versa.)
- Calculated attributes are indeed perfectly natural within the relational model, but the model doesn't say anything about how they are supported. In designing a multiparadigm system it is particularly important to follow the OnceAndOnlyOnce principle, unifying similar concepts from different paradigms where possible. This reduces the overall complexity of the system, and facilitates MixingParadigms when operating on the same data. The calculated attribute mechanisms that are usually implemented in RDBMS's are not general enough to support objects. So, it makes sense to replace them with object methods, since we need object methods anyway.
- [Well, since we're in a multi-paradigm environment (which I applaud), I would think that closures or simple functions as first-class objects would serve well for defining extensions.]
- Closures are equivalent to objects. The question is what the environment of the closure or the state of the object is: is it a domain, or a row? I claim that it is more useful and more general for it to be a row, since that allows constraints and behaviours to be expressed in terms of several related attributes, rather than just for individual attributes. (Of course we can still use tuples that have only trivial accessors as a special case, when there is no need for anything more complicated.)
- [That doesn't say anything about a need for base attributes to be calculated via methods on a tuple.]
- In cases where no non-trivial behaviour is needed, we just use the built-in tuple type. If a programmer has chosen to use an application-specific class, then presumably there is a need for one.
- If we want private fields to be encapsulated and only used to compute other attributes, then methods will have to be written to access those fields.
- [Agreed that most RDBMS implementations are restricted (or are augmented using a particular language, such as Transact-SQL or VBA in SQL Server or Jet, respectively).]
- [Storing objects as attributes gives you a melding of both models, without requiring any modifications to the relational model.]
- The approach I'm suggesting doesn't require any modifications to the relational model either. The model already has to deal with "missing" tuple elements. (Well, maybe DarwenAndDate? would disagree with this, given their opinions about NULLs, but I think they are wrong on that point.)
- [I'm not sure how the NULL issue fits in here, but for the record, I side with Date on this. The complications to predicate logic introduced by NULLs doesn't sit well with me, and I have found their "respectable outer join" idea to work quite well in practice.]
- I need to look at their work in more detail before deciding about this.
- [Replacing tuples with objects raises numerous questions. Objects are supposed to encapsulate their data; tuples are supposed to expose theirs.]
- Objects expose their public attributes. They only encapsulate private data, and private data shouldn't be used in queries anyway. Suppose that instead of an object, we had a tuple in which some fields were intended only to be used to calculate other fields -- i.e. they were private by convention. That would be like using objects, except that there would be no enforcement of the convention (and the private fields would still have to appear in SQL queries because of the broken design of SQL).
- [I see where you're going, but I think that such a system will not scale well. Think about a large database with hundreds of thousands of records, each with a potentially different internal structure and different table of methods. How would such a system ever scale up? More on this later.]
- I'm assuming a language/environment with proper metaprogramming support. So there don't need to be separate method tables for every possible view; there only needs to be a way to produce the required behaviour given a description of which attributes are present in a view under which names, and a collection of objects that those attributes come from. I.e. there would be some set of explicitly programmed application-domain classes, but any other views would be generated on the fly.
- The fact that there are hundreds of thousands of records doesn't mean that there will be hundreds of thousands of distinct kinds of behaviour. There will be as many kinds of behaviour as the application needs -- typically on the same order as the number of explicitly programmed classes. This will scale as well as any OO application with hundreds of thousands of objects, but far fewer classes.
- [Objects can be polymorphically heterogenous; tuples within a given relation should have the same visible structure.]
- I'm assuming that by "visible structure" you mean the set of public attributes and their types. Polymorphism in the computation of any particular attribute presents no problems; on the contrary it's a feature. The presence of "extra" attributes not used by a query also presents no problems.
- [What about name clashes between "extra" attributes and user-defined extensions, for instance? Not a fatal problem, certainly, but having the latter shadow the former could certainly lead to surprises.]
- This is just nested LexicalScoping. Anyone using this system will need to have a solid understanding of lexical scoping, and the fact that variables in an inner scope can shadow those in an outer scope (NestedScopes). I intend that scoping rules will be consistent across languages and paradigms.
- You're heading towards a model that will be much more complex than the relational model -- and it's already difficult to get programmers to study and understand that model adequately (somewhat to my amazement).]
- The "vanilla" relational model will be the special case of this model where only built-in tuples are used as rows. If a particular programmer or project only wants to use that subset in their own code, that's fine by me.
- OTOH, they may have to understand or integrate other people's code that uses the full model. One of the drawbacks of any multiparadigm system is that you're more likely to come across code that uses techniques you're not familiar with. (There should be a WikiPage about multiparadigm pros and cons -- MixingParadigms isn't it.)
- I don't think that this particular extension of the relational model is difficult to understand, though -- basically, it's just abstracting from concrete tuples, to things that support the public interface of a tuple. I'll only know how complex people will find that in practice, when/if I have chance to implement it and get some real feedback.
- So that leaves cases in which a type is not a subtype of the expected type, or an expected attribute is missing. In a statically typed system, this can't happen as long as the type system is sound: the query is not well-typed. In a dynamically typed system, this would cause an error (probably an error value rather than an exception).
- [So the accessible attributes of your objects would have to be a subtype of the expected attributes within a relation in order to conform to the relational model. The only thing you'd get out of this exercise is the ability to attach "behavior" to the tuples; but what does it mean if that behavior varies polymorphically from one tuple to the next?]
- What's the difficulty in assigning a semantics to this? I also think you're undervaluing this ability: it is as useful for the behaviour of table rows to vary polymorphically as it is for any object. The motivations are exactly the same as for OO in general. I've frequently found the lack of support for polymorphism to be a limitation of RDBMSes -- it is very common, at least for the way I generally model things -- to find that "type" fields and conditional code are needed to implement differences in behaviour that would be more naturally expressed using polymorphism. The popularity of OODBMSes seems to back this up, but IMHO most OODBMSes go too far away from the relational model and end up losing some of its advantages.
- [Agreed on the latter point, anyway. :) What is the difficulty? OK, start with read-only methods. We have two kinds - one provide access to attributes that are expected, others are "extra". Now -- what use are the extra ones?]
- This is just the same as using an object via an interface that does not include all of that object's methods. The "extra" methods/attributes are not directly useful at that point in the code, but they are presumably useful to other code, or they wouldn't have been included.
- [ How would you know, given a relation value consisting of many tuples, which ones support or don't support a particular extra method? Via type codes? In the absence of static typing, I suppose you could just send a message and hope for the best. Doesn't seem very useful, or safe.]
- Well, that's an argument against DynamicTyping. This model could work with almost any type system, as long as it supports interface polymorphism. (Personally I like SoftTyping and static TypeInference.)
- Needless to say, the semantics would be well-defined regardless of whether a method exists (strong typing as defined in TypingQuadrant).
- [With respect to the methods that represent actual attributes of the relation, here I see a potentially very large implementation problem. For tuples of base tables, it might not be so onerous to track their class (or prototype), and efficiently find their method implementations when needed. But as new tuples are created willy-nilly by queries, you are potentially generating many new classes as the various slices (mentioned later) combine to form new tuples. I have a hard time imagining how such a system could scale to something useful given current or near future hardware. (But maybe you're on to something for the coming era of quantum computing...)]
- Generating new classes is only one possible implementation. If we use a single metaclass & view-description approach instead, the overhead of working out which attribute to get from which object should be acceptable, even if it is, say, 10 times slower than a normal method call.
- Most of the databases I would be competing with are heavyweight out-of-process monsters connected via incredibly inefficient IPC mechanisms.
- [And what about mutating methods? What does it mean, for instance, to call a mutator on a tuple which is part of a query, where the tuple in question might map to several base table tuples, or even to none at all?]
- The mutator gets forwarded to the object from the base table that provided the relevant attribute (or a copy of that object, depending on the update semantics of the view).
- [Does such a tuple even have an object identity in the usual sense?]
- Tuples are value types. The equality relation I would use is 'egal' from HenryBaker's EqualRightsForFunctionalObjects (this is definitely the right notion of equality for a functional/object system).
- I should clarify the last point. As is correctly pointed out earlier on this page, "the only mutable thing allowed in the RelationalModel are RelationalVariables". So when we "update a row" in a relational database, in terms of the RelationalModel we are setting the corresponding relvar to a completely new relation that differs only in that row. Now, suppose we embed the relational model in some "outer" model of computation. If that is a pure functional model, then (ignoring monadic I/O and similar techniques), variables can still only refer to relations, not relvars. I.e. a pure functional program can never observe a row being updated; it can only be given the "before" and "after" relations and observe that they are different. If the outer model is stateful, OTOH, then its variables can be relvars. This means that it is possible to construct a GeneralizedReference, that refers to "the tuple found by looking up a given key in the relation specified by a given relvar". Call such a GeneralizedReference a "tuplevar". A tuplevar has identity. Since tuplevars could be constructed even if we didn't allow direct references to the objects that implement tuples, there is no point in disallowing such references.
- "Object identity" is a misnomer. If two things have the same current state but observably distinct identities, that is because they are stateful, not because they are objects.
- [And what does the relational projection operation do to your "object tuples"? Slice them into partial objects?]
- Yes. It would use something similar to FacetPattern (but with the ability to rename attributes, and not with the same security motivation). Also it would be necessary to be able to combine attributes from different objects, like FacadePattern.
- [See earlier comments. You're talking about an explosion of newly generated classes.]
- Or one extra metaclass :-)
- [I'm not saying that these questions couldn't be answered -- but I suspect this only scratches the surface.]
- And I'm not claiming that there are not difficult issues to solve -- but I am confident that it is worth the effort to solve them.
- [As a first step, implementing what D&D suggest or something based on it would already be a huge step in the right direction, and would facilitate much better cooperation between these two models than currently exists. Perhaps your ideas can lead to a tractable model and implementation, but I remain skeptical.]
[Really, to discuss this further, it would be helpful if you identified the specific host language that you have in mind.]
Any reasonably dynamic language with some support for MetaProgramming would do; for example SmalltalkLanguage, RubyLanguage, PythonLanguage, CommonLisp, etc. (DynamicTyping isn't required, although it would make some things easier.)
[And what's not sensible about having database concepts built-in?]
If they are implemented in libraries, they can be more easily changed, or alternative implementations provided in application code. Things that are part of the language semantics are more difficult to change or extend. The raison d'�tre of object-oriented systems can be thought of as allowing functionality to be moved to libraries rather than having to be built-in to the language; that's certainly what they are most successful at. (Then again, maybe I just have a bias toward SmallLanguage?s.)
[Whether a library can provide convenient access to a full relational model really depends quite a bit on the host language. E.g. in a language like LISP, a library is a no-brainer; in C++, difficult but perhaps not impossible. I've been working on the latter, and have had to make many compromises, mainly in forfeiting static type checking. The comments above should give you a glimpse of the issues, but I can provide more detail if you like.]
I'll try to track down that C++-based system (I was borrowing someone else's copy of CUJ). It used templates to compute the result types of relational expressions.
Found it: http://lists.boost.org/MailArchives/boost/msg59731.php
[Thanks, sounds interesting. I considered this type of API in C++ myself, but my application programmers prefer a dynamically typed API, so it wasn't worth the bother of wrestling with the templates. See the somewhat dated NotesOnaCeePlusPlusRdbmsApi.]