AaC Inheritance

Version 0.4.0 changed how inheritance works in AaC. AaC now provides a more “standard” inheritance construct within the schema data declaration using the extends field. By “standard”, we mean you can extend your schema in the same way you would extend a Python class since AaC uses Python under the hood.

In short, you can extend schemas using an object-oriented mindset. That said, this only applies to schema. While it is technically possible to extend an Enum in Python, AaC does not support enum extension. This may be revisited in the future, but initial attempts to support Enum extension in the underlying Python code generation did not work. And of course, extending primitives is not a thing, but you can define new primitives.

Modeling AaC Inheritance

Let’s take a look at how AaC supports Schema inheritance.

Example AaC Schema extension:

schema:
    name: Shape
    package: aac.shapes
    modifiers:
        - abstract
    fields:
        - name: area
          type: number
        - name: perimeter
          type: number

We’ve defined a generic Shape schema above. All shapes have a perimeter and area value, so the generic Shape schema also has a perimeter and area field. Shapes which inherit from Shape will also inherit these fields. Below, we will define other schemas which inherit from Shape:

schema:
    name: Triangle
    package: aac.shapes
    extends:
        - name: Shape
          package: aac.shapes
    fields:
        - name: side_one_length
          type: number
        - name: side_two_length
          type: number
        - name: side_three_length
          type: number
        - name: height
          type: number
---
schema:
    name: Circle
    package: aac.shapes
    extends:
        - name: Shape
          package: aac.shapes
    fields:
        - name: radius
          type: number
---
schema:
    name: Rectangle
    package: aac.shapes
    extends:
        - name: Shape
          package: aac.shapes
    fields:
        - name: length
          type: number
        - name: height
          type: number

Each of the above schemas have their own properties that help distinguish them as their specific shape. But they all require a perimeter and area field as a valid shape, and so inherit from Shape.

Inheritance Modifiers

Just as in most programming languages, the AaC language provides some basic modifiers to control what you can and can’t do with inherited schema definitions.

  • abstract - This attribute allows declaring a schema as an abstract type. This means it cannot be “instantiated” directly in a definition. In AaC, this means the schema is either a root type or field in another Schema.

  • final - This allows declaring a schema as a final type, meaning it cannot be extended by another Schema. In AaC this means the final schema cannot appear in another schema’s extends field.

To use these inheritance modifiers you simply add them to the modifiers field of the schema. Let’s take another look at the above example to see how it works:

schema:
    name: Shape
    package: aac.shapes
    modifiers:
        - abstract
    fields:
        - name: area
          type: number
        - name: perimeter
          type: number

As you can see, the Shape schema has an abstract modifier. This means that Shape can no longer be instantiated on its own. To make a shape, you must create or use a schema which inherits from Shape.

    name: Square
    package: aac.shapes
    extends:
        - name: Rectangle
          package: aac.shapes
    modifiers:
        - final
    fields:
        - name: length
          type: number
    requirements:
        - "SQR-1"
---
req:

The above Square schema inherits from Rectangle as well via inheriting from Rectangle, but it has a final modifier. This means that no other schema can inherit from Square.

Using Inheritance

Now that we can define inheritance, how can we use it. The intended use case is to allow polymorphism in your field declaration. Let’s look at another example to explain.

schema:
    name: Pattern
    package: aac.shapes
    fields:
        - name: name
          type: string
        - name: shapes
          type: typeref(aac.shapes.Shape)[]

The above pattern schema has a shapes field which takes in a number of shapes to create a pattern with. Since we cannot instantiate the Shape schema on its own, we must use a schema which extends Shape. Since the field takes in any Shape, however, we can use any schema which extends Shape.