JavaScriptでの非同期関数合成

Unserscore.jsや互換ライブラリのLo-Dashを使うと関数合成が出来ます。
複数個の関数があって、関数を呼び出した結果を使って関数を呼び出して…っていうのを1個の関数にします。
ドキュメントの例を見れば分かるかと。


簡略化のために関数合成の対象になる関数を1引数・戻り値ありの関数とします。
これを非同期処理をする関数に当てはめるとcallbackを含む2引数・戻り値なしの関数が当てはまるでしょう。
この場合のcallbackは1引数の関数とします。

まず、logを出力するcallback関数を定義しましょう。

function log(result){
  console.log(result);
}

次にcallbackを含む2引数・戻り値なしの関数を定義します。別に非同期処理はやっていないです。

// 1を足す
function add1(callback,arg){
  callback(arg+1);
}

// 2をかける
function mul2(callback,arg){
  callback(arg*2);
}

実行してみましょう

add1(log,4); // 5
mul2(log,5); // 10

結果を引き回す形で呼んでみましょう。

add1(function(result){
  mul2(log,result);
},4); // 10

呼び出しが深くなるとcallbackのネストが段々鬱陶しくなります。

add1(function(result){
  mul2(function(result){
    add1(log,result);
  },result);
},4);

Function#bindによる部分適用に置き換えます。

add1.bind(null,mul2.bind(null,log))(4); // 10

べた書き部分を汎用にしてみます。

(function(){
  var slice = Array.prototype.slice;
  var funcs = slice.call(arguments);
  return funcs.reduceRight(function(prevResult,current){
    return current.bind(null,prevResult);
  })
})(add1,mul2,log)(4);

今回やりたいことは、callbackを含む2引数・戻り値なしの関数をn個合成してcallbackを含む2引数・戻り値なしの関数にすることなのでこれは違います。
で、こうします。

function noop(){}
function composeAsync(){
  var slice = Array.prototype.slice;
  var funcsOrig = slice.call(arguments);
  return function (callback,arg) {
    var funcs = funcsOrig.slice();
    funcs.push(callback || noop);
    funcs.reduceRight(function(prevResult,current){
      return current.bind(null,prevResult);
    })(arg);
  };
}
実行してみましょう
composeAsync(add1,mul2)(log,4); // 10
composeAsync(add1,mul2,add1)(log,4) //11

「callbackを含む2引数・戻り値なしの関数をn個合成してcallbackを含む2引数・戻り値なしの関数にする」ので合成した関数も合成対象にできます。

var composed = composeAsync(add1,mul2);
composeAsync(composed,composed)(log,4);

この縛りでは合成対象の関数の汎用性に問題があるという場合は、
汎用性のある多引数の関数を定義して部分適用したものを合成すればいいと思います。

packerで圧縮されたJavaScriptのdebug方法

minifyされたJavaScriptは各種開発者ツールのdeminifierを使えば整形された状態でdebug出来ますが、packerで圧縮されたJavaScript(以下packed JS)はどうかという話。
Google Chromeの開発者ツール前提で話をするので他のブラウザについては誰か調べてみてください。
packed JSをdebugする際の壁はそれがevalで実行されることですが、Google Chromeの開発者ツールではevalで実行されたJSもdebug実行出来ます。
問題はどうやってdebug実行に持っていくかですが、globalから辿れる何らかのAPIがあるなら呼び出し元でブレークポイントを貼ってstep intoすれば良いです。
その場実行されるようなJSの場合はそれもできないので工夫が必要です。
以下は古いjQueryを使った例でそのような工夫は本来不要ですが、その場実行されるJSに対して使えるテクニックです。

その場実行されるpacked JSをdebug実行する(Google Chrome開発者ツール)

ターゲットとなるpacked JSが読み込まれる前に何らかの手段でJSを読み込めるようにしましょう。
CocProxyあたりで差し替えられるようにすれば良いと思います。
そこで、ターゲットの中で使われているであろうbuiltin methodを差し替えるJSを読み込ませ実行します。
失敗したら使われていそうなmethodを変えてやっていきましょう。
元と同じ動作でdebugger statementが仕込まれたmethodに差し替えます。

(function() {
  var proto = String.prototype;
  var x = proto.replace;
  proto. replace = function() {
    debugger;
    return x.apply(this, arguments);
  };
})();

