Template Method Patternがマッチする場合、マッチしない場合

リーダブルコードとハードバップコード

まずはこのおもしろツイートをご紹介させてください。

このツイートを某所で紹介したところ、「たしかに紛らわしい」「"よりよいChordを書くためのシンプルで実践的なテクニック"でもふつうに意味が通じるのがすごい」という声のほかに、「ワーオ,君の書くコードはまるでハードバップのようだ(喧嘩になります)」「 $_[++$_]->%* これとかハードバップ感ある」という声が上がったため、ハードバップのようなコードを書いて見ました。Perlで実行してみましょう。

s///;sub bop {


'$_[++$_]->%* is_a', +{ 'hard', 'bop' }, 'code'


} sub hard { print


$_[++$_]->%*


}

hard bop

このコードは、実行すると "hardbop" を出力します。わたし程度ではこのくらいのヒップさが限界ですが、もっとヒップにできそうな気がするので、ぜひあなたのバップも聴かせてください!ちなみに、このヒップなコード片( $_[++$_]->%* )を書いたのは id:moznion です。彼こそがヒップ。

アジリティを高めるための設計

この発言でもうすべてを語っているんだけど、たとえばDDDの流行(?)によりレイヤー化アーキテクチャ、そこにDIPによる依存関係の整理を加えたオニオンアーキテクチャやヘキサゴナルアーキテクチャ、それらを統括した概念としてのクリーンアーキテクチャなどへの関心はますます高まっているような気がする(要出典)。それ自体は喜ばしいことだとぼくは考えているのだけれど、一方で、初学者などがいきなり「ふんふん、これが"正解"か」と思って、その「構造だけ」を真似してみるという不幸な事故があとを絶たない、というような状況がいまあるような気がしている(要出典)。

そこに存在するのは、「こういう構造だよ」とか「こうやるんだよ」ということだけだ。重要なのは、そうではなくて「こういう構造にすることで、こういう場合に変更に強くなるでしょ。だから、その変更があることが予想できるときにはこうしたらいいよね」くらいまで理解をすることだと思う。

ぼくたちはプログラミングパズルを解いているわけではなくて、実際には「プログラミングを使って問題を解決しようとしている」はずだ(超絶技巧系の遊びだとまたちょっと話は違うんだけど、それはそれとしておく)。そのときに「良い設計」というのは、「新しい問題が出てきたときにすぐにそれに対応できるアジリティの高い構造になっていること」(この「アジリティ」という考え方については、moznion氏と話をしたことに多くを依っている)だと思う。

抽象的な話になったので、例を出したい。

ツイートにもある通りだけど、たとえば「なぜMVWhateverにするのか」というのは、簡単だ。「ViewとModelが密結合したコードだと、"見た目の変更"のときにデータやロジック、状態管理が邪魔をして簡単に見た目の変更ができない」とか、逆に「ロジックを変えようとしたら見た目が壊れちゃうから簡単にロジックの変更ができない」というような、「新しい問題にぶち当たったときにアジリティを落としてしまうような問題」を解決するために役立つパターンだからだからだ。

そして、この「UIとそれ以外を分けないでいるとアジリティ落ちる」問題は、どんな領域のアプリケーションでも「ありがち」なものだらか、「UIとそれ以外を分けるパターン」は広く使えるパターンとして「とりあえず選択しておいてもペイする」ようなパターンだったりする。けど、そうじゃないものもある。

たとえばDDDの戦術的設計(EntityとかValueオブジェクトとかあれね)とかは、本当にそれを選択しないとあとあとアジリティを落としてしまうような問題を引き起こすのかどうか、一旦考えてみてもいいと思う。単なるCRUDの簡単なTODOアプリケーションに対して、いちいちEntityやValueオブジェクトを考えることが、どれだけ後々のアジリティに影響するだろうか。むしろ、それは過度な分割を生み、後々のアジリティを落としてしまうのでは?

もちろん、「戦術的設計をいざやるぞ!ってときに、いきなりやってもできなそうだから、練習のためにやってみます」ならわかるし、そういうのをやってみることはとても価値のあることだと思う。一方で、それが後々もメンテするようなアプリケーションであるならば、「この構造を選択したのは、後々こういう変更があるときにすぐに変更できるアジリティを確保したいからです」を答えられないようなら、「XXアーキテクチャでいきます!」「XXパターンで行きます!」を選択するのはやめたほうがいい。

