エラーハンドリング・クロニクル #nds41

はじめに

プログラミング技術の歴史は、ありとあらゆる歴史がそうであるように、いろんな「史観」で眺めることができます。ならば、プログラミング技術の歴史を、「エラーハンドリングとの戦い」という視点から見ることもできるのではないでしょうか。本日は、エラーハンドリングとの戦いの歴史を俯瞰することで、エラーハンドリングの勘所について考えていこうと思います。

なお、このエントリはNDSという勉強会の第41回で発表した内容と同一です。

Cの時代

Cの時代のエラーハンドリングでは、関数の返り値と、グローバル変数errnoを見ることで処理が成功したか失敗したかを見るのが一般的でした。

例として、文字列をlongに変換するstrtol関数をmanで引いてみましょう。すると、だいたい以下のようなことが書かれています。

  • 変換に失敗すると、0を返す
  • 変換に失敗した場合、グローバルな変数であるerrnoに以下の定数を格納する
    • invalidな文字列を渡された:EINVAL
    • longをoverflowしたりunderflowする数を渡された:ERANGE

さて、一見してとてもシンプルな仕様ですが、現代的な視点からみてみると、いくつか気になることがありそうです。ざっと挙げてみると……

  • 関数を呼び出したあと、エラーであるかどうかを手動でチェックする必要があるし、仮にチェックしなかったとしてもコンパイラが怒ってくれるわけではない
int main(int argc, char *argv[]){
  long l = strtol("string");
 
  // なんかこうごちゃごちゃといろいろやる
 
  some_function(l); // ここでおかしなことになる
  return 0;
}

これは結構怖いですね。たとえば"String"みたいな文字列をstrtolに渡して、エラーチェックをしなかった場合、0という数だと解釈されたまま、プログラムは動き続けます。そして、どこかこのstrtolを呼び出したところから遠く離れたところで、いきなりプログラムがクラッシュします。バグを入れ込んでしまったところと、バグが発見されたところが遠く離れているとき、デバッグは困難を極めます。うーん。おそろしい。

では、必ずエラーを手動でチェックすればそれで問題は解決でしょうか。そんなことはありません。たとえばあなたが文字列と文字列を受け取り、それをlongとして解釈して足す関数を含むライブラリ作成しているとしましょう。このとき、もしstrtolにinvalidな文字列を渡してEINTVALが帰ってきても、自分ではそのエラーをどうハンドリングすべきか、ということはわかりません。なぜかというと、「変な値入れられた時にそれをどう扱うべきか」というのは、ライブラリが決めるべきことではなくて、アプリケーションの要件によって決まることだからです。

なので、あなたはあなたが作成している関数の利用者に対して「invalidな文字列が渡されたよ」というエラーを通知する必要があるわけです。つまり、エラーを上流に伝播させる必要があるわけです。そして、このようなシチュエーションは決して珍しいものではありません。そのたびにあなたは起こりうるエラーに対してすべてエラーコードを定義して、エラーの場合の返り値を決めて、という作業をしなければいけません。そして、あなたの書くコードは本質的な処理よりもエラーハンドリングのためのコードによってどんどん太っていきます。

つまり、このやり方では、

  • エラーを無視しやすい
  • エラーを上流に伝播させるのがだるいしむずかしい

という問題があるわけです。

そして、それだけではありません。まだ気になる点はあります。それは、

という点です。グローバル変数は、いつ書きかわるかわかりません。なので、errnoをあとから参照したい場合はそれ用の変数を作ってコピーしておく、という回避策が一般に取られています。

int main(int argc, char *argv[]){
  long l = strtol("string");
  int strtol_err = errno; // コピーしとかないといけない
 
  // ちょっとなんかやる間にerrnoが書きかわる可能性‥‥
 
  if (strtol_err== EINVAL) {
    // エラー処理
  } else if (strtol_err == ERANGE) {
    // エラー処理
  }
 
  return 0;
}