packed版jQueryを読み込ませてみましょう。

s=document.createElement('script');s.src='http://jqueryjs.googlecode.com/files/jquery-1.2.6.pack.js';document.body.appendChild(s);

早速debugger statementでbreakされますが、

step outするとpacked JS自体の実行だと分かるのでどんどん実行してしまいましょう。

さて、ここからが本番です。scriptタブの下部の「{}」をクリックして有効(青)にしましょう。これが標準で使えるdeminifierです。
適当に以下のようなコードを実行して、packed版で読み込んだjQueryのコードを動かしましょう。

$('<div>');


step outすると整形されたターゲット(jQuery)のコードが見れます。

以降はscriptタブ上部のソース選択で(program)というものがあるのでその中でconsoleから実行したものではない方を選べばいつでも整形されたターゲットのコードが見れますし、以下のように好きなところにブレークポイントを貼ってdebug実行することも出来ます。

MacHGでのrebase onto Mercurial Advent Calendar 2011

partake.inの12日目は文殊堂がお送りします。
深夜35時などという遅くに申し訳ありません。
今日はSourceTree無料キャンペーンによって存在意義が危うくなった感のあるMacHGも得意な箇所は凄いんだよ、ということで
MacHGでrebase ontoをやるお話です。

Mercurialのrebase

rebaseというとGitのコマンドという印象が強いですが、
Mercurialでもビルトインのextensionを有効化するだけで使用できます。
話の都合上ついでにいくつか有効化しておきましょう。

[extensions]
rebase =
mq =
graphlog =

rebase onto

さて、今日お話しするのはgit rebase --onto相当のことです。
まず以下のglogを見てください。
このmami branchから分岐しているmado branchをhomu branchから分岐するように改変します。

$ hg glog
@  changeset:   3:09876c6c806e
|  branch:      homu
|  tag:         tip
|  parent:      0:663f5d36f850
|  user:        monjudoh <monjudoh@gmail.com>
|  date:        Tue Dec 13 09:51:41 2011 +0900
|  summary:     branch homu
|
| o  changeset:   2:917591f587ee
| |  branch:      mado
| |  user:        monjudoh <monjudoh@gmail.com>
| |  date:        Tue Dec 13 09:44:03 2011 +0900
| |  summary:     branch mado
| |
| o  changeset:   1:eaa1adaa875b
|/   branch:      mami
|    user:        monjudoh <monjudoh@gmail.com>
|    date:        Tue Dec 13 09:42:29 2011 +0900
|    summary:     branch mami
|
o  changeset:   0:663f5d36f850
   user:        monjudoh <monjudoh@gmail.com>
   date:        Tue Dec 13 09:41:46 2011 +0900
   summary:     first commit

とりあえず--sourceと--destを指定してみるとただのmergeになってしまいます。

$ hg rebase --source mado --dest homu
saved backup bundle to /Users/monjudoh/Dropbox/example/.hg/strip-backup/917591f587ee-backup.hg
$ hg glog
@    changeset:   3:4f7d2a838aa9
|\   branch:      homu
| |  tag:         tip
| |  parent:      2:09876c6c806e
| |  parent:      1:eaa1adaa875b
| |  user:        monjudoh <monjudoh@gmail.com>
| |  date:        Tue Dec 13 09:44:03 2011 +0900
| |  summary:     branch mado
| |
| o  changeset:   2:09876c6c806e
| |  branch:      homu
| |  parent:      0:663f5d36f850
| |  user:        monjudoh <monjudoh@gmail.com>
| |  date:        Tue Dec 13 09:51:41 2011 +0900
| |  summary:     branch homu
| |
o |  changeset:   1:eaa1adaa875b
|/   branch:      mami
|    user:        monjudoh <monjudoh@gmail.com>
|    date:        Tue Dec 13 09:42:29 2011 +0900
|    summary:     branch mami
|
o  changeset:   0:663f5d36f850
   user:        monjudoh <monjudoh@gmail.com>
   date:        Tue Dec 13 09:41:46 2011 +0900
   summary:     first commit

Gitのrebaseのようにやるには--detachを付けます。
しかし、mado branchが消えてしまいました。あんまりだよ、こんなのってないよ。

