kinoppyd.dev

blog

products

accounts & contact

秒でBotを作れるMobb、その裏にあるReppって何者?

posted at 2018-12-02 01:06:24 +0900 by kinoppyd

このエントリは、Mobb/Repp Advent Calendar の2日目です

Reppというもの

昨日のエントリで、Mobbという秒でBotを作るための強力なフレームワークを紹介しました。このエントリでは、そのMobbを支えるReppという仕組みに関して書いていきます。

Webサービスを作ることに詳しいRubyプログラマ向けに説明すると、MobbとはSinatraであり、ReppとはRackです。それで理解できる方は、此処から先はそれ以上の情報が書かれていないので読む必要はありません。

ReppとはRackと言われてピンとこなかった方のために、Botを作るための複雑性を排除し、いかに秒でBotを作るかという話を交えつつ、Mobbに対してReppが何者を説明したいと思います。

Botを秒で作るために必要なもの

Botを秒で作るために必要なものは、複雑な物事をうまく隠す方法です。そしてSlackやTwitterを始めとした各種サービスとの結合部分は、その複雑な物事の最たるものです。

例えば、フレームワークに頼らず自前でBotを作るとしましょう。各サービスのSDKを探してきて、そのドキュメントを軽く読み、データの受取と投稿部分のコードを書きます。そしてその後、自分のbotが本当にやりたいロジックを書き、サービスと連携するコードと結合します。ドキュメント読むの、面倒じゃないですか? サービスと接続する部分の実装、面倒じゃないですか? こんなのでは秒でBotを作れません。

秒でBotを作るためには、サービスとの結合部分を徹底的に隠さなくてはいけません。サービスとの接続に手間取っているようでは、それは複雑も複雑であり、怪奇です。また、サービスとの接続部分は使いまわせなくてはいけません。あなたが作りたいBotは、一つではないからです。秒でBotを作るということは、毎秒新しいBotのアイディアを思いついて、すぐに実装する。そういうことです。

複雑さを回避するためのフレームワーク

次に、何かしらのフレームワークを使ってみましょう。フレームワークは、サービスとの結合部分を隠蔽してくれます。何かしらのサービスに接続して投稿を返すまでの一連の機能を、フレームワークは提供してくれます。さて、これで複雑さは減りました。しかし、まだ状況は複雑ではないでしょうか?

多くのフレームワークでは、複数のサービスに対応し、それに対して統一的なインターフェイスを提供します。そこまではとても良いことです。ですが、そのインターフェイスはフレームワークごとに実装された、独自の方法で提供されています。これはとても複雑なことです。

それだけではなく、多くのフレームワークは、複雑なBotの実装を実現するために、サービスへの投稿部分を自分で書く必要があります。具体的には、何かしらのオブジェクトのreplyメソッドを自分で呼び出したりして、サービス側に情報を投げたりします。覚えられますか、そんな方法? 私は毎回フレームワークのドキュメントを見て、合っているのかどうか確認します。これはとても時間の無駄です。

Reppという単純さと、Mobbによるさらなる単純化

フレームワークを扱う複雑さは、そのフレームワークのルールを理解しなくてはいけないことです。そしてそのルールが少なければ少ないほど、複雑さは減って、シンプルになっていきます。秒でBotを作るために求められるシンプルさは、次の2つのルールまで単純化することができます。

  1. あるオブジェクトは、callという名前のメソッドが定義されていて、サービスに投稿された情報を引数として受け取る

  2. callメソッドは配列を戻り値として返し、その配列の先頭の文字列がサービスに投稿される

これを読んで単純と感じるかどうかは、かなり人によって差があると思います。それでは、次のルールはどうでしょうか?

  1. サービスからの入力と一致するかを確認し、それに対応する文字列を返すメソッドを定義する

これはかなり単純だと思います。その最も大きな理由としては、メソッドの戻り値の文字列がそのままサービスに投稿される、つまりオブジェクトを操作してreplyとかいうメソッドとかを呼び出さず、ただ戻り値を返せばいいだけだからです。

最初に出てきた2つのルールと、あとから出てきた1つのルールは、それぞれあるプロダクトと対応させることができます。ReppとMobbです。

ここで、Mobbのコードを見てみましょう。

require 'mobb'

on 'Hello' do
  'Hi'
end