シングルスレッドで動いているならば、「次の行で必ずチェック、あるいはコピーする」ということを徹底すれば(それだって結局人間がやらなければならないのですけれど)問題にはならないでしょう。しかし、もしもこれがマルチスレッドで動いていたら?errnoは本当に「どのタイミングで書きかわるかわからない」ものになります。

さらにもうひとつ問題があります。それは「エラー時にリソースの解放をするのが煩雑」という点です。

エラーになってしまったとき、mallocで確保していたものをfreeせずに早期returnなどをすると、正常系では解放されるリソースが解放されなかったりします。これを防ぐためによるあるパターンは、関数の後ろのほうにリソース解放の処理を書いておき、返り値は変数に入れておき、gotoでリソース解放のところにすっ飛ぶパターンです。

int nyan(){
  int *p1 = (int *)malloc(sizeof(int))
  int *p2 = (int *)malloc(sizeof(int))
  int *p3 = (int *)malloc(sizeof(int))
  int retval = 0;

  // ごちゃごちゃなんかやる
  
  //!エラーが起こった!  
  if (err) {
     retval = -1; // エラーコードを入れて
     goto cleanup; //cleanupにすっ飛ぶ
  }
  
  //正常系は続く
  
  retval = 1; //正常系の場合の返り値を入れて
  
cleanup:
  free(p1);
  free(p2);
  free(p3);
  return retval;
}

エラー処理周りのコードだけで、こんなに大きな関数になってしまいました。バグを入れ込みそうで怖いですね!一時期話題になったApple史上最大のセキュリティバグ、goto fail; なんかはこのパターンをつかってたやつです。

Cスタイルのエラーハンドリングの問題点については、おおまかにこんなところでしょうか。

一度まとめておくと、

  • エラーを無視しやすい
  • エラーを上流に伝播させるのがだるいしむずかしい
  • エラーの内容がグローバル変数に格納されている
  • リソース管理が絡むと煩雑になる

ですね。

例外の時代

さて、Cの時代のような問題に対抗するために、人間は例外という新しい武器を作り出しました。例をあげながら、上述の問題点がいかに解決されているのかを見てみましょう。

  • エラーを無視しやすい

例外をrescue(キャッチ)しわすれるということはあり得ますが、その場合もプログラムはすぐさまクラッシュしてくれるので、エラーを無視してしまっても、「間違えた内部状態のままプログラムが進んでしまって、バグを入れ込んだところと遠く離れたところでいきなりクラッシュする」というようなことは防げるようになりました。

class WanError < StandardError; end

def wan
  raise WanError, "エラーだよ!!!"
end

wan # rescueしていないのでここでプログラムは止まってしまう
  • エラーを上流に伝播させるのがだるいしむずかしい

例外をraiseしてそれをrescueしなかった場合、例外はコールスタックを上流に向かってどんどん突き進んでいきます。なので、何も書かなくても nyan の中でよんだ wan のエラーを main で捕まえることができます。

class WanError < StandardError; end

def nyan
  wan
end

def wan
  raise WanError, "エラーだよ!!!"
end

begin
  nyan
rescue WanError => e # wanの中で発生したエラーをここで捉えられる
  p e
end

例外が起こるたびに例外オブジェクトを発生させるので、グローバル変数にエラーを入れておく必要がありません。

  • リソースお片付け問題

ensureやfinaly(例外が起こっても起こらなくてもかならず実行される部分)があるので、そこでお片付けすればシンプルです。

r = Resource.new
begin
  # do something
rescue => e
  # do something
ensure
  r.close
end  

さて、いいことづくめであるような気がする例外機構ですが、近年、この例外機構をもってしても解決できない問題が人類を襲いました。

  • キャッチし忘れ問題

例外をキャッチしわすれると、アプリは死にます。それはもう見事に簡単に死にます。Javaの検査例外は例外のキャッチし忘れをコンパイル時に見つけてくれる仕組みですが、批判も多い機能ですね。今回はちょっと分量的に無理なので検査例外の話には立ち入りません。

  • 非同期処理との相性の悪さ