$ hg rebase -s mado -d homu --detach
saved backup bundle to /Users/monjudoh/Dropbox/example/.hg/strip-backup/917591f587ee-backup.hg

$ hg glog
@  changeset:   3:15acace3840a
|  branch:      homu
|  tag:         tip
|  user:        monjudoh <monjudoh@gmail.com>
|  date:        Tue Dec 13 09:44:03 2011 +0900
|  summary:     branch mado
|
o  changeset:   2:09876c6c806e
|  branch:      homu
|  parent:      0:663f5d36f850
|  user:        monjudoh <monjudoh@gmail.com>
|  date:        Tue Dec 13 09:51:41 2011 +0900
|  summary:     branch homu
|
| o  changeset:   1:eaa1adaa875b
|/   branch:      mami
|    user:        monjudoh <monjudoh@gmail.com>
|    date:        Tue Dec 13 09:42:29 2011 +0900
|    summary:     branch mami
|
o  changeset:   0:663f5d36f850
   user:        monjudoh <monjudoh@gmail.com>
   date:        Tue Dec 13 09:41:46 2011 +0900
   summary:     first commit

branchを保持してrebaseしたいのであれば--keepbranchesを付けましょう。そろそろoptionの長さにイライラしてきていると思いますが、ひとまずちゃんとrebase ontoに成功しました。

$ hg rebase -s mado -d homu --detach --keepbranches
saved backup bundle to /Users/monjudoh/Dropbox/example/.hg/strip-backup/917591f587ee-backup.hg

$ hg glog
@  changeset:   3:1548007f5eaa
|  branch:      mado
|  tag:         tip
|  user:        monjudoh <monjudoh@gmail.com>
|  date:        Tue Dec 13 09:44:03 2011 +0900
|  summary:     branch mado
|
o  changeset:   2:09876c6c806e
|  branch:      homu
|  parent:      0:663f5d36f850
|  user:        monjudoh <monjudoh@gmail.com>
|  date:        Tue Dec 13 09:51:41 2011 +0900
|  summary:     branch homu
|
| o  changeset:   1:eaa1adaa875b
|/   branch:      mami
|    user:        monjudoh <monjudoh@gmail.com>
|    date:        Tue Dec 13 09:42:29 2011 +0900
|    summary:     branch mami
|
o  changeset:   0:663f5d36f850
   user:        monjudoh <monjudoh@gmail.com>
   date:        Tue Dec 13 09:41:46 2011 +0900
   summary:     first commit

ただ、id:monjudoh:20110801:1312179612 で書きましたがMercurialで歴史改変を行う場合は、

元のchangesetsを保持するoptionを付けて行い、正常に出来たことを確認した上で古い方の歴史をstripで削除する

のをお勧めします。


今回の場合だと、-keep optionを付けて実行し、確認後に古いほうをstripすることになります。

$ hg rebase -s mado -d homu --detach --keepbranches --keep

正直かったるいですね。
思うにrebase ontoという操作はコマンドラインで行うには複雑すぎる操作なのではないかと思います。
本家のgit rebase --ontoはoptionこそ少ないですが、3引数の順番とか覚えてられません。

MacHGの強力な歴史改変サポート

MacHGではrebase,mq(stripのみ),collapse,histeditといった歴史改変系のextensionを強力にサポートしています。
このような履歴になっている状態で、右クリックからRebase changesets…を選択し、

移動元と移動先をグラフからそれぞれ選択し、必要に応じて--keepbranches --keep相当のチェックボックスを選択しRebaseボタンを押すだけです。

無事元branchを保持したrebase ontoが出来ました。

stripもこのUIならまず誤爆しないでしょう。

まとめ

MacHGの歴史改変操作のUIはとても素敵です。
皆様も活用してみてはどうでしょうか。
後、よくわかってない奴は歴史改変とかすんな。

では13日目の @ponkotuy さんよろしくお願いします。

LT概要「GitとMercurialのリポジトリ構造の違いと歴史改変について」SCMBootCamp in Tokyo

SCMBootCamp in Tokyo 開催しました。KPT公開。 - うさぎ組にて手ぶらLTをしたので資料はないが、内容を軽くまとめておく。

GitとMercurialの比較

