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

this.type

Definition

ThisType is also referred to as SelfType, in Eiffel it is written like Current.

The purpose of ThisType is to allow co-variant argument and result type changes for operations such as numeric.infix +(a numeric) in heir features such as i32, where this should be i32.infix +(a i32).

Motivation

Often, features need to work with instances of the same type as the current instance, even if we are in an heir feature. An example is infix + defined in an abstract feature numeric whose argument must match the type of numeric.this. For heir i32, the argument must be of type i32, while for complex f64, the argument must also be of type complex f64.

like this in Eiffel

Eiffel provides a type like anchor that is essentially syntax sugar for redefinitions of features with changes in their result types, the redefinition of the anchor automatically redefines the type of all features using type like anchor.

A special case in Eiffel is the anchor Current that corresponds to this in Java or A.B.C.this in Fuzion. An anchored type like Current in an Eiffel feature F the type of the current instance, which is just F unless the current instance is of an heir G of F, where it is G.

The example given by Bertrand Meyer in Object-Oriented Software Construction, 2nd edition is as follows:

class POINT feature

  x, y: REAL

conjugate: like Current is
  do
    Result := clone (Current)
    Result.move(0, -2*y)
  end

end

In a descendant of POINT, conjugate will automatically be changed to result in an instance of the descendant type. E.g.,

class PARTICLE
inherit POINT
feature
  ...
end

will automatically redefine conjugate to result in an instance of PARTICLE.

Workaround using parametric types

As a first solution, Fuzion uses constrained type parameters to mimic ThisType. As an example. feature numeric is declared generically as numeric(T type: numeric T), and the type argument T is carried through heirs integer(T type: integer T) : numeric T, wrapping_integer(T type : wrapping_integer T) : integer T and i32(val i32) : wrapping_integer i32. This works, but is cumbersome, in particular when the inheritance graph becomes more complex (e.g. i32 also inherits from has_interval, which uses the same mechanism to mimic ThisType).

Type parameters as anchors in Fuzion

Imagine we want to declare an abstract feature numeric that provides operations such as infix + to add numeric values. Concrete instances of numeric could be 32-bit unsigned integers, 64-bit floats, complex numbers, or user defined features that provide the required operations.

What should the argument type of infix + be in this case? It does not make much sense in an expression a + b where a is of type i32 to permit arbitrary numeric values, it makes most sense to accept only values of the same heir of numeric, which is i32 in this case.

One way to achieve this is to give numeric a type parameter T that is set to the actual type by heirs of numeric and then declare operations as follows

numeric(T type : numeric T) is

  ...

  infix +(b T) T => abstract

and give a concrete type for T in a concrete heir for numeric:

i32(val i32) : numeric i32 is

  ...

  infix +(b i32) i32 =>
    addi32_intrinsic i32.this b

This works, but is a little ugly.

this.type in Fuzion

A Fuzion variant of Eiffel's like Current could be the expression A.B.C.this.type. Since there might be several outer features, we need to qualify which outer instance we refer to. Applied to the numeric example above, the code could then become:

numeric is

  ...

  infix +(b numeric.this.type) numeric.this.type => abstract

with heirs able to provide concrete types:

i32(val i32) : numeric is

  ...

  infix +(b i32) i32 =>
    addi32_intrinsic i32.this b

Rules for assignments this.type that must be respected

  • values of type A.B.C.this.type can be assigned to one another, they are of the same type
  • the value A.B.C.this is of type A.B.C.this.type, so it can be assigned to a field of type A.B.C.this.type
  • values of type A.B.C.this.type may have an actual type that is a sub-type of A.B.C, so they cannot be assigned to a field of type A.B.C unless A.B.C is a ref type.
  • consequently, value A.B.C.this cannot be assigned to a field of type A.B.C unless A.B.C is a ref type.

Rules for redefinitions using this.type that must be respected

A redefinition using A.B.C.this.type in feature A.B.C1 must replace the type accordingly by A.B.C1.this.type. E.g., an heir to numeric might be integer as follows:

integer : numeric is

  ...

  redef infix +(b integer.this.type) integer.this.type => abstract

Alternatively, an heir might provide a concrete implementations such as i32 that replaces numeric.this.type by i32:

  infix +(b i32) i32 =>
    addi32_intrinsic i32.this b

However, this redefinition no longer provides a valid implementation of infix + in any heir of i32. Here is an outline of a possible heir mod5 that provides module-5 arithmetic based on i32:

mod5(redef val i32) : i32 val
pre
  0 <= val < 5
is

  ...

  infix +(b mod5) mod5 =>
    mod5 (val + b.val) % 5

Literature

last changed: 2024-06-28