一般に、非同期処理が絡むと、例外の扱いはかなり難しくなってきます。というのも、(たとえば)スレッドAで起こった例外は、そのままスレッドBでキャッチすることはできません。これは、スレッドAとスレッドBが別のコールスタックを持っていることを考えれば当然のことです。そのため、スレッドをまたいだ例外の取り扱いというのは非常にむずかしいものとなります。Javaはそれに対して Callable と Future という回答を出し、それは一定の成果を上げていると言えそうです(このあたりも詳しく入り込む余裕がないので入り込みません)。しかし、たとえば goroutine のように、非同期なタスクから連続的に値を受け取りたいときなどはどうすればいいでしょうか? Runnable では依然として別スレッドの例外を補足する方法はなく、非同期処理と例外機構というのは、やはり結構相性が悪いもののようです。

Eitherの時代

さて、Cスタイルのエラーハンドリングに対して、例外とは別の方向から回答を出したのが、関数型界隈でよく使われているEitherというデータ型です。

Scalaの例で説明しましょう。Scalaにおける Either というのは、LeftかRightどちらかの値を持つデータ型です。LeftとRightはコンテナになっていて、どんな値でもその中に入れることができます。

Eitherの使い方としては、正常に処理が成功した場合はRight(正しい、という意味のRightと掛けている)に値を突っ込んで、失敗した場合は失敗の理由などを表すオブジェクトをLeftに突っ込んで返します。こんな感じ。

def divide(x:Int, y:Int): Either[String, Int] = {
  if (y == 0) {
    Left("can't divide by zero")
  } else {
    Right(x / y)
  }
}

divide(2, 2) // => Right(1)
divide(0, 0) // => Left("can't divide by zero")

Cスタイルと同じく、値としてエラーかどうかを返すスタイルです。が、Eitherの場合どのようにCスタイルの問題が解決されているのか見てみましょう。まず、

  • エラーを無視しやすい

という点は解決されています。というのも、Eitherの中身はそのままでは使えません。なんらかの方法で取り出す必要があります。

たとえば、Eitherの中身は、パターンマッチで取り出すことができます。

val either = divide(2, 2)

either match {
  case Right(x) => println(x)
  case Left(message) => println(message)
}

このとき、RightだけでパターンマッチしたりLeftだけでパターンマッチしようとすると、コンパイラが「caseが網羅的じゃないよ」と怒ってくれます。終始こんな感じで、Leftを無視してRightの中身だけを扱おうとするとコンパイラに怒られる仕組みが揃っています。

次に

  • エラーを上流に伝播させるのがだるいしむずかしい

という問題について見てみましょう。Eitherにはrightというメソッドがあって、これを呼ぶとRightProjectionというものが取得できます。これは「Eitherのright側を正当なものとして扱うよ」と決めたもの、のようなものです。このRightProjectionにはmapメソッドが生えていて、そのmapメソッドは「Leftの場合はそのままLeftを返して、Rightの場合は引数に指定した計算を行う」という挙動をします。

def divideAndDouble(x: Int, y:Int): Either[String, Int] = divide(x, y).right.map(_ * 2)

divideAndDouble(2, 2) // => Right(2)
divideAndDouble(0, 0) // => Left("can't divide by zero")

このように、map(やflatMap)を利用することで簡単にエラーを伝播させることができます(for式やモナドについては触れません。興味があれば調べてください)。

毎回Eitherを作るのでグローバル変数は駆逐できます。

  • リソースの解放問題

これはEitherによって解決されるものではないですが、関数型スタイルではリソースの確保や解放は副作用とみなします。関数型スタイルでは、副作用をなるべく局所的にまとめて、ロジックから分離するという別の方法で解決しています(雑な説明ですがここに入り込むとまた時間がかかるのでこれも詳しく気になるひとは調べてください)。

というわけで、Cスタイルの問題点はどうやらEitherでだいぶ解決できそうです。

ここからさらに、例外が持ち込んでしまった問題点をEitherがどのように解決しているかも見てみましょう。

  • キャッチしわすれ問題

上述の通り、エラーを無視しようとすると、コンパイラが怒ってくれますし、検査例外ほど煩雑でもありません。

  • 非同期処理との相性問題