Git Mercurial
リポジトリ commit objectのグラフと、branchのHEAD,tagなどの参照で出来ている。 commit objectのグラフだけで出来ている。
歴史改変サポート デフォルトであり。 デフォルトではなし。extensionが必要。
歴史改変 新しいcommit objectグラフを作成し、参照を古いHEADから新しいHEADに移す。表面上要らない歴史の削除として使われるresetはHEADの移動のみを行う。 新しいcommit objectグラフを作成し、古いcommit objectグラフをリポジトリから除去する。要らない歴史の削除として使われるstrip(MQExtension)は対象のcommit objectグラフをリポジトリから除去する。
改変前の歴史の参照方法 reflogを使えばGCされる前のcommit objectは作成時昇順で全部見える。「今の私にはね、過去と未来の全てが見えるの。かつてあった宇宙も、いつかあり得るかもしれない宇宙も、みんな」 基本的に見ることは出来ない。
改変失敗時の復旧 reflogを見て改変前にHEADだったcommit objectをreset --hardすれば良い。 bundle backup等があれば失敗した改変後commit objectグラフをstripで除去後、import出来る。

Mercurialで安全に歴史改変するコツ

ある特定のリポジトリの中に閉じた話に限定すれば、Mercurialの歴史改変はGitのそれに比べてずっと危険。
回避したい場合は、元のchangesetsを保持するoptionを付けて行い、正常に出来たことを確認した上で古い方の歴史をstripで削除すること。

伝えたかったこと

GitとMercurialの機能には表面的に似ているものは多いし、ポリシーの違いもextensionによって乗り越えられるように見えるが、
内部はぜんぜん違うのだから、表面的に似ているのと本質的に似てるのは違うって事を自覚し、気を付けること。
特にGitとMercurialのコマンド対応表といったものを鵜呑みにするのはやめること。

勉強会での内輪ネタについて

内輪ネタはただの+α

最近の話だとRubyKaigi他での魔法少女まどか☆マギカネタとRubyKaigiでのたこ焼き仮面ネタあたりか。
内輪ネタって知ってたら面白い、知らなかったらそれはそれで困りはしないってものであるべきだと思ってて、
つまり、+αであるべきだと思っている。
例えば、スライドだったら内輪ネタが入ってるスライドを除いても、
もしくはスライドの中から内輪ネタの部分を除いても、意味が通るならOKだと思うわけです。
例えば、「GCアニメ・まどか☆マギカによる空前のGCブーム」なんてネタが何のことか分からなくたって、
GCのないRubyっていう本題を理解するには別に何も問題ないわけです。

問題なのは同調圧力

内輪ネタはただの+αなんで別に"知らなくてもいい"んです。
普段、「〜を知っていて当然」みたいな同調圧力をかけられているので、
自分の知らないネタを当たり前のように使われるとそういう同調圧力をかけられていると思ってしまうのかもしれませんが、
IT系勉強会等でそんな同調圧力をかける者なんて、誰もいない、と思います。いたら私が許さない。

自分で自分に同調圧力をかけてませんか?

自分以外の人達(本当は自分以外全部じゃなくてせいぜい何割かの人達)に一体感を感じたとき、
自分がその中にいないのをまずい事だと思い込んでませんか?
勉強会なんだし興味深い話が聞ければ、利用価値のある知識が得られればいいじゃないですか。
そう割り切ってしまえば色々楽になると思うよ。

関連商品

魔法少女まどか☆マギカ (1) (まんがタイムKRコミックス フォワードシリーズ)

魔法少女まどか☆マギカ (1) (まんがタイムKRコミックス フォワードシリーズ)

魔法少女まどか☆マギカ (2) (まんがタイムKRコミックス フォワードシリーズ)

魔法少女まどか☆マギカ (2) (まんがタイムKRコミックス フォワードシリーズ)

魔法少女まどか☆マギカ (3) (まんがタイムKRコミックス フォワードシリーズ)

魔法少女まどか☆マギカ (3) (まんがタイムKRコミックス フォワードシリーズ)

clone元以外のリポジトリとの同期

  1. remote repository追加
    • remote repositoryをgit remote addで追加。
    • GitTowerの場合Refs->Add New Remote Repositoryから追加。
  2. remote branch情報の取得
    • remote repositoryに対してgit fetchで、remote repositoryのremote branch一覧を取得出来る。
      • cloneはfetchを含んでいるようなものなので不要なんだと思う
    • GitTowerの場合remote repositoryを右クリックして、Fetch from Remote Repository

