# Type Aliases: Opaque

An opaque type alias is created using `newtype`. Unlike with transparent type aliases, with care in organizing source code, the compiler can make sure that general-purpose code cannot access an opaque alias's underlying type directly.

## Aliases without Type Constraints

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.

Consider a file, `point.inc.php`, that contains an opaque alias definition for a 2D point type and a number of function primitives:

```<?hh

namespace Hack\UserDocumentation\TypeAliases\Opaque\Examples\AliasNoConstraint;

// point.php - Point implementation file

newtype Point = (int, int);

function createPoint(int \$x, int \$y): Point {
return tuple(\$x, \$y);
}

function setX(Point \$p, int \$x): Point {
\$p[0] = \$x;
return \$p;
}

function setY(Point \$p, int \$y): Point {
\$p[1] = \$y;
return \$p;
}

function getX(Point \$p): int {
return \$p[0];
}

function getY(Point \$p): int {
return \$p[1];
}
```

Only those functions that need to know `Point`'s underlying structure should be defined in the above `Point` implementation file. All general-purpose functions that support the `Point` type can reside in something like PointFunctions.php, as shown below:

```<?hh

namespace Hack\UserDocumentation\TypeAliases\Opaque\Examples\AliasNoConstraint;

// point-functions.php - Point's supporting functions

function distance_between_2_Points(Point \$p1, Point \$p2): float {
\$dx = getX(\$p1) - getX(\$p2);
\$dy = getY(\$p1) - getY(\$p2);
return sqrt(\$dx*\$dx + \$dy*\$dy);
}
```

Here then is some code that creates and uses some Points:

```<?hh

namespace Hack\UserDocumentation\TypeAliases\Opaque\Examples\AliasNoConstraint;

// test-point.php - User code that tests type Point

function run(): void {
\$p1 = createPoint(5, 3);
var_dump(\$p1);
\$p2 = createPoint(9, -5);
var_dump(\$p2);
\$dist = distance_between_2_Points(\$p1, \$p2);
echo "distance between points is " . \$dist ."\n";
// But we cannot pass a tuple of two ints since they are not a Point
// This will give a Hack typechecker error
\$will_not_type_check = distance_between_2_Points(tuple(2, 3), tuple(3, 4));
// However, the code will still run in HHVM
echo "distance between points is " . \$will_not_type_check ."\n";
}

run();

/*

Here is the type error for \$will_not_type_check

test-point.php:18:52,62: Invalid argument (Typing[4110])
point-functions.inc.php:9:36,40:
This is an object of type
Hack\UserDocumentation\TypeAliases\Opaque\Examples\AliasNoConstraint\Point
test-point.php:18:52,62: It is incompatible with a tuple
test-point.php:18:65,75: Invalid argument (Typing[4110])
point-functions.inc.php:9:47,51:
This is an object of type
Hack\UserDocumentation\TypeAliases\Opaque\Examples\AliasNoConstraint\Point
test-point.php:18:65,75: It is incompatible with a tuple

*/
```
Output
```array(2) {
[0]=>
int(5)
[1]=>
int(3)
}
array(2) {
[0]=>
int(9)
[1]=>
int(-5)
}
distance between points is 8.9442719099992
distance between points is 1.4142135623731
```

Being in the same file as the alias definition, function `createPoint` and friends have---and need---direct access to the integer fields in any Point's tuple. However, any other file does not.

## Aliases with Type Constraints

Consider a file that contains the following opaque type definition:

```<?hh
newtype Counter = int;
```

Any file that includes this file has no knowledge that a `Counter` is really an integer, so that 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:

```<?hh
newtype Counter as int = int;
```

The presence of the type constraint allows the opaque type to be treated as if it had the type specified by the 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. The following example would fail to typecheck for this reason:

```<?hh
// Assume this code is in a different file than where the Counter type is
// defined.
class A {
public Counter \$c;

public function __construct() {
// This is prohibited, as there is no implicit conversion from int
// (the type of 0) to Counter
\$this->c = 0;
}
}
```

A type constraint must be a subtype of the type being aliased.

In the example below, `Point` has a constraint of `(int, int)`; thus we can pass a `Point` to any method expecting a `(int, int)` ... but not vice-versa!

```<?hh

namespace Hack\UserDocumentation\TypeAliases\Opaque\Examples\AliasConstraint;

// point-constraint.inc.php - Point implementation file

newtype Point as (int, int) = (int, int);

function createPoint(int \$x, int \$y): Point {
return tuple(\$x, \$y);
}

function setX(Point \$p, int \$x): Point {
\$p[0] = \$x;
return \$p;
}

function setY(Point \$p, int \$y): Point {
\$p[1] = \$y;
return \$p;
}

function getX(Point \$p): int {
return \$p[0];
}

function getY(Point \$p): int {
return \$p[1];
}
```

The two examples above motivate several of the use cases for poking a hole in opaque type aliases like this.

In the `Counter` example, we may have extra restrictions on the value of a `Counter` and how it is maintained, and so need the opacity to ensure the proper invariants are respected. This means we can't let just any `int` be a `Counter`. But going the other way is just fine; doing math on a `Counter` makes sense.

For the `Point` example, it may look like we are largely breaking the abstraction of the `Point`, and in fact we are. You probably wouldn't want to write new code that looks like this. However, it can be extremely useful when converting existing, untyped code. We can introduce a new `Point` opaque alias, but with a type constraint for backwards compatibility. Any new code will use the `Point` type, and thus be subject to the `Point` abstraction and its invariants. Existing code can continue to work on an `(int, int)` tuple directly, if it needs to. But that code can't convert back to a `Point` without going through the abstraction again, so the abstraction cannot be broken. Once all code is converted over, the constraint on the alias can be removed, and it can be fully opaque.