さて、「なぜその構造にしたの?」に対して「後々こういう変更があるときにすぐに変更できるアジリティを確保したいからです」を言えたとして、もうひとつ考えるべきことがある。それは「ほんとうにその変更って起こる可能性高いの?」ということだ。「起こらないはずの変更」に備えた部分は、「誤った構造化」となって、起こらなかった問題とは異なる「起こった問題」にぶち当たったときの負債となる(見に覚えがありませんか?ぼくは腐るほどあります)。

と、じつはぼくはサラッといまとても難しい話をしていて、これは要するに「未来を予測せよ」と言っているのと変わらない。未来を予測しなくては、「起こる問題」と「起こらない問題」はわからないからだ。しかし、そう簡単に未来が予測できればソフトウェア開発ってのはこんなに難しくないわけで、このあたりは本質的にソフトウェア開発の難しさであると思う。

少なくとも暫定的な話としては、「ある構造を選ぶときには、"こういう未来の問題に対してアジリティ高く反応するため"を言えるようにしよう」「予測できる未来(見た目は絶対かわるよね)(最初は予備校向けのサービスとして売出しますが、そのあと学校に攻め込んでいく予定となっています!)(このアプリやサービスが解決しようとしてる問題領域はめちゃめちゃでかいからそういうレベルのでかさに対応できないとだめだな)については、それに対応する構造にしておこう」「予測できない未来は予測せず、"それに備えた構造"は負債化する可能性が高いのでやめよう」というあたりに結論をおいておきたい。

で、最初に戻るわけです。「アジリティ高くすることが重要なわけで、UI変更のアジリティ高くするためにPDSを意識したり、モデルに関してもドメイン層とインフラ層を分離したりするわけで、"その分離によってどういう変更に対するアジリティを高めたいのか"を説明できないならやるな。」

暇にかまけて

暇。いい響きだと思う。声に出してみる。ヒマ。とても良い。「ヒ」の音でちょっと勢いづいた感じがするも、マ行の発音をするためには一度唇を閉じなければいけないこの感じが、いかにも暇という言葉の甘美な後ろめたさを表しているように思う。

これがもしも「ヒマ」ではなくて「ヒカ」とかだったら、あまりに印象がシャープにすぎる。暇にかまけてよしなし事をそこはかとなく書きつくるようなことは許されないような、そんな感じがする。逆に「ニマ」くらいまでいってしまうと、今度はほげほげとしすぎていて、なんとなく忙しい世間から取り残されるような後ろめたさが感じとれないではないか。

この、シャープなわけでもなければ、ほげほげとし過ぎているわけではない、そんな捉えどころのない「暇」というものについて、少し考えてみたい。

ぼくが「暇」について考えるときにいつも頭に浮かぶのは、スチャダラパーの「暇の過ごし方」という曲だ。これは WILD FANCY ALLIANCE という名盤に収められた曲で、これを「要約」してしまうのは非常に気がひけるというか、それこそ「要約」のような行為はどちらかというと「暇を許容できない」側の好むようなもののような気がするのだけれど、それはそれとして無理に要約を試みれば、「暇が悪いことみたいに言われるけど、暇ってそんなに悪いことか?」みたいな曲だ。ちなみにぼくは「大仏 ピラミッド 巨乳 万里の長城 この世の多くのデカいものの 発想自体ヒマの賜物」というフレーズが好きだ。巨乳がそこに入る発想どうなってんだよ。その「でん」であれすると、まあこの文章自体も「ヒマのなせるわざ」という感じがしないでもない。ともあれ、この暇ってやつの「一筋縄ではいかない感じ」の一端がこの曲にはあらわれているような気がする。