例外機構は制御構文ですが、Eitherは単なるデータ型です。スタックを飛び越えたりしないし、「普通の値」として扱えます。なので、例外機構よりも素直に非同期処理においてエラーを扱うことができます。何度も言いますが、単なる値ですから。

おまけ・現代プログラミング言語の異端児、golangについて

golangも、例外機構以外の方法でエラーを扱う言語です。これは、goroutineの存在が大きいのではないでしょうか。goroutineはJavaのFutureとかと異なり、goroutine同士でデータをやりとりするために channel を使います。このとき、素直な例外機構はまったく役に立ちませんよね。

そこでgolangは、返り値を複数持てることを利用して、最初の返り値に正常系の返り値、2つめの返り値に異常系の時のエラー値を返す、という「慣習」をつくることにしてしました!!!!

file, err := os.Open(filename)

これはかなり大胆な考えかたですが、実は結構バランスのとれた解だと思います。

まず、うっかりerrを受け取り忘れると、コンパイル時に怒られます

f := os.Open(filename) //multiple-value os.Open() in single-value context

さらに、errを受け取ったとして、それを無視してもコンパイル時に怒られます

f, err := os.Open(filename)
// このあとなにもしないと、err declared and not usedと怒られる

さらに、リソースお片付け問題に関しても、defer を導入することで解決しています。

func openAndClose(filename string) {
    f, err := os.Open(filename)
    // snip
    defer f.Close() // openAndCloseを抜けるときに必ず呼ばれる
    // snip
}

現代的な言語なのにいわゆる例外がないの!!!!って最初はびっくりしますが、goroutineとの絡みを考えると非常にバランスのとれた設計だと言えそうな気がしますし、ある意味「あんまり堅苦しくないしモナドじゃないEither」みたいな立ち位置で、かなり面白いですね!

まとめ

エラーハンドリングのパラダイムをいろいろ見てみました。「どれが最高の正解」ってことはないけれど、それぞれのプラットフォームやパラダイムがどう問題を解決しようとしているのかを知ることで、より安全なアプリケーションを書く助けにはなるのではないでしょうか。

phpでzipしたい

php で zip したい場合は、array_map を使う(!!)

<?php
$arr1 = ["a", "b", "c"];
$arr2 = ["A", "B", "C"];
$arr3 = ["エー", "ビー", "シー"];

$zipped = array_map(
  function($a, $b, $c){
    return [$a, $b, $c];
  },
  $arr1,
  $arr2,
  $arr3
);

$zipped; // [["a", "A", "エー"],["b", "B", "ビー"], ["c", "C", "シー"]]

PHPのarray_mapは配列を複数放りこむことができ、配列を複数放り込んだ場合はmapのわたす関数の引数にそれぞれの配列の要素が順に入ってくる。ことを利用するとzip的なことができるっぽい。

さらに面白いことに、関数の代わりにnullをわたすと上記のzip操作と同じ動きをする。なんでや!って感じするけど。

ちなみに、長さの違う配列を複数渡した場合、足りない分はNULLが渡ってくる。

<?php
$arr1 = ["a", "b", "c"];
$arr2 = ["A", "B"];
$arr3 = ["エー", "ビー"];

$zipped = array_map(
  null,
  $arr1,
  $arr2,
  $arr3
);

$zipped; // [["a", "A", "エー"],["b", "B", "ビー"], ["c", null, null]]

このarray_map、他の言語ではあまり見かけない挙動のように思うし、これってarray_mapっていう名前でいいのかなって感じもするけど、zipとmapを一緒にやりたいときとかはこれはこれで便利っぽい気もする。というのも、PHPには遅延リストがない(よね?)ので、zipしてmapすると2回配列をトラバースすることになるけど、これなら一回のトラバースで良いので効率的ではある(zipとmapを一緒にやりたいときにのみ効いてくる効率化がどれほど重要かという話はあると思うけどまあそれはそれとしておいておきましょうよ)。PHPらしく、長さの違う配列渡したときにどっちに合わせるかを決めるオプションがありそう!!!とか思って探したけど、なかったっぽい。

see also: PHP: array_map - Manual

