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 anotherSchema
.final
- This allows declaring a schema as a final type, meaning it cannot be extended by anotherSchema
. 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
.
schema:
name: Square
package: aac.shapes
extends:
- name: Rectangle
package: aac.shapes
modifiers:
- final
fields:
- name: length
type: number
requirements:
- "SQR-1"
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
.
Required Fields¶
If a definition is extending another definition with required fields, then the is_required
property of the fields will not carry over.
schema:
name: AacType
package: aac.lang
modifiers:
- abstract
description: |
The base type for any data item defined in AaC.
fields:
- name: name
type: string
description: |
The name of the type.
is_required: true
- name: package
type: string
description: |
The 'dot notation' package name for the type. All type names must be unique within an assigned type.
The package will also define the directory structure produced by gen-plugin.
is_required: true
- name: description
type: string
description: |
A brief description of the type.
is_required: true
schema:
name: Schema
extends:
- package: aac.lang
name: AacType
package: aac.lang
root: schema
description: |
A definition that defines the schema/structure of data.
fields:
- name: extends
type: SchemaExtension[]
description: |
A list of Schema definition names that this definition inherits from.
- name: modifiers
type: dataref(modifier.name)[]
description:
A means of further defining the schema and how it can be used within the model.
- name: root
type: string
description: |
The root key to use when declaring an instance of the type in yaml/aac files.
- name: fields
type: Field[]
description: |
A list of fields that make up the definition.
is_required: true
- name: requirements
type: dataref(req.id)[]
description: |
A list of requirements associated with this schema.
- name: constraints
type: SchemaConstraintAssignment[]
In the above example, Schema
is an extension of AaCType
. So while a Schema
definition can use the name
, description
, and package
fields from AaCType
, the is_required
field does not carry over. If you want an extension’s field to be required, you will have to redefine the field.