たとえば数学。ある種の「現実的な」ひとたちからすると、もうなんていうかキング・オブ・暇のなせるわざだと思うんですよ。「それなんの役にたつの?」出たよ「現実的」なひとたちの伝家の宝刀。とはいえ数学は多分まだマシなほうで、たとえば「いやいや、一見単なる数字遊びに見える素数の研究が、じつはコンピュータの暗号のためにすごく役にたってるんですよ」というような話があったりするわけだから。ただ、これもおかしな話で、おそらく多くの数学ファン(数学が得意なわけではないけどそのファン、というのはけっこういると思うのですよ)は、数学が役にたつかどうかなんてほんとはどうでもよくて、単純にそれがそれとして面白いんですよね。役にたつからやってるわけじゃないはず。さらに言うと、ある種の「現実的な」ひとたちからしてみたら、数学のこと考えるのに一日のうちの多くの時間を使うみたいのは「暇だね〜」って言いたくなるような感じだと思うんですけど、数学のひとたちの頭の中は暇どころか数学で忙しかったりすんだからな。わかっとけよそれ。

なんか話があっちこっちに飛んでるけど、そんな感じで「暇」ってやつはなかなか一筋縄でいかないところがあるとおもうんですね。自分自身の話に引きつけて言うと、ぼくはもう子供も生まれていい年したオッサンなのに、音楽がやめられないんです。「そんな暇あったら」って言われたことも一度や二度じゃないですよ実際。「音楽なんかやってる暇あったらXXできるでしょ」「いつまで音楽続けてんの?暇だねー」みたいな。

しかし一方で、いるんですね実際。「そんな暇あったら」って感じで、いわゆる「生産性のあること」を四六時中バリバリやってる大人たちが。そういうひとたちの生き方はたしかに暇ではない。暇の入り込む隙間を無くして無くしてビッシリと生産する人たち。どっちかっていったら「ヒカ」って感じの過ごし方してるひとたち。

そういうひとたちに対して後ろめたさみたいなのを感じてる一方で、「そんな暇あったら」って言われながらも音楽辞められないみたいなぼくはたしかに「ヒカ」でも「ニマ」でもない「暇なひと」なのかもしれないと思う。でもね、いいじゃない、それで、とも思うんですよ。「そんな暇あったら」でバリバリ生きていくの、かっこいいし尊いと思います。けど、「そんな暇」とともに過ごすのもまた、人間に備わった高度な機能だと思うんですよ。だめ?許してくれない?

あなたは「どんな暇」を過ごして過ごして生きていますか?「こんな文章読む暇あったら別のことすればよかった」と思われてなければいいなと思います。

12月なので今年の音楽活動(ソロ音源制作)を振り返る

ぼくは普段自分自身をプログラマやソフトウェア・エンジニアではなく「自称ミュージシャン」(音楽で食えていないので「自称」が取れない)と呼称している(労働という文脈の上ではしょうがなく「ソフトウェア・エンジニア」やプログラマを名乗っているけども)。今年は結構な数の音源をsoundcloudにアップしたんだけど、そんなわけで今年のソロ音楽活動の中で音源制作について振り返りたい。

今年は作った/関わった音源は全部で13曲。そのうち新作が2曲。新曲のうち1曲はインストなので歌物の新作は1曲だけという結果だった。今年結構いろいろ作った気になってたけど、ほとんどがリメイクだったり再録だったりひとの曲のアレンジだったりしていたみたい。習作が2作あって、これは歌詞をつければ歌物として成立する感じなので、やっぱり作詞とボーカルがネックになっていると感じる。

そんな感じで新作が少ないから、というわけでもないけど(いや、だからかもしれない)、今年は作詞、作曲、演奏についてはあまり成長のない一年だった気がする。一緒にやってるバンドメンバーの作詞・作曲能力が非常に高いので、負けないように来年は作家性の面でももう少し真剣にやっていきたい。

一方で、編曲とDTMは今年でだいぶ成長できたように思う。とくにパーカッションの扱いについてはゼロがイチになったというか、一切ノウハウのない状態だったのが、今年ようやく自分の使える語彙の中にパーカッション類が入ってきたのが大きな成長だと思う。自分があまり上手に弾けないから苦手意識のある鍵盤のアレンジについても、だいぶ語彙が増えてきたかなという感じがする。しかしこれはまだまだ課題を感じているので、来年も引き続きいろんな工夫をしてレベルアップしていきたい。ドラムの打ち込みとベース、ギターのアレンジは壁を感じている。去年からあまり変わってないように感じる。ドラムの打ち込みについてはだいぶ壁があるというか、これ以上はどうやってうまくなればいいのか見えてないような感じ。ギターとベースのアレンジは演奏の語彙に依存する部分が大きいので、来年はコピーとかやって演奏の語彙を増やしたほうがいいかもしれない。ミックスダウン、マスタリングに関しても、やっぱり最近の曲になればなるほどまともになっていっている。やっぱりこれも数こなすしかないのかな。ミックスダウン・マスタリング教えてくれるひとがほしい……。

