type holyshared = Engineer<mixed>

技術的なことなど色々

Wescoのブーツが届きました

f:id:holyshared:20190819120329j:plain
Wesco Custom 38LTT

3月7日にオーダーしていたWescoのブーツが最近届きました。
当初の8月末くらいという話だったのですが、8月17日にオーダーしたお店から午前中に連絡をいただいて、その日に受け取り行きました。(オーダーから約5ヶ月)

当初はオーダーするつもりはなかったのですが、Wesco100周年記念の3rd Anniversary Modelだったので、オーダーしました。

オーダー時は店舗でサイズを測ってもらい、レギュラートゥの38RTか、レーストゥトゥの38LTTを選ぶ感じでした。

フィット感がいい方が好きなので、レーストゥトゥの38LTTを選びました。

f:id:holyshared:20190819120343j:plain
入っていた物

パッケージには、メンテナンス用のクロスや、オイル、ウールのキャップ、フォルスタン、ブーツ用の袋が入っていました。 しかも、ケース付きです。

また、ギャランティカードがあるので、下記の場合は修理費用がお安くなります。

  1. 1回目のヒール交換が無料
  2. 1回目のオールソールは¥5,000円程度 (通常は¥16,000円程度)

ワークブーツは大体頑丈なので、よっぽど酷使しなければ修理はそんなにする必要はないので、使うとしても大分先になりそうです。

f:id:holyshared:20190819120443j:plain
箱の正面

履き方はフォルスタンなしで、シューレスはレザーのものを使用しています。 履くときにレザーのシューレスがフックにかけにくいのですが、Rootsで慣れているので、特に気にならなかったです。

一回試し履きした感じは、NicksのHot Shotより軽くていい感じです、足馴染みも早そうです。

これからどんどん履いて行きたいです。

HackでAttributeを表現するためのインターフェース

HHVM3.29より、Attributeを表現するインターフェースが追加されました。
今までも、Attribute自体は使用できていましたが、以下の問題がありました。

  • タイプチェックがかからない。
    • Attributeに依存する処理でバグがあると原因を特定しにくい。
  • ユーザー独自のAttributeの仕様をコードで表現できない。
    • 引数は2つなのか、型はstringなのか表現できないので、ドキュメントなどにまとめる必要があった。

この問題をHHVM3.29で組み込まれた、インターフェースで解決できるようになりました。
.hhconfiguser_attributes= を設定として追加した後に、下記のインターフェースを実装するだけで、Attributeが利用できる部分の指定、インターフェースを実装したクラスでのタイプチェックがかかります。

  • ClassAttribute - クラス
  • EnumAttribute - 列挙型
  • TypeAliasAttribute - 型の別名
  • FunctionAttribute - 関数
  • MethodAttribute - メソッド
  • InstancePropertyAttribute - インスタンスプロパティ
  • StaticPropertyAttribute - 静的プロパティ
  • ParameterAttribute - 引数

Attribute仕様の表現

例えば、クラスとメソッドに指定できるAttributeを表現したい場合、下記のようにインターフェースを指定します。

\HH\ClassAttribute, \HH\MethodAttribute をクラスに実装することで、クラスとメソッドに指定できる部分を制限できます。

<?hh //strict

namespace TypedUserAttributes;

final class Version implements \HH\EnumAttribute, \HH\ClassAttribute, \HH\MethodAttribute, \HH\FunctionAttribute {
  public function __construct(
    private float $version
  ) {
  }

  public function __toString() : string {
    return (string) $this->version;
  }
}

また、指定する場所を間違えたり、パラメータの型を間違えた場合、タイプチェックに引っかかることがわかります。

f:id:holyshared:20181223224854p:plain

Attributeの取得

Attributeの情報を取得する場合は、リフレクションを使用します。
メソッドとして、getAttributeClass が提供されているので、Attributeのクラス名を引数に渡して、Attributeのインスタンスを取得できます。

<?hh //strict

namespace TypedUserAttributes;

require_once 'Post.hh';
require_once 'Version.hh';

use \ReflectionClass;

<<__EntryPoint>>
function main(): noreturn {
  $class = new ReflectionClass(\TypedUserAttributes\Post::class);

  $classVersion = $class->getAttributeClass(\TypedUserAttributes\Version::class);
  \printf("class version: %s\n", $classVersion);

  $methodVersion = $class->getMethod('getTitle')->getAttributeClass(\TypedUserAttributes\Version::class);
  \printf("method version: %s\n", $methodVersion);

  exit();
}

最後に

インターフェースが追加されたことにより、パッケージを設計する開発者にとっては非常にありがたい恩恵を受けることができるようになりました。

これからはAttributeを使う際には、インターフェースを使用して欲しいです。

また、ここで紹介したコードはここに置いて起きました。 gist.github.com