Functor における map の引数の順序を考えてたらいっこストンと腑に落ちた話

別に知見は書いてないですが、なるほどなーと思ったという感想を書いたエントリです。

ScalazとHaskellのFunctorの提供するmap(fmap)は、引数の順番が異なります。

  • Scalaz の Functor

    • def map[A, B](r: F[A])(f: A => B): F[B]
    • F[A] なFunctor値が最初の引数で、A => B な関数が次の引数で、F[B]なファンクター値が返り値
  • Haskell の Functor

    • fmap :: f => (a -> b) -> f a -> f b
    • (a -> b) な関数が最初の引数で、f a なファンクター値が次の引数で、f b なファンクター値が返り値。

つまり第一引数と第二引数が逆。

ふつうに考えると Scalaz のやつが直感的に思えます。C言語とかでも、ある構造体を操作するための関数って大体第一引数にその構造体を渡して、他のパラメータをその後に渡すし、Perlとかだって $nyan->do_something したら $nyan が第一引数に渡ってくるし。

なんでなんでそうなっているのか調べたわけではないんだけど、関数のリフティングするときにはHaskellみたいになってたほうが便利だよなーと思い当たって「ふーむなるほど」となりました。

要するに、Haskell スタイルだと f :: (a -> b) な関数を g :: (f a -> f b) に変換したいというときに、なにも特別なことをしなくても fmap f とすれば部分適応されるので自動的にそっから g :: (f a -> f b)を得ることができてうれしい。

一方 Scalaz はデータに注目した場合は直感的ではあるのだけれど、Liftingのためのメソッド lift の実装は def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f) となっていて、引数の順番が違うせいで一枚噛ませる必要がある。

これ、言語の特徴を捉えていて面白いな、と思いました。つまり、Scalaオブジェクト指向的な考えで、第一の関心が「オブジェクト」の側にある。だから「最初にFunctor値を受け取って、それに対して関数を適用しますよ」という引数の順序のほうが自然なんだけど、liftみたいな"関数に主眼が置かれた操作"をやるときにScalazスタイルだと「引数の順番が逆だったらな〜」ってなる。

逆に、Haskellスタイルは第一の関心が「関数の側」にある。だから「関数をリフティングしたい」みたいなときは自然にかけるんだけど、データ(この例で言えば Functor値)のほうに注目していると、この引数の順序ってなんとなく不自然に思える。

で、思ったんですけど、これ、fmapに限らず、filterとかでもそうですね。Haskellはだいたい関数を第一引数に取るようになっていて、部分適応してやることによって新しい関数を作り出しやすいようになっている。

なるほどな〜〜〜という感じでした。

(さらに気づいたことを追記)

これ、型推論上の都合もありそう。型推論が左から右に流れるから、先にF[A]が来てないと、実際のFunctor値の型から A => B の A を推論してくれないという都合があるかもしれない。

肉をやりました

個人の日記です。

id:moznion がはるばる新潟まできて肉をやってくれるというので好意に甘えて肉をやりました。

f:id:nkgt_chkonk:20150307182338j:plain

肉をはじめたところです。すでに仕込み済の肉をmoznionが持ってきてくれたので、焼くフェーズから開始している様子です。

f:id:nkgt_chkonk:20150307182352j:plain

焼いてくれています

f:id:nkgt_chkonk:20150307182446j:plain

このような見た目で最高な感じでした

f:id:nkgt_chkonk:20150307182944j:plain

うまい(確信)

f:id:nkgt_chkonk:20150307183555j:plain

こちらはアンティクーチョというペルー料理だそうです。ハツをスパイスとかビネガーとかに漬け込んで焼いたものだそうです。これ本当においしかったのでうちでもやろうと思った。

f:id:nkgt_chkonk:20150307185037j:plain

牡蠣も食べたかったので生牡蠣をレモンと塩でいただきました。

f:id:nkgt_chkonk:20150307211718j:plain

今回の功労者が青色のなにかを口元に押し当てている様子です。

感想

肉はとにかくうまかったのでとてもよかったです。