演奏面についてはギター、ベースともに「理想の演奏」にまだ近づいてないというか、自分が弾きたいフレーズに追いついていないのでこればかりは単純に練習するしかなさそう。ボーカルについてはなかなか練習する機会がないのでどうしたもんか、という感じ。歌練習部とか作りたい。歌録りについては「下手を前提に」録る方法が少し成長した。

以下一曲ずつふりかえりです。

38度7分

soundcloud.com

昔、数値海岸というバンド(バンド名は飛浩隆「グラン・ヴァカンス 廃園の天使Ⅰ」 に出てくるリゾート世界「数値海岸コスタ・デル・ヌメロ」が元ネタ)をやっていて、そのバンドでやった曲を再録。アレンジは数値海岸でやっていたときのものを踏襲して少しだけ手直ししたという程度。高熱で朦朧としているときの感じがうまく表現できたのが気に入っている。あとビートルズのGirlをアレンジ上引用してるのもハマってて気に入ってる。いまやってるバンドのメンバーのタダくんに「よくできてる」と言ってもらえたのが嬉しかった記憶がある。ボーカルはひどい。

コーヒー・アンド・シガレッツ

soundcloud.com

これは新作。タイトルはジム・ジャームッシュ監督の同名の白黒映画から拝借。雰囲気から着想を得て、タイトル->メロディとアレンジが同時進行->作詞という順番で作った曲。というかリアレンジのとき以外、だいたいの場合この順序で曲を作っている気がする。アレンジについてはもう少し凝ってもよかったのではないか(とくにドラムとピアノがまだまだ練れていないし、アウトロももうちょっとやりようがあっただろうと思う)という気持ちだが、そういう気持ちが「(demo)」という表記に表れている気がする。これもボーカルはひどい。音楽仲間の友人何人かから「好き」と言ってもらえて嬉しかったので、いつかもっときちんとやって再録したい。

ブラックサンダー

soundcloud.com

tmrrさんという音楽友達の曲をアレンジして演奏して打ち込みして録音したやつ。男声ボーカルはぼくで、女声ボーカルはtmrrさん。アレンジ面で言えばこの一年で一番よくできたのはこの曲だと思う。アレンジャーとしては良い思い出 of the yearという感じ。曲自体の良さに引っ張られてこのアレンジが出てきたので、tmrrさんさまさまという感じである。男声ボーカルはひどい。女声ボーカルは録音慣れしてない上iPhone録音とは思えない出来だと思う。

sketch_2018_01_24

soundcloud.com

習作。ストリングスとホーンでキラキラしたポップスを作る練習をしたやつ。出来上がった瞬間は「なんかダサいJ-POPみたいになっちゃったかなあ」って思ったけど、今聴くとちゃんと狙ったところに落ちてる気がする。サビの感じはLIFEのころの小沢みたいじゃんって思ってちょっと自分で自分の曲を見直した。

Choonie

soundcloud.com

tmrrさんの曲第二弾。アレンジして演奏して打ち込みして録音したやつ。ブラックサンダー!は曲をいただいた瞬間にアレンジが思い浮かんでそのままシュッとDAWに向かって出力した感じだったんだけど、これは結構試行錯誤した記憶がある。おかげでちょっと自分のアレンジの幅が広がったかなという感じがする。これまたおかげさまである。ひとの曲をアレンジするときには、歌詞や曲の世界観をサポートするようなアレンジを心がけているんだけど、試行錯誤したとはいえ最終的にはきちんと歌詞と曲から感じるアンビバレントな感じを表現できたのではないかと思っている。才能のあるひとの作る曲のアレンジを任せてもらえるのは楽しい。アレンジャーとしての良い思い出その2と言えそう。

バターと黒胡椒

soundcloud.com

