Type Instances
The goal of type instances is not to permit Java-style reflection, but instead to unify the handling of parametric types and normal arguments. Special syntax for type parameters (generics) should be avoided and types should be treated as one form of feature similar to the way that lambdas or choice types are features as well.
Type parameters currently use a special syntax in feature declarations and in feature calls, similar to generics in Java:
stack(T type, max_size i32) is push(x T) is ... pop T => ... sp := stack point 300It would be nicer to have a uniform syntax as in
stack(T, max_size i32) is push(x T) is ... pop T => ... sp := stack point 300
Type instances
Every feature that defines a type should declare an implicit inner
feature type
that returns a unit-type value with information on its
type. The result is a subclass of Type:
Type ref is as_string String => intrinsic byte_size is intrinsic any_feature_defining_a_type.type : Type is intrinsic
Then, we could declare a feature with type parameter as follows:
stack(T Type, max_size i32) is push(x T) is ... pop T => ... sp := stack point.type 300
Syntactic Sugar
In argument field declarations, a fully capitalized name could be used to
implicitly assume Type
as its type.
When passing an argument to a type parameter, the .type
could be added automatically.
So we get
stack(T, max_size i32) is push(x T) is ... pop T => ... sp := stack point 300
Inner features of types
Defining Inner Features
Types could provide an environment to define global features associated to a
type but not requiring an instance such as special values
like i32.type.one
or monoids like String.type.concat
.
i32.type.one => 1 String.type.concat : Monoid String is redef infix ∙ (a, b String) => a + b redef infix = (a, b String) => a = b redef e => ""
Abstract Inner Features
For this to be most useful, types of (abstract) features such
as numeric
should be able to define features that can be inherited
and redefined by concrete heirs:
numeric(T numeric T) is type.zero is abstract type.one is abstract type.sum : Monoid T is redef infix ∙ (a, b T) T => a + b redef infix = (a, b T) bool => a = b redef e T => zero i32 : numeric i32 is redef type.zero => 0 redef type.one => 1 # type.sum is inherited from numeric.type.
The type instances should duplicate the inheritance relation of their underlying features.
this.type in type instances
In the previous example, feature numeric
has a type
parameter T
that is used in numeric.type
. This type
parameter should be replaced by this.type, so
this should work for type parameters as well:
numeric is type.zero is abstract type.one is abstract type.sum : Monoid numeric.this.type is redef infix ∙ (a, b numeric.this.type) numeric.this.type => a + b redef infix = (a, b numeric.this.type) bool => a = b redef e T => zero i32 : numeric i32 is redef type.zero => 0 redef type.one => 1 # type.sum is inherited from numeric.type.
Inner Fields
Inner fields of types would provide a mechanism similar to static fields in Java, with all the horror that static fields cause: When and in what order should they be initialized, what happens if they are modified, how are race conditions avoided, etc...
On the other hand, inner fields in types could define, e.g., default instances such as 'zero' and avoid overhead of re-creating these instances for every use. But then, default instances are typically cheap to create and if they were expensive, we need a means for memoization anyways to reuse results of expensive calls, so that could be applied to inner features of types as well.
For now, it seems reasonable to forbid fields as inner features for types and make sure type instances are unit values.
Type instances and equality checks
Defining a comparator operation in a type instance might solve the problem of different equality operations along the inheritance chain discussed in the section on Equality.
The definition of has_equals
could look something like this:
has_equals is type.equals(a,b has_equals.this.type) bool => abstract infix = (b this.type) bool => this.type.equals has_equals.this b
Type instances and monads
Without type instances, Fuzion has problems implementing operations that
do not require an instance of a particular type. An example is the
function monad.return
that wraps a value of the type parameter by the
monad, so there is no instance of the monad involved in this operation.
monad.return
should really be monad.type.return
, i.e., it is
not applied to an instance of monad, but to the unit
type monad.type.