In the section Fuzion Choice Type, one of Fuzion's mechanisms to store data of different types has been presented. Choice types are ideal for situations in which the number of possible choices is known and fixed from the beginning, while new operations on that data might be added later with little effort. Any new operation has to provide code to handle all the choices that a type provides. The compiler will check if the code is complete.
However, in situations where the kind of operations on data are known and fixed, but the actual kind of data might change and new data might be added, the choice types provide little help.
One classic example is a graphical editor that supports an extensible set of graphical objects such as boxes, circles, text blocks, polygons, images, etc. A small set of operations must be performed on these objects. E.g., drawing onto the screen, scaling the object.
For this purpose, object-oriented techniques such as classes with inheritance and dynamic binding are ideal. However, these concepts require additional data for type information and additional code execution for call resolution. This is why many purely object-oriented languages suffer from a performance penalty.
Fuzion is a pure object oriented language in the sense that every feature defines a type that can be used for inheritance and dynamic binding. However, fuzion does not require type information or dynamic binding in cases that are not polymorphic. Furthermore, the compiler specializes code to avoid this overhead in many of the remaining cases.
drawable(x, y i32) abstract is draw(g graphics) abstract is move(dx, dy i32) is x := x + dx y := y + dy
circle(x, y, radius i32) : drawable x y is draw(g graphics) is g.draw_circle x y radius
square(x, y, side i32) : drawable x y is draw(g graphics) is g.drawRect x y x+side y+side
Fuzion supports multiple inheritance, such that different aspects of a
feature can be expressed via inheritance. An example is a numeric value, that
defines a total order and that can be converted to a string. Consequently, the
feature defining the numeric value should inherit from features like
The presence of multiple inheritance leads to several possible conflicts, that need to be resolved:
A conflicting inheritance occurs when a feature inherits from two different
parents features that happen to have the same name. Say a feature
boat has a feature
max_speed, while a
max_speed. If you wanted to define a feature
amphibious_vehicle you would get two different features
max_speed with different meanings.
Repeated inheritance happens when the same feature is inherited through different parents. This is no problem if the feature is not redefined on the way. The original feature will then appear only once in the heir.
However, if a feature is redefined on one or several of the inheritance paths, we end up with conflicting implementations that need to be resolved somehow.
Through inheritance, a feature could inherit repeatedly from a generic parent giving different actual generic arguments.
A conflict might occur between features that are not visible by the heir class. For example, a module might export a feature that redefines a feature that is visible only locally within the module. Repeated inheritance in another module could then result in conflicts that cannot be predicted by the developer of the other module.
A mechanism to rename features that are inherited helps to solve name
conflicts. If conflicting features can be renamed, using the example from above, we
could have two features
TBD: In case one feature
f defined in feature
inherited repeatedly through
C and renamed
f2 in the heir class
version should be called in a call
a.f with the target having
A? Possible options: explicit selecting one renamed
version or just using the first inheritance clause.
In case of conflicting implementations, the heir class could provide a new implementation that meets the requirements of both implementations that were inherited.
A simple solution for conflicting generics would be to just forbid repeated inheritance unless the generic arguments are equal.
Alternatively, generic classes with different actual generic arguments could be treated like completely different classes. Then, the repeated inheritance results in name conflicts that could be resolved by renaming.
One solution to avoid invisible conflicts would be to forbid inheriting from features and redefining inherited features that have a visibility that is more restrictive than the heir class' visibility. I.e., a feature exported from a module may only inherit from features that are also exported from the module and may only redefine features that are also exported.
One consequence this has is that inheritance cannot be hidden, inheritance is not an implementation detail. Consequently, inheritance should not be used as a means of implementation of a feature unless this implementation should be revealed to the public.
As an example, say you implement a module that provides basic data types like
stack based on an internal datatype
Since you do not want to expose the internal datatype,
not inherit from
contain a field of type
It should be possible to add inheritance relations to existing features from other modules as long as these new inheritance relations are invisible to that other module. This is similar to adding the implementation of a trait to a type in Rust.
This could result in name conflicts and repeated inheritance. Name conflicts can be solved by renaming within the heir module.
To solve repeated inheritance, redefining external features is not an option: apart from being very error-prone it would result in new conflicts when there are conflicting redefinitions in different modules. Instead, the only permissible way to solve repeated inheritance would be renaming of the feature that was added later.