新作。インスト。「演奏ができるバカがやるセッション」みたいなコンセプトで作った曲。セッションといいつつ全パート打ち込みなんですが。習作も含めた今年の新作の中では一番の出来なんじゃないかなと思う。「ラテンジャズとかジャズファンクとかの雰囲気に挑戦してるロックバンド」みたいな感じですね。ベースソロの後ろでパーカッション隊がバカみたいに騒いでるのが「演奏ができるバカがやるセッション」を体現していて気に入っている。「うるせえ!!ベースソロやってんだろうが!!!」みたいな。この曲作ったおかげでパーカッション隊の扱いにおいて一皮むけたという実感があります。

アンドロイドの恋

soundcloud.com

昔妻と一緒にやっていた「kkum」というバンドでやろうとしてできなかった曲のリメイク。再録ではなくリメイクなのは、アレンジが当時とガラッと変わっていて、歌詞も多少手直ししているから。コード進行とメロディは当時を踏襲している。ベースラインの元ネタはダニー・ハサウェイの「ゲットー」。ギターはスティーブ・クロッパーを、ドラムはアル・ジャクソンを意識した演奏、打ち込みになっている。ドラムソロとピアノソロがお気に入り。結構あの時期のソウルっぽい雰囲気をうまく現代風に持ってこれたのではないかと思っている。バターと黒胡椒で得たパーカッション・ノウハウも生きていて、自分でもこのアレンジは気に入っている。ボーカルはもう少しなんとかならんかったんかな、って気がする。いつかボーカルだけ再録したい気がする

季節

soundcloud.com

たぶん自分の結婚パーティで妻と一緒にやったのが初演。もう7年前とかですね。今から考えるとなんでこんな後ろ向きの歌を結婚パーティでやってんだw。ソロで弾き語りするときの定番レパートリー。自分で歌ってる音源が存在してないので、これ(どれ?)を機に録音したもの。いまのバンドでもやったことあるけど、アコギとベースだけのこれくらいシンプルな構成(けどアレンジはそれなりに凝ってる)のほうが曲に合う気がする。この曲は歌い慣れているのもあってそれなりに聞けるボーカルになっていると思う。

花粉症ビバップ

soundcloud.com

再録。リメイクですらない。前録音したときにまじで花粉症ど真ん中で、鼻声がすぎたので録音しなおしてmixのバランスをちょっと直したもの。あ、ドラムも打ち込みしなおしてるかも。撮り直してこのボーカルかよ……という感じがするんだけど、この曲難しくて歌えないんだよ……。曲自体は気に入っているんだけど、自分では歌えないのまじでなんとかしてほしい。だれがなんとかしてくれるっていうんだ……。だれか歌ってくれるひといるなら歌ってほしいまじで。歌唱のスキルが低くて作曲、編曲のスキルに追いついていないためこういうことがおこるのである。

ゆう[優・融]

soundcloud.com

こだわりのタイトル。これは19才とかそれくらいのときに作った曲の再録。アレンジはほぼ当時のものを踏襲している。コーラスとシンセサイザーのぴこぴこだけは当時のアレンジにはなく、近年足したもの。ベースラインは当時からけっこう考えてたことがわかるけど、全体的にシンプルで、「まだ難しいことできません」って感じがする。この曲は「曲と歌詞が同時進行 -> タイトル -> アレンジ」という順序で作られていて、自分としてはめずらしいパターン。だからなのか、アレンジの力じゃなくて歌詞とメロディで勝負するぞ!って感じになってて、今聴くと気恥ずかしいところもあるんだけど、なんども再録してたりしてなんだかんだ気に入っている曲であることがわかる(他人事かのように)。ギターソロは当時のままだけど、なかなかいいソロやんけ。

sketch_2018_10_07

soundcloud.com

ベースラインの語彙を増やそうと思って作った習作。まじで習作という感じなのでとくにいうことない。ベースラインの語彙は増えた。

Q

soundcloud.com

