その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

builderscon tokyo 2017に参加してきた

builderscon.io

参加してきました.
見た中で印象に残ったトークについて感想を少し.

詳細な内容を何も書けないんですがはちゃめちゃに面白かった

ブラウザ拡張のクロスブラウザ対応についての話.いろいろな便利ツールを自作して利用していて格好良かった.とにかくクロスブラウザ対応は大変そうという印象 (特にedge).なんとかなってほしい……

複雑なUIを持つブラウザアプリケーションをどう設計・実装するかという話.毎度のことながら実例や実コードを交えてわかりやすく説明していてよかった.Validation周りのアーキテクチャについてはあまり触れられていない分野だと思っていて,そこに言及されていたのは大変参考になった.
最後に言っていた「いくらきれいな設計に見えても,チーム内で合意が取れていない設計は間違えた設計」は本当に至言だと思う.

分割QRコードは完全に初耳で,その存在を知れただけでも収穫だった.大きなQRコードだと破損確率が上がるから,それを分割して1QRコードあたりの情報量を下げることで破損確率を下げるというのは考えられているな〜と思った.便利.

CIに時間がかかってしまうと開発のテンポが悪くなり結果的にスピードが落ちる.とにかくCIが遅いとつらい.そこで大量の並列数でゴリゴリぶん回して高速化を図るというのは合理的な考え方だと思った.そうした開発の足周りについてしっかりR&D的な活動をしているのは素晴らしい.

ファブレスな環境でキーボードを大量生産するという話.去年のbuildersconではcho45さんがキーボード製作のテクニカルな話で登壇されていたけれど,このセッションはどちらかと言うと製品製造の話で,製品設計や工場選定,部品選定,QAに至るまでのフルスタックなプロセスについて話されていて,とてつもなく濃密なセッションだった.有象無象の工場がどんどん違法行為を働いている様もうっすら明かされており迫力があった.普通にこういうコンサルタントで金を取れそう.しかしファブレスで製品を作るのはとにかく大変そうだ……

PHPの話かと思いきやHHVMの話だった.かつてPHP5で関数 (風) プログラミングスタイルで書かれていたSlackのサーバサイドソフトウェアをHHVMに徐々に移行して,今となってはHHVM + hacklangでPHPコンポーネントの全てが書かれているというのはなかなか先鋭的で面白かった.
「なぜHHVMを使っているのか」という質問に対して「SlackにはHHVMのコントリビュータがいて,知見を持っている人がたくさんいたから」と回答していて,技術選択のポリシーが確立されていてとても良いと思った.
ところで本筋とは関係無いんですが「Surprised type conversion」を「びっくり型変換」と通訳の人が翻訳されていて,非常に良い翻訳だと感心しました (本来は「Unexpected type conversion」とかになるはずなので,元の言葉のニュアンスを踏まえた結果なんでしょうが).

雑感

会場の日吉は程よく都心から遠く,カンファレンスに参加しているという感じがして良い.
慶応日吉キャンパスはHUBがあるので便利な一方,しかしついつい酒を飲みすぎてしまい一長一短あると感じている.毎日体調が悪かった.
それはそれとして今回も前回のbuilderscon同様様々な分野の話を聞けて良かった.次回も参加したい.

YAPC::Fukuoka Hakata 2017にてWeb Application Good Error Messageというタイトルで話してきました

表題のとおりです.話しました.
これは僕が普段の開発中にエラーメッセージと触れあう時に気にしていたり,考えていることを上手いこと言語化したいという試みから始まったものです.

speakerdeck.com

発表中にdan kogaiさんから「『間違えたことを言っているエラーメッセージ』も悪いエラーメッセージじゃないのか」というフィードバックを頂いて,確かに!! と思いました.すっかり抜けていました.おっしゃる通りです.

発表後頂いた質問としては「(セキュリティ的な観点から) エンドユーザ (非開発者) に詳細なエラーメッセージを表示しては駄目な場合とかがあると思うんだけど,そこらへんどうしてるのか」というものがあったんですが,回答としましては:

  • 技術的に詳細なエラーメッセージはエンドユーザに提供しない
    • エラーが出ている場合,エンドユーザに技術的詳細を提供しても基本的に対処不可能 (多くの場合サーバのエラーなので)
    • 非技術的な方法で対処できる場合はその手順をエラーメッセージとして示す (e.g. 「ネットワーク状態の良いところで試してください」)
    • 仮に「エンドユーザの操作・手順がおかしいからエラーが出ている」という場合があったとしても,それはアプリケーション側のバグ (ユースケースを想定してない,あるいは限定しきれていない) ということなので,「ユーザに正しい使い方を提示する」といったエラーメッセージは (基本的に) 提示しない
    • Tracking IDみたいなものを併記すると問い合わせとかに使えて良さそう
  • 何らかの統一的なエラーに統一する,みたいな手法もある
    • 「アプリケーションがおかしいので後でやり直してみてください」みたいなのに全部のエラーを隠蔽するとか……
    • これに関しては賛否あると思いますが
  • 技術的詳細が書かれた「解決」の為のエラーメッセージは開発者向けには出す

