Fuzion Logo
flang.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, ideally, 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>(maxSize i32) is
    push(x T) is
      ...
    pop T is
      ...

  sp := stack<point> 300
It would be nicer to have a uniform syntax as in
  stack(T, maxSize i32) is
    push(x T) is
      ...
    pop T is
      ...

  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
    asString string is intrinsic
    byteSize is intrinsic

  AnyFeatureDefiningAType.type : Type is intrinsic

Then, we could declare a feature with type parameter as follows:

  stack(T Type, maxSize i32) is
    push(x T) is
      ...
    pop T is
      ...

  sp := stack point.type 300

Constraint type parameters

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, maxSize i32) is
    push(x T) is
      ...
    pop T is
      ...

  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 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 is
      redef infix ∙ (a, b T) T is a + b
      redef infix == (a, b T) bool is a == b
      redef e T is zero

  i32 : numeric i32 is
    redef type.zero => 0
    redef type.one  => 1

    # type.sum is inherited from numeric.type.

So, the type instances should duplicate the inheritance relation of their underlying features.

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 hasEquals could look something like this:

  hasEquals is
    type.equals(a,b hasEquals.this.type) bool is abstract
    infix = (b this.type) bool is
      this.type.equals hasEquals.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.