数値海岸でやってた曲のリメイク。歌詞とアレンジがかなりドラスティックに直されている。それどころかタイトルすら変えられてしまっている。数値海岸でやってるときから「マッドチェスターっぽくしたい」って思ってたんだけど、このリメイクでようやくそのイメージが形になったかなと思っている。普段はあんまりこういう歪んだギターを弾かないので、久しぶりに「ロックギター!」って感じのことができて楽しかった(しかしソロのスケールなどはちょっとロックギターっていうにはひねくれてますね)。こうしてみると、自分の音楽的語彙は大きくフリッパーズ・ギターに依存しているのだなあと思う。フリッパーズ・ギターを通じて知った音楽を掘っていったという自分の音楽の聞き方上、あたりまえなのだけれど……。ボーカルについては、自分のボーカルが下手なことを受け入れた上で、その下手な歌を素材としてどう聴かせるかみたいな部分で少し成長がみて取れる。

これからむかえにいくよ(スガシカオcover)

soundcloud.com

鬱屈した攻撃性とでもいうべきものが、ベースとギターでだいぶうまく表現できたカバーになっているのではないかと自画自賛している。ベースとギターは派手だけど、派手なところだけじゃなくて、Bメロに入っているウィンドチャイムとか1オクターブ下でユニゾンしてるボーカルとか、サビでなってるシェイカーとか、Aメロの一部に実は入ってるカウベルとか、そういう「ちゃんと聴かないと聴こえないけど、あるとないとでは雰囲気に違いが生まれる」みたいな細かい部分をきちんと作り込んでて、「去年だったらこういうことはできなかったよな、今年もDTMとアレンジちゃんとうまくなったな、成長してるな」と1年を振り返って思える出来になったのでよかった。一曲一曲ちゃんと成長してるんやで!!

Scalaの `:+` と `+:` にまつわる話

これはClassi Advent Calendar 2018の3日目の記事です。

Classiでいろいろやってるしんぺいです。最近は「Scala入学式」と称してScalaに入門してもらう社内勉強会を主催しています。

というわけで今日もScalaの話です。

Seqの +::+

Scalaには順序を持ったコレクションの抽象としてSeqというtraitが定義されています。そして、そのSeqの先頭に要素を追加するのに +: が、最後に要素を追加するのには :+ が使えます。

val s = Seq(1, 2, 3)
println(0 +: s) // => List(0, 1, 2, 3)
println(s :+ 4) // => List(1, 2, 3, 4)

Seq は抽象なので、デバッグプリントすると具象クラスであるところの List が表示されますが、List is a Seq なので期待通りです。

一見「演算子」に見える +::+ ですが、じつはこれは Seq のメソッドです。まずはわかりやすい :+ から見てみましょう。

val s = Seq(1, 2, 3)
println(s :+ 4) // => List(1, 2, 3, 4)
println(s.:+(4)) // => List(1, 2, 3, 4)

s :+ 4 と書いた場合も s.:+(4) と書いた場合も同じ結果を返しています。というわけで、じつはscalaでは、object.method(arg)object method argとも書けるのですね。これは :+ メソッドに独特の挙動ではなく、ほかのメソッドでも同じことです。

println("myself".splitAt(2)) // => (my, self)
println("myself" splitAt 2) // => (my, self)

では +: についてはどうでしょうか。

val s = Seq(1, 2, 3)
println(s.+:(0)) // => List(0, 1, 2, 3)
println(0 +: s) // => List(0, 1, 2, 3)

なんと、object method arg という書き方ではなく、 arg method object という書き方になっています!

これはなぜかと言うと、scalaにおいて : で終わるメソッドの場合、dotなしの記法だと arg method object という書きかたになるというルールがあるからです。このようなルールがあるとなぜうれしいのでしょうか。じつは +::+ を同時に使うと嬉しさが伝わってきます。

val s = Seq(1, 2, 3)
println(0 +: s :+ 4) // => List(0, 1, 2, 3, 4)

「先頭に0を、末尾に4を追加したSeqを返している」というのがとてもわかりやすく表現されているのではないでしょうか。こういうようなときに、たしかに arg method objectという書き方ができると嬉しいかもしれません。

+::+とパターンマッチ

さて、+::+ はパターンマッチでも出てきます。

val s = Seq(0, 1, 2, 3)
s match {
 case _ +: xs :+ _ => println(xs)
 case _ => ()
} // => List(1, 2)

先頭要素 +: Seq :+ 末尾要素 で、「Seqの先頭に先頭要素を、末尾に末尾要素を追加Seq」が構築できるのと同じような感じで、パターンマッチで 先頭要素 +: Seq :+ 末尾要素 を使うことでSeqを分解することができています。さきほどの +: と  :+ の正体はメソッドでしたが、パターンマッチ内ではもちろんメソッドでマッチさせることはできません。ではこれらの正体は一体なんなのでしょう。