という感じでした.このへんは発表時間の都合上,省略してしまったので情報が薄くなってしまいました.反省しています.

あと「moznionを呼ぶボタン」はどういう実装になってるのかという質問もあったんですが,あれはボタンを押すとikachanが発火して僕にチャットでメンションが飛ぶという素朴な仕組みになっています.


今回のスライドに書かれていることは個人の思想が強いので,他にも様々な意見等あることと思います.エラーメッセージに関してはもっと色々な考えや,トピックや,思想があると思うんですがあまり表立ったものが無い気がするので,色々議論したい感じがしています.しましょう!

Exit statusのセマンティクス

*nixのexit statusのセマンティクスについてかつて質問して,答えていてもらっていたことを思い出したので記します.

moznion   [5:46 PM] signal受け取ってexitする時,そのsignalの値をそのままexit codeに使う,みたいなお作法みたいなのってあるんでしたっけ
takesako  [5:50 PM] ないと思いますー
moznion   [5:50 PM] 特に無いんですねえ,ありがとうございます
songmu    [5:51 PM] なんか、ラッパースクリプトとか書くときは、ものによるけど維持するように気をつけることとかある。
          [5:51 PM] horensoとかは維持するようにしてたはず。
moznion   [5:52 PM] なんかそこら辺はお行儀みたいな感じですかねえ
songmu    [5:53 PM] 上位でどのシグナルで殺されたかとか判断したいかどうか、とかかなぁ。
xaicron   [5:53 PM] eixt code はアプリ自体で定義しているものを出すってことに決めればいいと思ってる派
          [5:53 PM] コマンドラインだったら成功か失敗かぐらいしかほとんどユースケース無い気がする。あとはログを出そう
moznion   [5:55 PM] まあですよねえ
          [5:55 PM] 利用者がexit codeで挙動変えるような使い方をしてるかも知れないから,ラッパーであればcodeを維持するみたいな理念だと察しました
songmu    [5:55 PM] そですね
moznion   [5:57 PM] ログを出しておいたばかりにそれを利用者側に正規表現で引っ掛けられて挙動を分岐させられるのは起こりえそうですが知ったことではない
hirose31  [6:09 PM] exit code、意味あるで
tokuhirom [6:12 PM] どこがダジャレになってるのか気になっている
hirose31  [6:12 PM] w
          [6:12 PM] http://www.unix.com/man-page/all/3/sysexits/ とか /usr/include/sysexits.h とか。
          [6:13 PM] sendmailとかは、aliasesで呼ばれてるフィルタプログラムが exit 75 (EX_TEMPFAIL) すると再実行したりするで。
cho45     [6:15 PM] 絶妙に中途半端な数字だ
hirose31  [6:16 PM] 「いそのー EX_UNAVAILABLE しようぜー」
kazuho    [6:31 PM] execすればexitコード一致問題なくなるで
yappo     [6:52 PM] ひどいw
moznion   [7:31 PM] exit code割と無自覚に使ってたもんで,なんか紳士協定とかがあるのか気になったという次第でした
mattn     [7:32 PM] http://linuxjm.osdn.jp/html/LDP_man-pages/man3/exit.3.html
          [7:33 PM] https://ja.wikipedia.org/wiki/%E7%B5%82%E4%BA%86%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9
          [7:33 PM] こっちか
          [7:33 PM] 普段 0/1 だけど usage 出す時に変えたりしますね
moznion   [7:34 PM] fsckって論理和でステータス変わるのか……
          [7:34 PM] ですね,僕もそういう感じで使ってました

JSON::XSでデシリアライズする時にtrueとfalseの扱いを変更する

Perl5の話題です.
JSON::XSを用いて,JSON StringをPerlのHashRefにデシリアライズする時にtrueとfalseの扱いを変えたいという話です.

デフォルト状態でJSON::XSを用いてデシリアライズすると, trueTypes::Serialiser::true すなわち JSON::PP::Boolean の真値として, falseTypes::Serialiser::false すなわち JSON::PP::Boolean の偽値として扱われます.
普通の処理であればこれで問題ないかもしれませんが,例えば「JSON StringをPerlのHashRefにしてそれを更に何らかのシリアライザに通す」といったような処理をしようとすると,JSON::PP::Boolean のオブジェクトだと取り回しが悪い場合があります.そういった時に,true/falseを任意の値にマッピングするにはどうすれば良いか.

