Inheritance Tutorial

 

The interface is the service contract of structure with the external world. We have seen the property flow types and external access permission key words, which are basic terms that can be used to draft such a contract.

The structure interface declares what each individual structure type does. Though when the number of different structure type grows, a large collection of unrelated structure interfaces are still difficult to remember and manage. And that is where the Luban structure interface inheritance comes to help.

Similar to the class inheritance concept in Object-Oriented programming, we want to take the advantage of commonalities among different components by categorizing them using inheritance. The idea is that it is easier to understand two animal types than to understand two hundred individual animals. The below example shows how to code inheritance in Luban:

 

            namespace demo;

 

            struct DualOp

            (

                        input:

                                    op1,op2;

                        output:

                                    result;

            )

            as process

            {

                        output.result = error(“No actually operation defined”);

            }

 

            struct Adder implements ::DualOp

            (

            )

            as process

            {

                        output.result = input.op1 + input.op2;

            }

 

The above Luban code defines two structures. Structure “DualOp” is a general interface for two operand operations. Structure “Adder” inherits its interface from “DualOp” and implements the internal to add two inputs together and set to output.

You may notice the syntax “::DualOp”, which is a short name for “demo::DualOp”. All Luban’s structure type names are full name. The short name can be used if the name space of the referred Luban type is the same as the name space of the caller component.

From this example, we can see that “Adder” inherits all input and output properties from “DualOp” and does not define any new property of its own. And we need to mention here that ONLY input and output property can be inherited. The store and static property can NOT be inherited. The reason for this design is to make interface inheritance simple.

 

1           Luban Inheritance Is Only For Interface

Unlike the general Object-Oriented languages like Java and C++, Luban’s inheritance mechanism is only for the interface. When one Luban structure inherits from another, only the interface of the parent structure is taken, and that excludes store and static properties. If you wish to use the internal implementation of the parent structure, you need to express you intention explicitly, as below example shows:

 

            // this example uses the Adder structure defined in previous example

            namespace demo;

 

            struct AdderWithUseCount implements ::Adder

            (

                        static usecount=0;

            )

            as process

            {

                        static.usecount++;

                        output = ::Adder(input=::Adder.input).output;

            }

 

The above example defines a structure “AdderWithUseCount” that works the same as the structure “Adder”, only with one more static usage counter property to count the number of evaluations.

The magic in this example is the last line of the structure process code. This line simply says: take the portion of input inherited from “Adder” structure, feed to a call to structure “Adder”, then take the output of this call and set to the output of structure “AdderWithUseCount”.

What actually happens in this example is very much like AdderWithUseCount inherits both interface and implementation from Adder. Though the taking of  Adder” structure’s implementation is explicitly stated in one line of code. The purpose of this design is to avoid the tricky ambiguity issue for inheritance by allowing programmer to state intention explicitly. Luban allows multiple inheritance of structure interfaces.

 

2           Abstract Structure

Similar to interface concept of Java and pure virtual class of C++, Luban has abstract structure to represent pure structure interface. Abstract structure can be used for inheritance and type checking. We can redo our “Adder” example using abstract structure like below.

            namespace demo;

 

            abstract struct DualOp

            (

                        input:

                                    op1,op2;

                        output:

                                    result;

            );

 

            struct Adder implements ::DualOp

            (

            )

            as process

            {

                        output.result = input.op1 + input.op2;

            }

 

In above sample code, we define an abstract structure named demo::DualOp and demo::Adder inherits its interface and implements actual operation. The inheritance relationship can be checked with type checking expression, like below Luban code shows:

 

            adder = demo::Adder();

            test = adder isa demo::DualOp;

 

The value of the variable test in above example is Boolean value true. The details of type checking expression can be found in next chapter.

 

Abstract structure can only be used for inheritance and type checking. Construction of abstract structure not allowed. For example, below Luban expression results in error value.

 

            errvalue = demo::DualOp();

 

3           No Cyclic Inheritance

Cyclic inheritance is not allowed in Luban. Not only direct cyclic inheritance among structure interfaces is not allowed. All structure type names used inside structure interface definition including names used in property data type definition and property value initialization expression are considered part of its dependent symbols. Luban requires all its dependent symbols (interface ) resolved before the structure interface can be resolved itself. Thus cyclic reference among dependent symbols will also result in error. For example, below Luban structure definitions are invalid because of cyclic reference of structure type symbols.

 

            namespace demo;

           

            structure TweedleDee

            (

                        input:

                                    x = ::TweedleDum();

                        output:

                                    y;

            )

            as process

            {

                        output.y = 1.0;

            }

 

            structure TweedleDum

            (

                        input:

                                    a = ::TweedleDee();

                        output:

                                    b;

            )

            as process

            {

                        output.b = 1.0;

            }

 

The above two structure “TweedleDee” and “TweedleDum”, though they don’t directly inherit from each other, they refer to each other in their property initialization expressions. And that is also cyclic interface definition and it is invalid.

 

4           Inheritance and Composition

Composition can be used together with Luban interface inheritance to create new structure based on old one, like below example shows:

 

            namespace demo;

 

struct AdderPlus implements TwoAdder

(

            output diff;

)

as composition

{

            output: demo::TwoAdder(input=demo::TwoAdder.input);

            output.diff: input.op2 – input.op1;

}

 

In above example, the structure “AdderPlus” takes the interface of “TwoAdder”. It also put one instance of “TwoAdder” into its composition and wired the right portion of its input to the “TwoAdder” and wired the output of “TwoAdder” directly to its output. So “AdderPlus” basically get interface and implementation of “TwoAdder” in this way. It also adds one extra output of its own, which is the difference of its two input properties. The first line in the composition can only be used with structure inheriting interface from other structure. Luban will do type checking to make sure the wiring work with inheritance. There could be ambiguity in the case of multiple inheritance. The basic rule to handle ambiguity is to explicitly specify the wiring of the output property on which the ambiguity occurs.