Posted at

hhvm/hslからHackを知る


マニュアル以外から学ぶHack

今回のアドベントカレンダーで、すでに何回も登場している hhvm/hsl

これまでのHackを学ぶヒントが散りばめられているライブラリの一つです。

今回はライブラリ内の実装を細かくみていき、

PHPには実装されていない、Hackにしかない機能も合わせて解説していきます。


Dict\merge

これは、第一引数のdictやvec(Hack Arrays)に、

第二引数以降のHack Arraysを結合したdictを返却する関数です。

<?hh // strict

use namespace HH\Lib\Dict;

require __DIR__ .'/vendor/hh_autoload.php';

<<__Entrypoint>>
function main(): void {
$m = Dict\merge(
dict['PHP' => '7.3', 'HHVM' => '3.29.1'],
vec[1, 2, 3]
);
var_dump($m);
}

この関数の実装コードは以下の通りです。

function merge<Tk as arraykey, Tv>(

KeyedTraversable<Tk, Tv> $first,
KeyedTraversable<Tk, Tv> ...$rest
): dict<Tk, Tv> {
$result = dict($first);
foreach ($rest as $traversable) {
foreach ($traversable as $key => $value) {
$result[$key] = $value;
}
}
return $result;
}

KeyedTraversable はHack専用のインターフェースで、PHPには存在しないものです。

<<__Sealed(

ArrayIterator::class,
AsyncMysqlRowBlock::class,
DOMNamedNodeMap::class,
ImagickPixelIterator::class,
IntlBreakIterator::class,
KeyedIterable::class,
KeyedIterator::class,
MysqlRow::class,
\HH\Rx\KeyedTraversable::class
)>>
interface KeyedTraversable<+Tk, +Tv> extends Traversable<Tv> {}

sealedで指定されているもので見慣れないものは全てHack専用インターフェースになっています。

このインターフェースは

In addition to Hack collections, 

PHP arrays and anything that implement KeyedIterator are KeyedTraversable.

となっていますので、Hack Arrays以外に対しても利用できることがわかります。

下記の様に利用することもできるというのがわかります。

<<__Entrypoint>>

function main(): void {
$m = Dict\merge(
dict['PHP' => '7.3', 'HHVM' => '3.29.1'],
vec['shapes', 'enums', 'collections'],
[100 => 'xhp'],
ImmMap{
'hhvm/hsl' => ImmVector{
tuple("hhvm", "^3.29.0"),
tuple("hhvm/hhvm-autoload", "^1.4")
}
}
);
var_dump($m);
}


C\count

これはPHPでおなじみのcountと同じものですが、実は細部が異なります。

<<__Rx>>

function count<T>(
<<__MaybeMutable>> Container<T> $container,
): int {
return \count($container);
}

PHPではみることがない実装コードです。

Containerは、HH\Rx\Traversable を継承したインターフェースです。

PHPのTraversableと同様にforeachで利用できるもの、から拡張されたインターフェースです。

基本的に配列から派生したもの全てに利用できます。


<<__Sealed(KeyedContainer::class, ConstSet::class)>>
interface Container<+Tv> extends \HH\Rx\Traversable<Tv> {}

下記に紹介するものは、PHPとランタイムが異なるHackならではの機能(マニュアル非掲載)です。

多分。

というのもどれも詳細に言及されているものが存在していません。。

主にパフォーマンス面で作用するものと思われます。


__Rx

gitへのコミット内容から推測しやすいものですが、

このAttributesを利用することで、reactiveになる様です。

リアクティブプログラミングそのものについては下記のものをみるといいでしょう。

【翻訳】あなたが求めていたリアクティブプログラミング入門

実は__Rxに関して、Hackではいくつか種類があります。

__Rx、__RxLocal、__RxShallowの三種類があります。

ただどれもマニュアルには記載されていません。

利用する場合 __Rx指定するものは、

__Rxが記載されていない関数やメソッドが含まれる場合は利用できません。

また、副作用を与える処理に関して基本的に利用できません。

これらを扱う場合は、.hhconfigに必ず下記のものを記述してください。

unsafe_rx = false

Typechckerが利用可否の検査を行いますので、

不必要な記述を防ぐことができます。

HackのCollectionクラスにもこれらは記述されています。

  <<__Rx, __OnlyRxIfArgs>>

public function __construct(<<__MaybeMutable, __OnlyRxIfImpl(HH\Rx\KeyedTraversable::class)>> ?KeyedTraversable<Tk, Tv> $it);


__MaybeMutable

これは引数にAttributes と記述されていますが、

引数以外にメソッドにも利用ができる様です。

というのもこのAttributeに関して明確に記述されたものが存在していません。

(hhvmの実装コードにはある)

利用するにはRxが指定されていることが条件の様です。


Dict\flatten

Collectionなどを平坦な一次元化するものです。

<<__Rx, __AtMostRxAsArgs>>

function flatten<Tk as arraykey, Tv>(
<<__OnlyRxIfImpl(\HH\Rx\Traversable::class)>>
Traversable<\HH\Rx\KeyedTraversable<Tk, Tv>> $traversables,
): dict<Tk, Tv> {
$result = dict[];
foreach ($traversables as $traversable) {
foreach ($traversable as $key => $value) {
$result[$key] = $value;
}
}
return $result;
}

この関数にも見慣れないAttributesが記述されています。

これらは基本的にRxに関するものとなります。

通常利用時に意識することはありませんが、

どういう実装になっているか、

を知ることはHackを使いこなすための近道になるのではないでしょうか。