HackのコードジェネレーターをHackTestに対応させた

Hack用のテストフレームワークが出たので、HackUnitから置き換えました。
github.com

そのついでに、CodegenにHackTestのテストコードを生成できるジェネレーターを追加しました。 github.com

HackUnitからの移行

名前空間をHackUnitからHackTestに置き換えるだけで移行できます。

移行前

use HHPack\Codegen\HackUnit\{TestClassGenerator};

移行後

use HHPack\Codegen\HackTest\{TestClassGenerator};

生成されるテストコード

下記のようなテストコードを生成します。
テストコード自体は、HackTestとFBExpectを使用する前提のコードなので、composerで別途パッケージをイントールする必要があります。

<?hh // strict
/**
 * This file is generated. Do not modify it manually!
 *
 * @generated SignedSource<<d89af2ac5307bacbedfa27fd6389c1ff>>
 */
namespace HHPack\Codegen\Example\Test;
use type Facebook\HackTest\HackTest;
use function Facebook\FBExpect\expect;

final class Bar extends HackTest {

  public function testExample(): void {
    expect(true)->toBeTrue();
  }
}

パッケージのインストールはHackTestのREADME.mdに書いてある通りにコマンドを実行するだけで、インストールできます。

hhvm /path/to/composer.phar require --dev hhvm/hacktest facebook/fbexpect

HackUnit、PHPUnit使わなくて済むのと、オフィシャルのテストフレームワーク(ブログで発表された)ぽいので、これ使った方がいいと思います。

jbuilderからduneに変えた

dune1.0がリリースされたので、jbuilderからduneに置き換えました。
作業内容としてはそんなに修正箇所はありませんでした。

github.com

作業内容

  1. jbuildファイルの変更
    • jbuildファイルをduneにリネーム
    • 設定ファイルの変更
  2. コマンドをjbuilderからduneに変更
  3. opamファイルの変更
    • 依存しているパッケージをjbuilderからduneに変更
  4. コンパイル時のwarningメッセージを出ないように修正
    • コンパイル時のオプションが変わったのか、warningがでるようになったのでソースを修正

1. jbuildファイルの変更

jbuildファイルをduneに名前を変えた後に、S式の()を一つ外します。
duneではいらなくなったようです。

削除し忘れていても、ビルド時にメッセージで()がいらないと言われるので、すぐ直せます。

不要な()を削除する

修正前
(library (
  (public_name typesafety.misc)
  (name misc)
))
修正後
(library
  (public_name typesafety.misc)
  (name misc)
)

パラメメータの置き換え

パラメータは${}で指定できましたが、duneからは%{}に変わったようです。  

(rule (
  (targets (github_t.ml github_t.mli))
  (deps (github.atd))
  (action (run ${bin:atdgen} -t ${<}))
))
(rule
  (targets github_t.ml github_t.mli)
  (deps github.atd)
  (action (run %{bin:atdgen} -t %{deps}))
)

2. コマンドをjbuilderからduneに変更

これは単純にjbuilderをduneに置き換えるだけです。

修正前

build:
    jbuilder build

修正跡

build:
    dune build

3. opamファイルの変更

これも単純にjbuilderをduneに置き換えるだけです。

修正前

build: [
  ["jbuilder" "build" "-p" name "-j" jobs]
]
build-test: [
  ["jbuilder" "runtest" "-p" name]
]
depends: [
  "jbuilder" {build}
]

修正後

build: [
  ["dune" "build" "-p" name "-j" jobs]
]
build-test: [
  ["dune" "runtest" "-p" name]
]
depends: [
  "dune" {build}
]

4. コンパイル時のwarningメッセージを出ないように修正

使用していない式や型の定義などは、warningがでるようになりました。
これは単純に直していけばいいので、そんなに難しくないかと思います。

js_of_ocamlをjbuilder(dune)で試してみた

OCamlバイトコードをJSに変換できるツール、js_of_ocamlを試して見ました。
とりあえず、jbuilder(dune)を使用して、JSを出力できるようにはなりました。
今回はビルドの方法だけの内容です。

必要なもの

opam経由で必要なパッケージをインストールします。
必要なパッケージは下記の通りです。
インストールされたバージョンは最新の3.1.0です。

opam install -y js_of_ocaml js_of_ocaml-ppx

jbuildファイルを用意する

ruleに出力するファイルの設定を追加します。
js_of_ocamlのオプションに--source-mapを追加して、ソースマップを出力するようにしています。

また、aliasにDEFAULTを追加して、依存しているファイルも用意するようにしています。
jbuildファイルがあるディレクトリをベースにindex.html、main.jsをコピーします。

(jbuild_version 1)

(executables (
  (names (main))
  (preprocess (pps (js_of_ocaml-ppx)))
))

