Types: Type Aliases

We can create an alias name for a type, and it is common to do so for non-trivial tuple and shape types. Once such a type alias has been defined, that alias can be used in almost all contexts in which a type specifier is permitted. Any given type can have multiple aliases, and a type alias can itself have aliases.

Quickstart

A type alias can be created in two ways: using type and newtype.

type Complex = shape('real' => float, 'imag' => float);
newtype Point = (float, float);

A type alias can include Generics as parameters.

Using type

An alias created using type (such as Complex above) is a transparent type alias. For a given type, that type and all transparent aliases to that type are all the same type and can be freely interchanged. type declarations are top-level declarations.

Using newtype

An alias created using newtype (such as Point above) is an opaque type alias. In the absence of a type-constraint (see Counter example below), each opaque alias type is distinct from its underlying type and from any other types aliasing it or its underlying type. Only source code in the file that contains the definition of the opaque type alias is allowed access to the underlying implementation. As such, opaque type aliasing is an abstraction mechanism. Consider the following file, which contains an opaque alias definition for a tuple that mimics a point:

newtype Point = (float, float);

function create_Point(float $x, float $y): Point {
  return tuple($x, $y);
}

function distance(Point $p1, Point $p2): float {
  $dx = $p1[0] - $p2[0];
  $dy = $p1[1] - $p2[1];
  return sqrt($dx*$dx + $dy*$dy);
}

Choosing between type and newtype

Looking at the earlier example, being in the same source file as the alias definition, the functions create_Point and distance have direct access to the float fields in any Point's tuple. However, other files will not have this same access.

Similarly, if a source file defines the following opaque alias...

newtype Widget = int;

...any file that includes this file has no knowledge that a Widget is really an integer, so that the including file cannot perform any integer-like operations on a Widget.

Consider a file that contains the following opaque type definition:

newtype Counter = int;

Any file that includes this file has no knowledge that a Counter is really an integer, meaning the including file cannot perform any integer-like operations on that type. This is a major limitation, as the supposedly well-chosen name for the abstract type, Counter, suggests that its value could increase and/or decrease. We can fix this by adding a type constraint to the alias's definition, as follows:

newtype Counter as int = int;

The presence of the type constraint as int allows the opaque type to be treated as if it had the type specified by that type constraint, which removes some of the alias' opaqueness. Although the presence of a constraint allows the alias type to be converted implicitly to the constraint type, no conversion is defined in the opposite direction. In this example, this means that a Counter may be implicitly converted into an int, but not the other way around.

Consider the following:

class C {
  const type T2 as arraykey = int;
  // ...
}

Here, we have a class-specific type constant that is an alias, which allows a value of type T2 to be used in any context an arraykey is expected. After all, any int value is also an arraykey value.

Was This Page Useful?
Thank You!
Thank You! If you'd like to share more feedback, please file an issue.