これでrepositoryName/branchName でremote branchを使える。

友達を作る 新卒準備カレンダー 2011春

新卒準備カレンダー 2011春 : ATND

お前だれよ

普段全然気にしてないのですが簡単な引き算が間違っていなければ社会人7年目らしいです。
去年くらいまではWebアプリを作る仕事がメインで、いろいろな言語でサーバサイド書いたり、
たまにクライアントサイド担当としてJavaScriptをモリモリ書いたりしていましたが、
今年に入ってからはiOS仕事がメインでObjective-C書いたりやっぱりJavaScript書いたりしてます。
マイテーマはeventによるUIコンポーネントの作成です。


新社会人もそうでない人も知っておくと上手くやっていける表裏の二つの話をしようと思います。

表-友達を作る

自分が属している組織の内外を問わず友達を作るのはとても大切です。
気を付けないといけないこととしては、愚痴る相手としての友達をたくさん作ってもしょうがないということです。


どこかしらでいいので自分から見て尊敬できる部分を持った友達を作りましょう。
既に周りにそういう人がたくさんいるなら幸せです。
もし、そうでないなら行動範囲を広げて徘徊してでもそういった友達を作っていきましょう。
もちろん、相手にとって自分がそうであるように努力するのも大事です。


後は、自分と同年代だとか自分でかってに決めた同程度だとか、
そういった縛りを作ってしまわないのも大事かもしれません。


そういった友だちができるとどんな良い事があるのか

色々なことに興味を持つきっかけになります

自分とは違ったことをしている人から、こんなことをしていて楽しいだとかいった話を聞くと興味が広がります。
難点としては流されると興味が発散するので自分をしっかり持ちましょう。

知の高速道路に乗れます

何かについて知りたい時、どうやって学べばいいか詳しい人に聞けるというのは効率がいいです。
難点としては興味の発散と下手に合わさると知識習得に押しつぶされるので自分をしっかり持ちましょう。

張り合いが出ます

周りが優秀な人ばかりの組織に所属している幸せな人を一端対象から外すと、
ちょっとやる気出して頑張った人は、周りの低レベルさにやる気を失ったり、
やさぐれたりといったことがままあると思います。
"周り"が組織内に限定されなければそれもないでしょう。

転社・転職に有利です

自分が何が出来てどういうタイプの人間なのかをよく知っている友人から
勧められた会社と接触してみるのは一から会社を探すより効率がいいでしょう。
また、人に信頼されている友人から推薦を受けることが出来れば、
自分も採用側も余計なコストを掛けずに済むといったこともあります。

裏-義理を疑う

会社を辞める時とかの話なので、こちらは今じゃなくてその頃になってから読んでもいいでしょう。


まー、私は自分では比較的義理堅いと思ってるんですが
前の会社の人には大変な薄情者とか思われてるんじゃないかなーなどと。
義理堅いのはいいことで+の義理には×たくさんした方がいいと思いますが、
0にはいくつかけたって0です。


折角のチャンスを後ろ髪引かれてフイにするとかそうなっちゃいそうな場合は、
義理が本当は0じゃないのか考えてみるといいでしょう。
自分がいなくなって残された人はどうなるんだ系の話は大抵0だと思うので、
ちゃっちゃと新天地に旅立ってしまいましょう。


残していく中に友達がいたって何も問題ありません。
その友達が何が出来てどういうタイプの人間なのかをよく知っているんだから、
合っていると思う会社をどこか紹介するも良し、
新天地で人から信頼せれるようになって推薦するも良し。
どっちにしろ、後ろ髪引かれて現状維持するより友達にとってもいい結果になると思いますよ。

おまけ

ああそういえば、仕事についての考え方とかは全然書いてませんでした。
自分の見方とお客さんの見方は違う、もしくは違ってもいい - 文殊堂でも読んでおくといいんじゃないでしょうか。

返答とか

id:zorio「自分がいなくなって残された人はどうなるんだ系の話」はあんまりこだわらない方が良いね。絶対何とかなってる。

「何とかなっている」じゃなくてならなくても義理は0のでどうでもいいという話です。