昔から家にひとを呼ぶのは好きだったんですけど、息子が生まれてからはなかなかこういう機会を作ることができませんでした。でもやっぱり家にひとを呼びたくて、妻に相談したら快くOKしてくれて肉会開催の運びとなりました。今回は来てくれたひとたち(@moznion @hayajo @NPoi @sambaiz)がみんな息子の相手をしてくれて、息子もとても楽しそうにしていたので本当にみなさんありがとうございますという感じでした。

あとこういう遊びを理解してくれて協力までしてくれるししかもかわいいわたしの妻はまじで最高という感じなのでみなさんもわたしの妻と結婚するといいですよ。ぜったい渡さないけど。

ところで、ここからは自分語りです。

わたしが子どもの頃、実家では母が音楽教室をやっていたのですが、母がイベント好きなのもあって、我が家は様々なひとが訪れる場となっていました。いい年してけっこう人間としてダメな感じのギタリストのおじさんとか、民族楽器を作ったり叩いたり弾いたり作り方を教えるワークショップを全国旅しながらやってるドレッドのおじさんとか、そういうちょっと「変」なひとたちがよく家に訪れてました。

わたしはちょっと性格に難がある(ちょっとだと信じたい)ので、どうしても子供社会でうまくやっていくことができなくて、実際に小学校や中学校はかなり苦痛だったのですが、そういう様々なひとがわたしを可愛がってくれるというのが、とても救いでした。楽しそうに生きてる大人が「学校以外にも世界はあるんだぜ」ってその態度で教えてくれる子供時代で、それは本当に恵まれていたんだなぁ、と今では思っています。

だから、というわけではないですし、どっちかというと単純にわたしが遊びたいからなんですけど、今後も家に大人をガンガン呼んでいきたいなぁ。そして結果として息子が「楽しそうに遊んでる大人たち」の姿からなにかを感じ取ってくれたらそれは多分いいことなんじゃないかな、なんて思っています(まあまだ息子はわけわかってないと思うけどな!)。というわけで、今後も家にひとを呼びまくるんで友人各位は覚悟しておいてくださると幸いです!

テンプレートをDRYにするのは慎重にやったほうがいいですよねというお話

社内でレビューおじさん業してて書いた内容ですけど守秘する必要ない情報なんでちょっと内容書き換えてオープンアンドシェアーします。

本文

見た目とかUIというのはソフトウェアの中でめちゃめちゃ柔らかい部品(些細な変更されることが多い部品)なので、「同じような部品だから共通化しちゃおう」ってやると失敗することが多いです。

特に気をつけるべきなのは、たとえばコンテンツをランキング形式でテーブルで表示する画面と、新着から順にテーブルで表示する画面があって、このふたつのテーブル部分は一緒だからパーシャルにしちゃおう、みたいなやつです。

見た目とかUIというのはソフトウェアの中でめちゃめちゃ柔らかい部品、というのがここで効いてきて、「新着とランキングは基本的に同じ表示なんですけど、ランキングのほうではランクがアップしたかダウンしたかのアイコンを表示してほしいんですよね〜」とか言われたり、「今見てるページが新着かランキングかわかるように、こことこことこことここの色をページによって変えたいんだよね」とか言われたりすることは想像できますね。でもここでテンプレートがパーシャルになっちゃってたりすると、共通化したテンプレートにif type == :ranking みたいな分岐持たせることになったりしちゃいます。そうすると、それってもう共通化の意味ないよね〜状態になることが結構あります。

そんなわけで、テンプレートの共通化はかなり慎重にやったほうがいいと思います。「たまたま同じような表示であるのか」それとも「将来にわたって同じ表示なのか」っていうのはなかなかわかんないものですから。

じゃあどういうときにパーシャル使うべきかっていうと、複数ページで共有されるフッターとか、ウィジェット的なやつとか、グローバルヘッダーとか、そういう部分はどんどんパーシャルにしちゃえばいいと思います。こういうのは全ページで同じであることが(基本的には)保証されてたほうがいいんで。

追記