https://metacpan.org/pod/JSON::XS#true,-false
Types::Serialiser - simple data types for common serialisation formats - metacpan.org

やり方としては上記のドキュメントのまわりにあるように, $Types::Serialiser::true 及び $Types::Serialiser::false を任意の値 (reference) に書き換えてやると良い.これらの変数はourで宣言されており,外から操作することができる.

use Types::Serialiser;
local $Types::Serialiser::true;
local $Types::Serialiser::false;
BEGIN {
    $Types::Serialiser::true = \1;
    $Types::Serialiser::false = \0;
}

use JSON::XS qw/decode_json/;

my $json = decode_json('{"true": true, "false": false}');
use Data::Dumper; warn Dumper($json);

例えばこのようにすると true\1 に,false\0マッピングされるようになります.

あるいは以下のように所望のオブジェクトにマッピングすることもできます;

package MyBool {
    sub new {
        my ($class, $val) = @_;

        return bless {
            val => $val,
        }, $class;
    }
}

use Types::Serialiser;
local $Types::Serialiser::true;
local $Types::Serialiser::false;
BEGIN {
    $Types::Serialiser::true = MyBool->new(1);
    $Types::Serialiser::false = MyBool->new(0);
}

use JSON::XS qw/decode_json/;

my $json = decode_json('{"true": true, "false": false}');
use Data::Dumper; warn Dumper($json);

注意としては, $Types::Serialiser::true 及び $Types::Serialiser::false はreferenceを要求しているという点です.仮にここにreferenceではないscalar値なんかを突っ込むとなにが起きるかというと

use Types::Serialiser;
local $Types::Serialiser::true;
local $Types::Serialiser::false;
BEGIN {
    $Types::Serialiser::true = 1;
    $Types::Serialiser::false = 0;
}

use JSON::XS qw/decode_json/;

my $json = decode_json('{"true": true, "false": false}');
use Data::Dumper; warn Dumper($json);
$ perl json.pl
Segmentation fault: 11

SEGVします. JSON::XSはxsの処理の中身で Types::Serialiser::trueTypes::Serialiser::false の中身を SV * つまりreference相当に割り当てているためです.ここで Types::Serialiser::true|false にscalar valueすなわち SV を割り当てると当然SEGVしてしまいます.

このへん
https://metacpan.org/source/MLEHMANN/JSON-XS-3.03/XS.xs#L95
https://metacpan.org/source/MLEHMANN/JSON-XS-3.03/XS.xs#L1974
https://metacpan.org/source/MLEHMANN/JSON-XS-3.03/XS.xs#L136

<追記>
誤りの指摘を頂いたので修正しました.SvROKで事前チェックすれば良い気もするんですがどうなんでしょうね.

karupanerura [17:59]
リファレンスでもスカラ値でも `SV *` だから、ここでSvRVしているときにリファレンスじゃないとNULLが返ってくるのでSEGVしているというのが濃厚なきがする https://metacpan.org/source/MLEHMANN/JSON-XS-3.03/XS.xs#L139
[18:01]
リファレンスは `SvRV` というSVで、数値も `SvIV` というSVで差異はなくて、XSでSVを扱うときは基本的に `SV *` として扱うので、<追記ここまで>

きをつけましょう.

List::Haystack - A immutable list utility to find and count element


Perl5の話題です.List::Haystackというものを書きました.CPANにもアップロードしています.
経緯としては以下.


というものが欲しかったのです.List内の要素検索について,Immutableに処理をしたい + lazilyに処理したい,みたいなモチベーションです.

個別の使い方としてはpodを読んでもらうとして,例えば以下のように書くだけでシェークスピアのワードカウントなんてのを手っ取り早く書けるわけです.皆さん大学とかでやりましたでしょう.僕はやりませんでした.

use LWP::UserAgent ();
use List::Haystack;
use Data::Dumper;

my $response = LWP::UserAgent->new->get('https://ocw.mit.edu/ans7870/6/6.006/s08/lecturenotes/files/t8.shakespeare.txt');
my $txt = $response->decoded_content;
my $words = [grep { $_ } split /[^a-zA-Z0-9]/, $txt]; # XXX: Sloppy word separation!!!
my $haystack = List::Haystack->new($words);
print Dumper($haystack->haystack);

まあこういうのってperlだとHashとかで簡単に書けるんで別にモジュールにしなくても……と言う感じですが,アドホックに書くと同じような処理が随所に散らばってしまいがちで精神衛生上良くなかったので一丁モジュールにしたためた次第です.あとlazilyなconstructionが欲しかったので.

