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 typeA.B.C.this.type
, so it can be assigned to a field of typeA.B.C.this.type
- values of type
A.B.C.this.type
may have an actual type that is a sub-type ofA.B.C
, so they cannot be assigned to a field of typeA.B.C
unlessA.B.C
is aref
type. - consequently, value
A.B.C.this
cannot be assigned to a field of typeA.B.C
unlessA.B.C
is aref
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
- Sukyoung Ryo: ThisType for Object-Oriented Languages: From Theory to Practice.
- Saito and Igarashi presented a paper Matching ThisType to Subtyping.