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.typecan be assigned to one another, they are of the same type - the value
A.B.C.thisis 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.typemay have an actual type that is a sub-type ofA.B.C, so they cannot be assigned to a field of typeA.B.CunlessA.B.Cis areftype. - consequently, value
A.B.C.thiscannot be assigned to a field of typeA.B.CunlessA.B.Cis areftype.
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.