ご利用くださいませ.

Perl5のサブルーチン呼び出し時に二項演算子を期待していたら単項演算子の引数として認識されてしまう

sub func {
    return 100;
}

みたいなサブルーチンがあった時に,

say(func - 99);

みたいな感じで呼び出すと,パッと見 1 が出力されそうに見えるが,実際は 100 が出力される.
これは - 99func の引数として解釈されて食われるためである. B::Deparse するとわかりやすい.

$ perl -MO=Deparse func.pl
sub func {
    use warnings;
    use strict;
    use feature 'say';
    return 100;
}
use warnings;
use strict;
use feature 'say';
say func(-99);
func.pl syntax OK

ここから,二項演算子としての振る舞いを期待していた -99 と結合して単項演算子として扱われ, -99 が引数として扱われているのが読み取れる.

もちろん,サブルーチン呼び出し時にかっこを付けて

say(func() - 99);

という感じで呼び出してやれば当初の期待値である 1 が表示されるようになる.もしくはプロトタイプを利用する方法もある.

sub func () {
    return 100;
}

このように引数を取らないことをプロトタイプで明示すると,サブルーチン呼び出し時にかっこを付けなくても後続の値が引数として解釈されることはなくなる.その場合の B::Deparse の結果は以下のようになる.明解.

$ perl -MO=Deparse func.pl
sub func () {
    use warnings;
    use strict;
    use feature 'say';
    return 100;
}
use warnings;
use strict;
use feature 'say';
say func - 99;
func.pl syntax OK

これらは基本的な挙動ではあるのだけれど,日々を生きているとたまにハマりがち.例えば Time:PieceTime::Seconds をあわせて使ったときなんかに

use Time::Piece;
use Time::Seconds qw/ONE_HOUR/;
my $t = localtime - ONE_HOUR;
say $t;

などと書こうものなら,結果が Thu Jan 1 08:00:00 1970 というふうになり破滅する.localtime(-ONE_HOUR) となるからだ.
普段,perlの組み込み関数の時は (基本的に) かっこを付けずに記述するスタイルで書いていたので,組み込み関数である localtime をその感覚で使っていたところ綺麗にハマってしまった. *1


いままでは割と野生の勘みたいな感じでこのへんの諸問題を回避していて,「そういう掟なのじゃ」という感じで生活していたんですが真面目に B::Deparse とかで調べてみると色々学びがあるものですね.

*1:Time::Pieceはlocaltimeをoverrideするから最早組み込み関数ではないんでは? という意見はあると思いますが

YAPC::Fukuoka 2017 HAKATAのトークプロポーザルを出した

yapcjapan.org

出しました.最近YAPCでしか喋ってない感じがしますね.果たしてそうなのです.

というわけでプロポーザル内容は以下のとおりです.

Web application good error messages and bad error messages

Webアプリケーションを作り,運用していると例外的な状況が発生するものです.
例外的な状況 (エラー) は起きない (起こさない) に越したことはありませんが,しかしながらそれらをゼロにするのには相当のコストを要しますし,そもそも「本当にエラーが起きる余地が無いのかどうか」を証明するのも困難です (こうした問題領域を解決する為の手法やツールもありますが,そうしたものについては本トークでは触れません).
従って現実問題として,そうした例外的な状況に対応していく必要があります.そのような時にプリミティヴな武器として有効なもののひとつに「エラーメッセージをログに書き込んでおく」というものが挙げられると思います.また,エラーメッセージはサーバ内にログとして留めておくだけではなく,クライアントに対して表現する必要がある場合もあります.つまり,サーバ・クライアントを問わず,エラーメッセージは問題の修正・解決にとって重要な役割を果たしていると言うことが出来るでしょう.これはユーザ体験にも直結する要因のひとつとも言えます.

本セッションでは

  • サーバ内部でのエラーメッセージ
  • 外部に提供するエラーメッセージ

という2つの文脈について

  • 問題の「修正」に役立つエラーメッセージとは
  • 問題の「解決」に役立つエラーメッセージとは
  • エラーメッセージの粒度
  • エラーメッセージのレベル
  • エラーメッセージの検索性
  • perlにおけるロギング

などといった話題について触れたいと考えています.




エラーメッセージやロギング等はアプリケーションの構築および運用において不可欠で重要な存在だと思っているのですが,あまりそこら辺にフォーカスを当てた発表や資料が無いな〜と思ったので *1 こうしたテーマを選択した次第です.

採択されたら喋れます.よろしくお願いします.

*1:しっかりあることにはある.例えば右は非常によい資料です: 良いデバッグログはプロジェクトの資産である // Speaker Deck