じつはこれらはobjectとして定義されています。定義を見に行きましょう。

https://www.scala-lang.org/api/current/scala/collection/$plus$colon$.html

https://www.scala-lang.org/api/current/scala/collection/$colon$plus$.html

unapllyが定義された+:というobjectと:+が定義されていることがわかると思います。

パターンマッチは内部でunapplyを呼び、成功した場合「マッチした」とみなして返り値をそれぞれの変数にbindするような挙動をしますが、+:というobjectや:+というobjectがunapplyを持っているおかげで、パターンマッチ内でこの「演算子に見えるようななにか」が使えるというからくりなのでした。

まとめ

Scala+::+ について見てきました。これらの「演算子に見えるようななにか」は、じつは普段はSeqのメソッドであり、パターンマッチの中ではunapplyを持ったobjectとして振る舞っていたんですね!「普段構築するとき」と「パターンマッチで分解するとき」の対応が取れているため、利用する側としては違和感なく使えていますが、じつは内部ではこんな工夫がなされているんだよ、というのが伝われば幸いです。

can't add a new key into hash during iterationが謎の状況で発生しているように見えるときは

どのような問題か

さいきんのrubyでは、Hashのiteration中にhashに破壊的な変更として新しいkeyを挿入しようとすると、can't add a new key into hash during iteration というRuntimeErrorをあげてくれます。

コード:

# nyan.rb
def iterate_hash_and_insert_key(hash)
  hash.each do |k, v|
    hash[:new_key] = :new_value
  end
end

hash = {a: :b}
iterate_hash_and_insert_key(hash)

スタックトレース

Traceback (most recent call last):
    3: from nyan.rb:8:in `<main>'
    2: from nyan.rb:2:in `iterate_hash_and_insert_key'
    1: from nyan.rb:2:in `each'
nyan.rb:3:in `block in iterate_hash_and_insert_key': can't add a new key into hash during iteration (RuntimeError)

スタックトレースをみれば、たしかにeachの中でhashにアクセスして新しいkeyを追加しようとしているのが見て取れるでしょう。

ここで、下記のようなコードとスタックトレースの場合を考えて見ましょう。

コード:

# nyan.rb
def iterate_hash_loop(hash)
  hash.each do |k, v|
    loop do
      sleep 1
    end
  end
end

def insert_key(hash)
  hash[:new_key] = :new_value
end

hash = {a: :b}
t = Thread.new do
  iterate_hash_loop(hash)
end

sleep 1 # threadが始まるのを確実に待つ

insert_key(hash)

t.join

スタックトレース

Traceback (most recent call last):
    1: from nyan.rb:20:in `<main>'
nyan.rb:10:in `insert_key': can't add a new key into hash during iteration (RuntimeError)

スタックトレースを見た限りでは、「いやいや、iterationの中でhashいじってないし」という感じがするのではないでしょうか。実際、 insert_key はコードの上でもコールスタックの上でも、iterationの外で行われています。

ではなぜ can't add a new key into hash during iteration RuntimeErrorが出ているのでしょうか。もちろん、別のスレッドで同時に(厳密には同時じゃないけどわかって)当のhashをiterationしているからです。

このように、スレッドセーフでないコードで複数の箇所からアクセスされるHashを触っている場合、「スタックトレースを見てもコードみても、iterationの外でhashにアクセスしてるはずなのになぜか can't add a new key into hash during iteration RuntimeErrorが出るな〜」という一見不可解な状況に遭遇することがあります。一見なぞの状況で can't add a new key into hash during iteration RuntimeErrorが出てしまう場合、別のThreadの動きを確認してみるといいかもしれません。

どのように解決すべきか

どのように解決すべきかと考えてみると、

  • Mutexなどを利用して当該コードをスレッドセーフにする(正道な気がする)
  • スレッドローカルな変数(Thread#[]Thread#[]=)を利用してスレッドセーフにする(これも正道っぽい)
  • そもそもマルチスレッドやめる(やめれるならこれが一番シンプルになる)

あたりかなあと思っております。