HHVM/Hack 型システム その1

# 型システムを制するものはHackを制す
Hack and HHVM Advent Calendar 2018も中盤になってきましたので、
Hackの型システムに慣れるために、色々みていきます
紹介するものは全て`strict`前提です。

## プリミティブ
公式ドキュメントにもありますが、まずは基本の型からです。
PHPとは微妙に異なります。

### bool

boolは当然boolのみとなります。

```hack
<?hh // strict

namespace Acme\Hacklang;

class TypeSystem {

  public function is(): bool {
    return true;
  }

  public function is_is(bool $bool): bool {
    return $bool;
  }
}
```

### int / float

intとfloatを混ぜて使うことはできません。
下記は共にTypeErrorです。

```hack
  public function is(): int {
    return 1.0;
  }

  public function is_is(float $bool = 0.1): int {
    return $bool;
  }
```

### string

文字列も当然それ以外は利用できません。

### array

これまでも紹介してきた様にarrayはPHPと同様にarrayだけでは不十分となります。

文字列のarrayを宣言しておいてintとなるとErrorとなります。
下記のは全てTypeErrorです。

```hack
  public function failedStringVarray(): varray<string> {
    return [1];
  }

  public function failedStringStringDarray(): darray<string, string> {
    return ['hello' => 1];
  }

  public function failedStringArray(): array<string> {
    return [1];
  }

  public function failedStringStringArray(): array<string, string> {
    return ['hello' => 1];
  }

  public function failedNestedMixedArray(): array<mixed> {
    return [
      1,
      'testing',
      'hello' => 1234,
      'nested' => [
        1 => 2,
        'testing',
        'hello' => 1234,
      ]
    ];
  }

  public function failedMixedArray(): array<mixed, mixed> {
    return [
      1,
      'testing',
      'hello' => 1234,
      'nested' => [
        1 => 2,
        'testing',
        'hello' => 1234,
      ]
    ];
  }
```

### resource
PHPと同じです。

```hack
  public function is(): resource {
    return \fopen("ftp://user:password@example.com/somefile.txt", "w");;
  }
```

## 他
プリミティブ型以外で利用するものです。

### void
戻りがないことの宣言です。
ユニットテストの場合は基本的にvoidになるので、記述しなければいけません。

## 混合系
普通のものから特殊なものまで様々です。

### mixed
Hackで開発する上で一番厄介で、なるべく利用して欲しくないのがこのmixedです。
いろんな型に加え、nullとvoidが許容されます。
mixedが記述されると、様々な手法でTypecheckerに理解可能なコードに書き直す必要があります。

下記のコードを例にしましょう。

```hack
  public function mixedParameter(mixed $param): string {
    return $param;
  }
```

引数にmixedを使い、そのまま返却している例です。
これはこのままだとTypeErrorになるため処理が実行できません。
$paramが文字列かどうかを保証するコードに変更しなければいけません。

下記のコードは問題がないコードです。

```hack
  public function mixedParameter(mixed $param): string {
    if($param is string) {
      return $param;
    }
    return '';
  }
```

invariantを利用することもできます。
mixedは使えば使うほど分岐処理などが増えていきますので、
注意が必要です。

```hack
  public function mixedParameter(mixed $param): string {
    invariant($param is string, 'Type Error.');
    return $param;
  }
```

### nonnull
nullではないことを表すものです。
`?nonnull`にすると、mixedと同じ意味になります

```hack
  public function nullableNonnull(): nonnull {
    return 1234;
  }
```

### num
intとfloatが利用できる型です。
以下のは問題なく動作します。

```hack
  public function returnNum(): num {
    if (true) {
      return 1.0;
    }
    return 1234;
  }
```

### arraykey
intとstringが利用できる型です。

```hack
  public function returnKey(): arraykey {
    if (true) {
      return 'hello';
    }
    return 1234;
  }
```

### callable
PHPのものと意味は同じですが、記述方法が異なります。
HackではcallableだけではTypecheckerでOKになりません。
callableなものでも無名関数の引数・型、無名関数の戻りの宣言しなければなりません。
またPHPの場合は __invokeを実装したクラスも許容されていますが、
Hackの場合は __invokeが実装されていてもcallableにはなりません。

```hack
  public function returnCallable(): (function(int): int) {
    return function(int $int) {
      return 1;
    };
  }
```

### Tuple
多値をあらわすものです。
難しい型ではありません。

```hack
  public function returnTuple(): (int, string) {
    return tuple(1, 'testing');
  }
```

### Classname
ライブラリ開発などで多用するであろう型です。
className::classを返却したり引数に利用できるところで利用できます。
下記のはものはstdClassの例です。

```hack
  public function returnClassname(): classname<\stdClass> {
    return \stdClass::class;
  }
```

Genericsを利用することで、様々なクラス名を扱うこともできます。

```hack
  public function returnGenericsClass<T as classname<T>>(T $name): T {
    return $name;
  }
```

今回は型の紹介のみでした。