共通にしといて、if文とかで分岐させないとだめになったらそのタイミングでバラすというのも手だとは思います。ただ、複数人で開発とかしてると設計の変更って分岐追加するよりMP消費するから、どうしても最初の設計にひっぱられがちになるというのは感じてて、今回の例のように関心がそもそも違うみたいな場合は別にしちゃうほうが筋が良さそうだなーと思います。

スロークッカーとかいうぐう有能調理器具wwwwwwww

はじめに

プログラマツイッターユーザの間では、誕生日とか退職とか就職とか引っ越しとか結婚とかそういう節目のタイミングでアマゾンの欲しいものリストを公開しプレゼントを請うという文化が存在します。不肖私もこのたび 2月13日に無事31歳の誕生日を迎えることとなりまして、浅ましくも欲しいものリストを公開したところ、ハリボのグミが1.4kgのほか、ウイスキーが約1.5リットル、KORGのクリップチューナー、アイカツのCD、さらには大物としてスロークッカーという調理器具を友人各位からいただきました。本当に友人に恵まれていて感謝以外の感情がない。各位におかれましては必ず誕生日にwishlistを私宛に送るように。恩に報いる所存です。

f:id:nkgt_chkonk:20150214125536j:plain

amazonの箱のチョイスがおかしい様子です

f:id:nkgt_chkonk:20150214131542j:plain

ゴールデンベアとハッピーコーラです

f:id:nkgt_chkonk:20150214125956j:plain

日本が誇るウイスキー、白州です。

f:id:nkgt_chkonk:20150214181156j:plain

Signalize!とカレンダーガールが収録されています。カレンダーガールは名曲中の名曲です。

f:id:nkgt_chkonk:20150215172410j:plain

癖のあるウイスキー好きにとっての定番、ラフロイグです。

f:id:nkgt_chkonk:20150215172348j:plain

スロークッカーだ!!!!!!!!!!!!!!!!!!!!

ところで、このスロークッカーという調理器具(写真ではダンボールに入ってるけど)はどういうものかというと、簡単にいうと「電気の力で弱火状態をずーっと保持してくれる君」なのです。ものによってはタイマー付きで、時間が来ると勝手に電源切ってくれます。

これ、いろいろとうれしいんですよ。ガスの力で弱火を保持しようと思うと、結局それって火を扱ってるのでそうそう簡単にキッチンを離れることができません。たまに火の様子見に行ったりしないとだめだし。あと、コンロを一口占領しちゃうのも困りものですよね。とくに「弱火でじっくり」系の料理ってすごく長い時間コンロを占領しちゃうので、なにかと困ります。そこで考え付くのが低温調理の雄としてよく挙げられる炊飯器ですが、炊飯器も長時間占領されちゃうとお米が炊けないという問題があります。そこでスロークッカーの出番になるわけです。スロークッカーによってわたしたちは、コンロのことも炊飯器のことも火の元のことも気にせず煮込み料理を遂行する自由を手にいれるのです。

誕生日にスロークッカーをいただいたのが本当に嬉しかったので、急遽スペアリブを買ってきて、こいつを煮込んでみることにしました。以下その工程であります。

スペアリブをホッロホロにしてやろう

f:id:nkgt_chkonk:20150216004144j:plain

はい。スペアリブを買ってきました。ちょっとお安くなっていてお得ですね。新潟市に特有のスーパーマーケットである清水フードのマークが見えて住所が特定されかねない気がしますが気にせずいきます。

f:id:nkgt_chkonk:20150216004325j:plain

表面に焼き色をつけるという工程を行っているところです。

f:id:nkgt_chkonk:20150216004928j:plain

肉を煮込むときにはまず普通に茹でてアクを取り除きましょう。

f:id:nkgt_chkonk:20150216010015j:plain

こちらはアクを取り除いたあと茹で汁を捨て、煮汁とともにスロークッカーに投入した様子です。煮汁は酒、砂糖、みりん、醤油、しょうがを適当にお湯にぶっこんだものです。写真を見るとわかるかと思いますが、パーソナルコンピュータで仕事の進捗を出していると、隣でスロークッカーが自動的に調理の進捗を出してくれるというCI(継続的インテグレーション)体制を敷くことが可能となっております。(PCとの距離が近すぎて熱が危なかったのでこのあとちょっと場所を移動しました)

