Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

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 300
It 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.

last changed: 2024-06-28