Carmot ODL (6) – inheritance
This post is number 6 in a sequence of 7. Click here to get to the beginning.
The ‘:’ symbol – type inheritance
Many of the goals of the Carmot language have parallels in intent, if not in methodology, with those of object oriented programming (OOP), one such similarity is the motive for supporting type inheritance. Despite this, Carmot deliberately restricts the inheritance to fields (and associated scripts etc.), whereas OOP extends inheritance to all aspects of a class including the code that implements class methods. The full reasoning behind rejecting OOP in Mitopia® is beyond the scope of this post, but boiled right down, the conflict is because OOP’s approach of wrapping both the data and the code in a class abstraction is completely incompatible with the needs of distributed data flow, since code state cannot be passed using the laws of data flow, but must instead be handled in a single ‘process’. For this and other reasons, OOP languages were found to be wholly unsuitable for use within Mitopia® (despite numerous early attempts – see here). In the discussion of inheritance that follows, it is important to distinguish the concept of ‘inheritance’ from OOP languages which are simply one model for implementing inheritance.
As with Object Oriented Database Management Systems (OODBMS), the inability of the relational model to handle complex data records as a single unit (in an RDBMS a record must be split into many essentially disconnected tables) for the purposes of access and modification, coupled with the decision to go with an ontological breakdown of data into meaningful categories for understanding the world, immediately leads to the conclusion that records of types that are descendent from another type should inherit all the fields of the ancestral type. Our preceding posts regarding the ‘##‘ and ‘@@‘ Carmot references should make clear that this restriction does not exist within Mitopia’s Congitive data model. Nonetheless, Carmot does of course support type inheritance which can be created in one of three ways. The most obvious is by use of the ‘:’ operator directly in-line in a full structure definition to declare the type on the left of the ‘:’ operator as a descendent type of that on the right. The declaration of ‘Organization’ in the listing below is a typical example:
The same syntax can be used with an anonymous or forward structure definition as in the second set of declarations in the listing including the second declaration for ‘Organization’. As before, the ancestral type follows the descendant type in the declaration, separated by the ‘:’ symbol.
Note that the C language itself allows typedef names to be used in later type declarations to declare what are effectively synonyms for the original type name. In C these statements do not introduce new types. Examples are as follows:
typedef struct S
typedef struct Ss1Type;
In this example, s1Type and s2Type can be used interchangeably with the type S to refer to the same type. This can be quite convenient in C in order to make the code more readable, and often the programmer in his mind may think of the synonyms as if they are in effect descendant types of the original. In fact this is not true, it is impossible in C to create descendant types, but it is still a useful model for code documentation purposes. The Carmot language interprets this C syntax differently, and actually does create a descendant type in an identical manner as if the ‘:’ operator had been used.
Examples of this syntax are given in the last section of declarations in the listing above. Be aware that by using the C syntax without the ‘:’ operator, the order of the parent and the descendant type are reversed, that is, the parent type is the first to occur in the declaration while the child is the second.
Unlike C++, Carmot does not allow multiple inheritance, that is, in Carmot any given type can be a direct descendant of only one parent type (Carmot does not use the word class as it smacks of OOP which would be disingenuous). This not only greatly simplifies logic associated with determining the binary layout of all fields of a type, inherited (directly and indirectly) or locally declared, but also avoids all the nasty pitfalls and complexity that OOP class multiple inheritance can lead to. Because Carmot provides extremely rich ways to interrelate various different record types through persistent and collection references, it is not limited in the same manner that OOP languages are, to forcing all aspects of an object into that object itself. Instead in Carmot, if something is both a populated place and an organization say (e.g., a city), then one creates a mutually cross referencing pair of records, one descendent from ‘Stage’, the other descendent from ‘Organization’ so that each record belongs in its proper place in the ontological hierarchy, and can be treated either alone or in conjunction with any other aspect of the thing involved. The intimate connection between the two records is explicitly declared through the cross referencing and echo fields involved, and thus the Mitopia® environment handles everything necessary to ensure that the two aspects of the city don’t get separated from one another. In OOP everything must be hidden behind the object wrapper and so this technique is not possible.
This flexibility and expressive power not only greatly improves understanding and use of Carmot based data, but also through the use of persistent data references, it greatly simplifies Carmot implementation and the practicality of passing data around unambiguously across data flows. This is one of the most fundamental and irreconcilable divergences in philosophy between Carmot and OOP. The other is in Carmot’s use of run-time discovered types for all data access, so that no compiled code contains any types that have anything to do with the target application. This too is a clear violation of OOP principles. Carmot relies on the Mitopia® run-time environment and memory model abstraction to provide data hiding and prevent illegal access, OOP relies on the compiler.
One advantage of the Carmot approach is vastly improved performance, since the substrate itself can implement caching and other discovered type specific techniques, even though client layer code cannot bypass the abstraction layer. In the OOP approach, one must always get through the object’s wrapper layer to get anything done (the same applies to OODBMS systems). Worse yet, most of what is an object and its behaviors, is hidden from the substrate in the code of the methods (inherited or otherwise) of the class, making it unsafe or impossible to ‘mess’ with the object or its data in any optimization.
This prevents many of the most powerful optimization techniques from being used in the object model. Later posts on persistence and queries will discuss this in detail. Another clear advantage of the Carmot approach is adaptability (see the Bermuda Triangle discussion) since unlike OOP, virtually no compiled code is dependent on any persistent type. This is because inheritance is limited to the data, types and hierarchies are discovered at run-time, and type inheritance is handled automatically by the substrate.
The Carmot implementation of type inheritance is conceptually quite simple because any inheritance of behaviors occurs automatically at run time since all client code must go through the various Carmot abstraction APIs. The inheritance of type fields from ancestral types is also quite simple, since multiple inheritance is forbidden, all that is necessary is to append any locally declared fields after the sum of all inherited fields. In C++ this issue must be addressed using virtual inheritance. The illustration to the right shows the arrangement of the fields within a record of type ‘Airline’ which clearly shows the inheritance chain from Datum which is the root type of all persistent data.
In addition to field inheritance, Carmot provides inheritance of scripts and annotations, and here the rules of inheritance are considerably more complex and flexible. This type of inheritance is discussed more fully elsewhere.
In an earlier post on Tagged Unions (see here), we already briefly discussed Carmot inheritance behaviors for enumerated types.
Click here for the last post in this sequence. Click here for the previous post.