f:id:nkgt_chkonk:20150216030548j:plain

スロークッカーが自動的に肉をビルドしている様子です。

f:id:nkgt_chkonk:20150216053502j:plain

ビルドが無事成功した様子です。

f:id:nkgt_chkonk:20150216053526j:plain

箸を入れたら、全く力を入れていないにもかかわらず骨から肉が分離されました。こういうのを骨と肉の疎結合と言います。プログラムの設計において非常に重要な概念ですね。

f:id:nkgt_chkonk:20150216053533j:plain

こちらが成果物です。控えめに言って最高にうまい。

前回の「タイ風豚の角煮」をこれで作るのも良さそう。

まとめ

  • 誕生日にアマゾンの欲しいものリスト公開したらみんなの愛が最高にうれしかった
  • なんとそのなかにスロークッカーとかいう有能すぎる調理器具が!!
  • 試しにスペアリブを煮てみました。
  • スロークッカーはCIツールである
  • 骨と肉の疎結合を実現することも可能である。

誕生日プレゼントをくださったみなさん、ほんとうにありがとうございました。

タイ風角煮考

ここ最近角煮が話題かどうか知りませんが角煮です。

角煮ですが、うまいし簡単に作れるのですが時間がかかります。とはいえ、タイ風角煮なら雑に作ってもうまいという知見があるのでシェアします。

まず、角煮が硬くなるのは調味料で煮る、そのタイミングです。このタイ風角煮はその工程をまるっと無視して、タイ風のたれをかけて食べるものなので、雑に作ってもほろほろの角煮が出来上がります。ただし圧力鍋は必須です。圧力鍋は角煮に限らず煮込み料理がはかどりまくるのでないひとはこの機会に買いましょう。

材料は以下のとおりです。

下ごしらえ

豚バラブロックは適当なサイズに切っておきます。しょうがは皮をむいて(皮は捨てない)、すりおろしておきます。みじん切りでもいいです。

豚バラを雑に圧力鍋で煮る

豚バラブロックを適当なサイズに切って、しょうがの皮の部分とネギと一緒に圧力鍋で雑に煮ます。私はいつも時間は適当にやってますが適当な時間煮込んだだけでも十分うまいです。

別の鍋でキャベツを茹でる

豚バラは結構油がしつこいので、キャベツと一緒にたべるといいでしょう。キャベツは千切りでもいいんですけど、この豚の角煮の場合茹でた方が合う気がします。

タイ風たれを作る

小鍋に、トムヤムクンのもとを適当な量(わたしはいつも計ってないです)と、豚を煮た煮汁をおたま一杯分くらいと、すりおろしたしょうが、ナンプラー、レモン汁を入れて煮詰めます。お好みでパクチーも入れるとよいでしょう。ここの味は好みの問題なんで、それぞれ好きな量入れれば良いです。わたしはいつも味を見ながら雑に投入してます。

トムヤムクンのもとですが、わたしは高円寺北口にある「東京屋」とかいう怪しげな店で買った瓶づめのやつをいつも使っています。

この店です https://www.google.co.jp/maps/@35.705495,139.649401,3a,75y,328.63h,99.9t/data=!3m4!1e1!3m2!1sHr6DitUq-jbcCeGRp9FAJw!2e0

このタレはけっこう万能で、どんなものでもタイ風の味付けにできる優れものなので、トムヤムクンを作らないとしてもトムヤムクンのもとを常備しておくと良いでしょう。いつでも手軽にタイっぽい味が楽しめるのでおすすめです。

盛り付ける

茹でたキャベツを持ったお皿に、味のついていない豚の角煮を盛り、その上からたれをこれでもかというくらいにかけます。その上にお好みでパクチーを散らして完成。

雑に作ってもいつもおいしくできるし、妻や友人たちの評判も良いし、ごちそう感のあるメニューですので、ぜひ大量に作って家族や友人たちと大量に食べてください。