(rule (
  (targets (main.js))
  (action
    (run ${bin:js_of_ocaml} --source-map ${path:main.bc} -o ${@} --pretty)
  )
))

(alias (
  (name DEFAULT)
  (deps (main.js index.html))
))

index.htmlの用意

main.jsのファイルを読み込む適当なHTMLファイルを用意します。

<!doctype html>
<html>
  <head>
    <title>example</title>
    <script src="main.js"></script>
  </head>
  <body>
  </body>
</html>

main.mlの用意

コードは簡単なものにして、デバッグしやすくします。
このコードは、DOMContentLoadedイベントにイベントハンドラを追加して、デバッグログを出力するだけのコードです。

open Js_of_ocaml

module Html = Dom_html

let document = Html.window##.document
let addEventListener = Dom.addEventListener
let domContentLoaded = Dom_events.Typ.domContentLoaded

let debug f = Printf.ksprintf (fun s -> Firebug.console##log (Js.string s)) f

(* DOMContentLoadedのイベントハンドラ *)
let dom_content_loaded evt =
  debug "debug: %s" "domContentLoaded!!";
  Js.bool true

let () =
  let event_handler = Dom.handler dom_content_loaded in
  ignore (addEventListener document domContentLoaded event_handler (Js.bool false))

Makefileを用意する

とりあえず、ビルドと成果物のクリーンアップをできるようにします。
jbuildのaliasにDEFAULTを指定しているので、@DEFAULTをコマンドの引数に追加するだけで、ビルドできます。

all:
    jbuilder build @DEFAULT

clean:
    jbuilder clean

JSのビルド

ビルドはmakeコマンドを実行するだけです。
__build/default の配下に出力されたJSファイル、index.htmlコピーがあるはずです。
index.htmlファイルをブラウザで開いて、コンソールにデバッグメッセージが出力されていればOKです。

make

Hackで書いたProcessパッケージ

Processパッケージをリファクタリングしました。
https://github.com/hhpack/process

主な変更点は下記の2点です。

  • stream_selectからstream_awaitへの置き換え
  • IDisposableを使用したリソース管理

stream_selectからstream_awaitへの置き換え

パイプの状態監視に今まではstream_selectを使用していました。
これをstream_awaitに置き換えました。

stream_awaitは非同期な関数なので、awaitを使用していたのですが、戻り値のステータスが期待した値を返してくれませんでした。

仕方ないので、\HH\Asio\join関数で処理を待つように変更したところ期待通り動作するようになりました。

stream_awaitは内部的にepollでも使ってるのかな?

before

await stream_await($handle, STREAM_AWAIT_READ, 0.2);

after

\HH\Asio\join(stream_await($handle, STREAM_AWAIT_READ, 0.2));

IDisposableを使用したリソース管理

前の実装までは__destructでプロセスのパイプをクローズしていたのですが、IDisposableが使えるようになったので、置き換えました。

これでブロックスコープを抜けた後などに、パイプをすぐにクローズできるようになりました。

この機能はHHVM3.24以降で利用可能です。 https://hhvm.com/blog/2018/01/16/hhvm-3.24.html

おそらく、C#ユーザーあたりから提案があったのだと思います。(async/awaitもそうだったはず)

<?hh //strict

use HHPack\Process;
use HHPack\Process\ProcessOptions;
use HHPack\Process\Output\{ Stdout, Stderr };

async function pipe_example() : Awaitable<void>
{
  $options = new ProcessOptions();
  $options->stdout(new Stdout());
  $options->stderr(new Stderr());

  using ($p = Process\spawn('hh_client restart', [], $options)) {
    await $p->wait();
  }

  using ($p = Process\spawn('hh_client check --json', [], $options)) {
    await $p->wait();
  }
}

ppx_inline_testの仕様が変わったみたい

ppx_inline_testの仕様が変わったぽいです。
今まで、テスト実行時にコンソールにテスト結果が表示される仕様だったと思うのですが、デフォルトで表示されなくなったようです。
なのでテスト実行されているのかがわかりにくくなっています。

テスト結果を表示したい場合は、-verbose オプションを指定すれば表示できます。
変更はjbuildファイルのテスト設定部分に指定するだけです。

(alias
  ((name runtest)
  (deps (test_runner.exe))
  (action (run ${<} inline-test-runner [ライブラリ名] -verbose -diff-cmd "diff -u --label actual --label expected"))))

f:id:holyshared:20180401205347p:plain

2回目以降のテストに関しては、ファイルに変更がない限りは結果は表示されないようです。

f:id:holyshared:20180401205357p:plain

テストを落ちるように変えてみて、結果が失敗するかもみてみましたが、正常にテストが落ちるので問題ないようです。
久しぶりにmake testを実行してみて、テスト結果が表示されないので、ビルドが期待どうりできてないかと思ってびっくりしましたが、問題なくてよかったです。