このコードを見たとき、あるルールに則っていることが一目瞭然でわかります。「サービスからの入力と一致するかを確認し、それに対応する文字列を返す」というルールです。このBotは、’Helllo’という発言に対して、’Hi’という投稿を返します。とてもシンプルで単純で、複雑さのかけらもありません。

それでは次に、Reppのルールを見てみましょう。

Class Bot
  def call(obj)
    obj.body == 'Hello' ? ['Hi'] : ['']
  end
end

このBotクラスは、「callというメソッドが定義されていて、サービスに投稿された情報を受け取り」、「callメソッドは配列を戻り値として返し、その先頭の文字列がサービスに投稿される」というルールを満たした実装になっています。つまり、obj.bodyが’Hello’であったばあいには、’Hi’という文字列が先頭に入った配列を返し、そうでなければ空文字列が入った配列を返しています。

このBotクラスは、Reppというインターフェイスを実装しています。Reppとは、Botフレームワークのためのインターフェイスです。先程出てきた2つのルール「callという1つのオブジェクトを受け取るメソッドが実装されている」「callは配列を返す」という取り決めは、Reppというインターフェイスの仕様だったのです。Botクラスを確認してください、間違いなくその仕様に一致しています。

ここである一つの事に気づきます。callメソッドの中身がやっていることは、先程Mobbの例で示したコードと、ほぼ同じことをしていると思いませんか? 実はこのBotクラスも、上に書いたMobbのコードの例も、どちらもReppというインターフェイスに則ったReppアプリケーションなのです。もっと正確に言うと、MobbとはReppアプリケーションのインターフェイス仕様を満たすオブジェクトを作成するためのDSLです

Reppというインターフェイス

Reppはインターフェイスであり、そしてインターフェイス通りに実装されたオブジェクトを受け取りサービスとの接続を仲介する実装でもあります。それではなぜ、Reppは存在するのでしょうか?

Reppの存在意義は、先程フレームワークを使ったBot開発のときに例に出した、フレームワークごとに違うルールを覚える必要があるという複雑性を解消するために存在しています。すべてのBotフレームワークが、Reppのインターフェイスを遵守すれば、覚えることはReppの仕様だけで済むのではないでしょうか?

もちろん実際にそんなにうまくいくことはありません。Reppのインターフェイスを守っていても、結局MobbのようなDSLの実装次第で、複雑性は変わるからです。そして、世の中にはすでにそのような仕組みが存在しています。Sinatra、Padrino、Ruby on Railsといった著名なWeb Application フレームワークは、全てRackというインターフェイスの仕様に準拠しています。Rackは、各種Webフレームワークと、Webサーバーとの間を橋渡しするための仕様です。Botの世界に置き換えると、WebフレームワークがMobb、WebサーバーがSlackといったふうに考えられます。Rackは、Reppとほぼ同様の2つのルールを定めています。

Rack: a Ruby Webserver Interface

To use Rack, provide an "app": an object that responds to the call method, taking the environment hash as a parameter, and returning an Array with three elements:

callというハッシュを引数に受け取るメソッドが定義され、そのメソッドは3つの要素からなる配列を返すオブジェクトが求められています。(このあとには配列の三要素の説明が続きますが、省略しました)

もちろん、このインターフェイスに沿ったからと言って、SinatraとRailsが同じように書けるかというとそんなことはありません。しかし、SinatraもRailsも、このRackインターフェイスに準拠したオブジェクトを生成するためのDLSだということに違いはありません。そしてその中でも、SinatraはRackの仕様を最もわかりやすく直感的な記述で表現しています。Mobbは、この最もシンプルで複雑性のないSinatraに影響を受けて記述されました。

Reppとは、Botフレームワークのインターフェイスです。そしてMobbは、Reppインターフェイスを満たしたオブジェクトを生成するための、最もシンプルで複雑性のないDLSです。この2つを使うことによって、ユーザーは自分のロジックに集中できる環境で、秒でBotを作ることができるのです。

そして、このReppのインターフェイスの仕様に則れば、Reppというサービスとの接続を仲介してくれる資産を活用することができます。もしかしたら、Ruby on Rails のようにリッチなBotフレームワークを作りたい人も、世の中にはいるかも知れません。だからこそ、ReppはMobbとは別の実装によって管理され、だれでもReppアプリケーションを生成するDSLを作れるようになっているのです。