golangでidobataのbotを書く

前提条件

  • idobataを使っている
  • idobata botがどうやって動いているのか知らないので、知りたい欲求がある
  • golangはほとんど書けないが、練習のためにgolangで書く
  • websocketについては、複数クライアントでのお絵かき同期サンプルを作ったことがある程度の知識

目標

  • botにリプライしたら返事ができる

調査内容

  • 自分が読んで意味のわかる言語Rubyで書かれているruboty-idobataのコードを読む
  • なんとなくPusherを使っているっぽいことがわかる
  • Pusherはwebsocket機能をやってくれるBaaSっぽい。ようはwebsocketを使っている
  • Pusherはgoでかかれたライブラリpusher/pusher-http-goを公式に提供している
  • idobataはbotのkeyがわかれば投稿自体はすぐできる
  • リプライされる事を知るためにはwebsocketで投稿を監視しなければならない

獲得情報

  • Pusherの公式ライブラリpusher/pusher-http-goは、サーバー機能実装をサポートするためのものであって、websocketは使っていない。つまりクライアントになる機能がない。おそらくはJSやiOS/Android端末ぐらいしかクライアントとして使われないだろうとの予測と思われる。
  • Pusherの公式クライアントがないのでwebsocketから直接通信するしかない(謎の確信)。というかそこが知りたい
  • websocketは基本的に生socketのように、Read/Writeすればいい(ようにライブラリーでラップされている)
  • websocketでは、たいていのアプリではただの文字列ではなく、情報を構造化してJSONでやりとりしている。ようはアプリが勝手に決めている取り決め。
  • さらに、websocketでよく見る「なんとかイベントが来たら、このコールバックを呼び出す」という実装も、websocketの通信上どういうデータが次に来るのかはわからないので、イベント名とデータ(JSON)のセットをアプリで取り決めて、実装しやすいようにしているだけ。本質はただ文字列をRead/Writeしているだけ。
  • websocketのgolangライブラリはgolang-samples/websocketで使われているgolang.org/x/net/websocketでいいだろう。golang界隈のライブラリ事情はよくわからないが、mattnさんが使っているのならまちがいない
  • 通信の順番はruboty-idobataを参考にすればいい
  • JSONからstructに書き起こすのが面倒だと思ったら、自動化してくれるサイトがあった( http://json2struct.mervine.net/ )。便利

成果物

疑問点

  • 一度idobataへメッセージを投稿すると、なぜかwebsocket側では二回同じメッセージを受け取ってしまうので、二回websocketをReadしなければならなかった。yasslab/ruboty-idobataではそんなことはしていないようなので、何か手順を間違えているのかもしれない。
  • websocketのプロトコルが深いところまではわからなかった。結局tcp-socketをいい感じの取り決めでRead/Writeしてるだけなんだろうか。
  • golangのセオリー的なものがまだよくわからないので書き方は模索中

妄想

  • JSONじゃなくてMessagePackでもよさそう。websocket.MessagePack.Receive()とか。

注意事項

  • idobataのAPIはすべて公式に公開しているわけではなく、仕様が変わる可能性が大いにある

デバッグに便利そうなgem LazyMethodを書いた

https://bugs.ruby-lang.org をなんとなく眺めていたら、とあるチケットが目につきました。

Feature #12125: Proposal: Shorthand operator for Object#method - Ruby trunk - Ruby Issue Tracking System

このチケットは、「Method classは便利だけど記述がめんどくさいから新しい記法作ろうぜ」的なものです。

Method classはデバッグやblockとして使うのに便利ですよね。

foo.method(:bar).arity #=> 1
foo.method(:bar).source_location #=> ["file.rb", 25]
Dir["*/*.rb"].map(&File.method(:basename)) #=> ["file.rb"]

Method classの欠点

僕が考えるMethod class唯一の欠点は「インスタンスを作るときにメソッド前後に何かを書かなきゃいけない」だと思います。

# bar methodはどこで定義されてるんだろう?
foo.bar

# 前後にコードを書いて
foo.method(:bar).source_location #=> ["file.rb", "25"]

# わかったから前後のコードを削除
foo.bar

サッと知りたいだけのとき、これは不便です。

methodsなら最後だけ、tapなら好きな場所に一箇所書くだけなので書きやすいですよね。

foo.methods #=> 後ろに付け足すだけ
foo.tap { |i| p i}.bar #=> 真ん中に書くだけ 

解決案

そこで考えた解決案として、「前後ではなく、前か後ろ片方だけなら楽なのでは」というものでした。

# あとに書く
foo.bar.method.source_location

# まえに書く
foo.method.source_location.bar

あとに書くのは文法的に苦しそうなので、まえに書く方法を提案してみたのが先ほどのチケットのコメントというわけです。

これなら新しい文法を追加することなく、前後へのコードの追加から前だけのコードの追加になるので、 ある程度手間を軽減できそうです。

LazyMethod

どうもGemを書くのが趣味みたいなところがあるので、これをGem化したのがLazyMethodです。

github.com

名前は、「まだメソッド名を決めてないからUnboundMethodにあやかって、UndecidedMethod?UnnamedMethod?UnfoundMethod?かな?でもなんか地味でイケてる感じがない……。」と思いましたが、 呼び方にEnumerable#lazyっぽい気がなんとなく感じ取れたので「LazyMethod」としました。

使い方は、Kernel#methodメソッドの呼び方をfoo.method(:bar)からfoo.method.barとするだけです。 引数をつけると、通常のKernel#methodメソッドの動作になります。

Object.instance_methodsに含まれるような基本的なメソッドはLazyMethodと相性が悪いです。 foo.method.to_sのように使うと、文字列がほしいのかto_sメソッドの情報がほしいのかで判断が別れるのですが、 こういう基本的なメソッドを書き換えると混乱を生んだりデバッグしにくかったりと、Methodオブジェクトとして使えてもあまり嬉しい要素がないので、 通常のRubyっぽい動きに舵を切りました。

また、source_locationparametersなどのMethod classのメソッドは使われることが多いでしょう。 ところがfoo.method.bar.source_locationのように書いては元の問題が解決できないので、 foo.method.source_location.barのように遅延的に呼び出すことができるようにしました。

require 'lazy_method'

foo.method #=> #<LazyMethod foo>
foo.method(:bar) #=> #<Method foo:bar>
foo.method.bar #=> #<Method foo:bar>
foo.method(:bar).source_location #=> ["file.rb", 25]
foo.method.bar.source_location #=> ["file.rb", 25]
foo.method.source_location #=> #<LazyMethod foo(source_location>
foo.method.source_location.bar #=> ["file.rb", 25]
foo.method.source_location.baz #=> ["file.rb", 29]
Dir["*/*.rb"].map(&File.method.basename) #=> ["file.rb"]

なんとなく便利っぽい気がしてきませんかね。僕はまだわかりません。

cgoがむずい

CとGoの世界の境界線をいったりきたり。

分かる人には簡単なんだろうけどgolang自体が初心者から抜け出せない。。。

コールバック関数をメンバーに持つstructをつくって、コールバックが動くと対応するgolangのfuncが動く感じになればいい。

直接GoからCにfuncを渡せないっぽいのでexternしたりラップしたりと面倒な感じになっていてイマイチ。

ようやく動いたと思ったらintっぽい値がoverflowしてるっぽい値になっているのでどこかでキャスト的なことが必要っぽい。

参考文献だけまとめておいて今日は終わり。

Go Tips Learned From Writing go-libxml2/go-xmlsec — Medium

cgo の基本的な使い方とポインタ周りのTips (Go v1.2) - LESS IS MORE

golang で string を []byte にキャストしてもメモリコピーが走らない方法を考えてみる - Qiita

Go1.6でポインタをcgoの関数へ渡す際に発生するcgoCheckPointerを回避する方法 - Qiita

cgo - GoDoc

アカマイ 知られざるインターネットの巨人 / 小川 晃通

積読消化に読んでみた。

内容は薄く広くという感じサラッとしているので2日くらいで読めた。

本書は「アカマイ」部分は半分で、もう半分は「インターネット」について書かれた本だった。

インターネットが好きだ。

インターネットで人生が変わったし、インターネットに対して感謝している。

そして、そんなインターネットに対して貢献したいと感じている。

ではどうすればインターネットに貢献できるのか。

どうすればインターネットに貢献したと言えるのか。

仕様の標準化グループに参加?awesomeなソフトウェアの開発?地味な技術研究?

自分が最も効果を出せることはなんだろうか。何をするのが一番効果的に貢献できるのか。

最近そんなことを考えている。

孔明の罠 Kaizo Trap

始まりはfacebookだった。

facebookでシェアされた何気ない動画。

普段なら「へ〜」で終わってページをスクロールすることがほとんどだけど、この動画だけは違った。

その動画を再生してから8分間、じっと見続け、再生が終わった瞬間二周目を開始した。

8分間のアニメと音楽が流れるだけの動画にこんなに心を奪われたのは初めてだった。

情報量、ネタのストライク感、音楽、何もかもがすばらしい。

これぞクリエイティブ。これぞコンテンツ。そんな感動がずっと僕の中をめぐり、次の日も続いた。

これがその元になった動画だ。


孔明の罠 - Kaizo Trap

ネタバレ

ここからは動画を見たことを前提でネタバレをガンガン話す。

事の発端は

Leslie Waiというミュージックアーティストが発表した曲を、Guy Collinsというオーストラリアのアニメアーティストがyoutubeで発見し、ミュージックビデオを作ったという流れのようだ。

www.youtube.com

ここまでヌルヌル動くアニメーションがおそらく個人?で作られてるのもスゴイ。

詳しくは知らないが、数々の作品を発表されているのでおそらく著名な方なのだろう。

こちらに投げ銭すると、動画中に登場した説明書や音楽、シークレットエンディングなどが入手できる。

mondomedia.com

シークレットエンディングはゲームの世界に自分も参加すると言えば聞こえはいいが、プレイは正直おすすめしない。

最後まで見たが、大変な時間と労力を割く割には得られるものがなにもなかった。

しいて言うなら「7a」は「z」、「6f」は「o」を表すということを覚えたぐらいだ。

ただの投げ銭と考えよう。

ループもの

ループものというのは様式美というかジャンルと化しているけど、ループものはゲームと同じ、アクションゲームはループものだったんだと気付かされた。

ループものといえば知ったかするつもりはないので僕の知っている範囲だと、Dグレミランダ編、エンドレスエイトまどか☆マギカ、All need is killなどがある。

ゲームではアクションゲーム全般、ブレス オブ ファイアVも死んでおぼえて強くなるという点ではループものっぽい。(ブレス オブ ファイアVは僕のもっとも好きなゲームだ)

映画ルーパーはループもののようでループものではない(これも素晴らしい映画)

作りこみ

細かいところも作りこまれているのもいい。

最初の玄関の裏にはあるのはこの絵だろう

男性がゲームを始めると、テレビ画面に一瞬ラスボスが映り込む。

随所には日本語が散りばめられている。

映画マトリックスでもそうであるように、日本語は謎めいたものだったり、文字化けによって出てくる奇っ怪な物を象徴していたりするのだろう。

タイトル「孔明の罠」はどこが最初かわからないが、おそらくニコニコ動画のゲームチャンネルだ。

スーパーマリオメーカーが発売されるはるか前からマリオは"改造"されてきた。

そして初見殺しのトラップは「孔明の罠」としてしたしまれ、空中に突然現れるブロックはあまりに有名だ。

このブロックも、主人公が連続して死ぬ場面をよく見ると登場している。

また、動画作者自身も改造マリオのTAS動画を上げていることからなかなかのゲーマーだと思われる。

彼氏の攻撃を受けて飛ばされるシーンの裏側には「エアーマンが倒せない」の歌詞が描かれている。

おわり

なによりシンプルな絵柄ながらも決してあきらめない彼女の姿勢に、僕は感動したのだった。

素晴らしい動画コンテンツを作ってくれた作者に敬意を評し、ダウンロードパッケージ$5+作者へのチップ$5+このブログを書くことで貢献としたい。

thank you for never give up -


Rick Astley - Never Gonna Give You Up

RubyでJSONをclassにmappingするやつと、2つの記法

僕はHashが嫌いだ。

できるだけHashは使いたくない。

h[:foo]

とか

h.fetch(:foo)

とか本気で使っているのかね君たちは(今日は何となくこんな感じですすみませんすみません)。

Hash

Hashはとにかくデバッグが面倒だ。

設定やら多値を表現するためか生存が長くなりがちなくせに、 どこで生成されたHashなのか、どこで追加されたkeyなのかよくわからなくなる。

class名がないから何を表したいオブジェクトなのか、デバッグした時にわからない。class名でgrepもできない。

JSONを扱うプログラムではついついHash#[]Hash#fetchばかりのコードになりがちで、 エラーが起こったらHashのkeyをtypoしたんだか値がnilなんだかよくわからなくなる。

Hash#fetchでkeyを間違えるとKeyErrorが発生することで検知できるけど、「どんなkeyがあったかな?」と思っても正しいkeyは自分でコードを読むかp h.keysするなりして探さないといけない。

いまのところdid_you_mean gemもKeyErrorに対応していないので、typoにおびえてコードを書くか、ひたすらデバッグするかだ。 *1

なにより

h[:foo]
h.fetch(:foo)

は長い。

Hashはどんなkeyが来るかわからない時だけ使うべきだ。 あと、「記述が少ない」「名前を覚えなくていい」という利点から、keyword argumentsのような引き数にも便利。 gemの外部APIとしては十分選択肢だと思う。

Struct

Structは好きだ。

どこで生成されたオブジェクトなのかclass名でgrepすればだいたいわかるし、key名を間違えてもNoMethodErrorで教えてくれる。 なんならdid_you_mean gemが正しそうな名前を教えてくれる。

「どんなkeyがあったかな?」と思ったらclassの定義を読めばいい。

s.foo

の方がシンプルだ。

だがしかし、JSONをスッとマッピングできるわけではない。

golangやcrystalのようにJSONを特定のclassに属するオブジェクトに変換する方法があれば、Hash的な使い方でなく、Struct的な使い方ができる。

TypeStruct

ようやく本題。

以前、keyword argumentsが使えてより厳しいStructということでTypeStruct gemを作った

これをさらに推し進めて、hashからマッピングできるように実装してみた。

github.com

README.mdから引用すると、

Point = TypeStruct.new(
  x: Integer,
  y: Integer,
)
Color = Struct.new(:code)
Line = TypeStruct.new(
  start: Point,
  end: Point,
  color: Color,
)

hash = JSON.parse(%({"start":{"x":3,"y":10},"end":{"x":5,"y":9},"color":{"code":"#CAFE00"}}))
line = Line.from_hash(hash)

p line
#=> #<Line start=#<Point x=3, y=10>, end=#<Point x=5, y=9>, color=#<struct Color code="#CAFE00">>
p line.start.y
#=> 10
line.stort
#=> NoMethodError

このように、key名とclassを設定すれば、HashオブジェクトをStruct(っぽい)オブジェクトに変換してくれる。

設定に違反するデータならエラーを出してその場で教えてくれるし、参照先のclassがTypeStructから作ったclassなら再帰的にオブジェクト化してくれる。

Structオブジェクトにも対応しているけど、Structの場合は値のclassを設定できないのでなんでも入れられて再帰的にオブジェクト化できなくなるが、既存のcodeで使いやすいようにつくってみた。

2つの記法

「値のclassを設定できる」を実現するために2つの記法を用意した。

Union

たとえば「truefalseになるメンバをつくりたい」と思うと、Rubyの場合困ったことになる。

Foo = TypeStruct.new(
  bar: TrueClass, # or FalseClass???
)
Foo.new(bar: false) #=> TypeError

TrueClassFalseClassに判別用のmoduleをincludeすればチェックできるけど、標準classを書き換えたくない人も多い。

そのため、独自のclassを用意してみた。

Foo = TypeStruct.new(
  bar: TypeStruct::Union.new(TrueClass, FalseClass)
)
Foo.new(bar: false)

標準classを少しなら拡張してもよいという方のために、こんな書き方もできるようにした。

require 'type_struct/ext'
using UnionExt

Foo = TypeStruct.new(
  bar: TrueClass | FalseClass
)
Foo.new(bar: false)
Foo.new(bar: true)

Class#|メソッドを用意して、classをつなげて複数のclassを表現できるようにした。refinementsを使っているので、Class#|が有効になる範囲も限定できる。*2

ArrayOf

メンバを配列にしたい場合もあるだろう。

Cだと

struct foo {
  int ary[];
};

golangだと

type Foo struct {    
        ary []int
}

みたいなやつだ。

その場合の表現方法も考えて、専用classを用意した。

Foo = TypeStruct.new(
  ary: TypeStruct::ArrayOf.new(Integer)
)
Foo.new(ary: [1,2,3])

名前が長いのが玉にキズなので、これも短縮できるようにしている。

require 'type_struct/ext'
Foo = TypeStruct.new(
  ary: ArrayOf.new(Integer)
)
Foo.new(ary: [1,2,3])

組み合わせ

もちろんUnionとArrayOfは組み合わすことができて、「配列の時もあるけどnilのときもあるメンバ」を表現できる

require 'type_struct/ext'
Foo = TypeStruct.new(
  ary: ArrayOf.new(Integer) | NilClass
)
Foo.new(ary: [1,2,3])
Foo.new(ary: nil)

これらの記法を組み合わせれば、かなり複雑な構造を持ったJSONでも、特定の名前のついたclassのインスタンスオブジェクトを作ることができるようになるので、お試しください。

もっとRubyならではの便利さもあるとうれしいのでアイデアも募集中です。*3

*1:とここまで書いたら「実装すればいいじゃん」と思い立ったのでやってみた

*2:この記法はcrystalを参考にしました。

*3:HashOfを用意していないのはHashが嫌いだからです。

GitHubの草を連続40日間生やしてみたけどやめた

https://github.com/ksss

f:id:ksss9:20160126144118p:plain

rebuild.fmの#120を聞いて、jQueryの作者が始めた「毎日コードを書く」というやつを40日間やってみた。

rebuild.fm

John Resig - Write Code Every Day

よかったこと

アウトプットがふえた

毎日コードを書くというルールなので、 アウトプットは多くなった。

手持ちのリポジトリを大幅に整理したり、PRも10件ぐらい飛ばした。

毎日コードを書くと、コンテキストスイッチのオーバヘッドが小さくなるというのは本当で、 お風呂に入っている時にアイデアが思いつく事もあった。

無意識領域の脳リソースをうまく使うことができてるのかもなあと思った。

よくなかったこと

インプットがへった

アウトプットのために使う時間を必ず確保するので、アウトプットからインプットへコンテキストスイッチするオーバーヘッドは増加したような気がした。

脳リソースをうまく使えているとはいえ、手持ちの情報からの構築になる。 なので新しいことをおぼえたり、本を読んだりといった時間がへった。

おおきいプロダクトのコードをしばらく読んでいたいだけなのに、どこかアウトプットのネタ探しがチラついて、 時間がかかりそうなコードリーディングはやめる傾向にあった。

義務感

想像していた通り、

序盤はよかったけど、中盤から「今日のノルマ」消化のために作業するようになっていた。

「今日のノルマを消化するために機能を追加する」 「今日のノルマを消化するネタ探しとしてコードを読む」

そんな考えが浮かび始め、だんだんとこのまま続けてもよくないなあという気持ちが増えていった。

やめた

コードをpushしてやっぱやめたとreset --hard HEAD~1してforce pushしても草がついたとき、 この活動はやめようと思った。

土日は一切コードを書かず、家の用事をすませたり、カフェで本を読んだりしてた。 おかげで「火星の人」を読み終えた。

火星の人

火星の人

おまけ

TEDにはおもしろい話があって「学ぶことを止めよう」という内容のものだ。

logmi.jp

www.youtube.com

学ぶことをやめ、自分で考えたほうがユニークな発想にいたることができるというものだ。

GitHub草運動を続けている方が、自分で考える状態に近いのかもしれない。

インプットがなく、自分の中のアイデアでなんとかしなければならないからだ。

「火星の人」でも主人公は手持ちの材料だけで戦った。それでも良いアイデアは浮かぶし、生き延びていけるのかもしれない。

しかし毎日一つ以上のアウトプットが強制されているので、ただアイデアを検証したり考えにふけったり休んだりといったことができない点が、違うように思った。

いいとこ取りをするアイデアはないだろうかなあ。