hataaaaa’s diary

個人用メモが中心です

arrow-ktを使ってRxのSingleでflatMapのネストをなくすのをとりあえず試してみる

サンプルコードをもとに適当に1時間くらいさわってみたメモ。 強い人に刺されるの怖い

RxJava2のSingleを非同期処理とかに使っていると、データをくっつけたりする際にflatMap地獄になるときがたまにある。

たとえば hogeRepository, piyoRepository, fooRepository, barRepository があって、それぞれのRepositoryが直前のRepositoryの結果を引数にとり、全結果をまとめて返す処理を書きたいとする。

とりあえず愚直に書いたとき

// repositoryは全部Singleを返す
hogeRepository.find().flatMap { hoge ->
    piyoRepository.findByHoge(hoge).flatMap { piyo ->
        fooRepository.findByPiyo(piyo).flatMap { foo ->
            barRepository.findByFoo(foo).map { bar ->
                Result(hoge, piyo, foo, bar)
            }
        }
    }
}

みたいになり、まあまあ辛い気持ちになる。

Pair使ってみる

// repositoryは全部Singleを返す
hogeRepository.find().flatMap { hoge ->
    piyoRepository.findByHoge(hoge).map { piyo ->
        Pair(hoge, huga)
    }
}.flatMap { (hoge, piyo) ->
    fooRepository.findByPiyo(piyo).map { foo ->
        Pair(Pair(hoge, piyo), foo)
    }
}.flatMap { ((hoge, piyo), foo) ->
    barRepository.findByFoo(foo).map { bar ->
        Result(hoge, piyo, foo, bar)
    }
}

雰囲気で書いたけどネストの深さは抑えられた。けどもうちょっとなんとかしたい。

本題

arrow-kt を使ってみる

arrow-kt.io

arrow-ktを使ったとき

// repositoryは全部Singleを返す
SingleK.monad().binding {
    val hoge = hogeRepository.find().k().bind()
    val piyo = piyoRepository.findByHoge(hoge).k().bind()
    val foo = fooRepository.findByPiyo(piyo).k().bind()
    val bar = barRepository.findByFoo(foo).k().bind()
    Result(hoge, piyo, foo, bar)
}.value()   // Single<Result>

ネストしないで書ける! コード書く上で嬉しい点は、 hoge とか piyo がSingleが剥がれた型で取れるというところですかね。

↑について超ざっくりメモ

  • SingleK : Rxの Singleのラッパークラス
  • SingleK.monad().binding {} : this が MonadContinuation になる(途中は追ってない)
  • .k() : Single -> SingleK
  • .bind() : 中みたらコルーチンがどうので実現してるっぽくて、そのあたりちゃんとわかってないとわからなそうだなと感じた

感想

  • とりあえずツールとして使う分には便利なのかなと思った。
  • ただ、もともと実現したかった flatMapの深いネストをなくしたい に対しては巨大すぎる仕組みな感じもした。
    • そもそもarrow-ktが一番輝くのはもっと違う場面なんだろうな
    • scalaのfor式でもflatMapでネストしない書き方できるけど、そっちはただのシンタックスシュガーと聞いているし、比べると黒魔術感

参考 (公式)

USBリモコンをSlackで操作する・・・ための準備

自分用メモ。家用SlackでUSBリモコンを操作したかったので、とりあえず環境を整える。 別にslackじゃなくてもよかったけどUIとか余計なこと考えなくてよくて楽。 使わないと思うけど外からも操作できるようになるので。

USBリモコン

こちらを購入。

株式会社 ビット・トレード・ワン | USB接続 赤外線リモコンキット

CLIツール

ガッと調べたところ、 github.comLinuxで手軽に使えて良さそうだったので導入しました。

赤外線コードをメモる

地道な作業。

Cloud Functions for Firebaseを使ってRealtime Databaseの更新をPush通知する

アプリ内お知らせみたいな機能をFirebaseのRealtime Databaseで実現してて、DBが更新されたときにPushも送りたかった。Cloud Functions for Firebaseを使うとRealtime Databaseの更新をトリガーに処理を実行できるようなので、Push送信に利用してみる。

Push送信にはFirebase Cloud Messagingを使う。(他のPush配信サービスでも良いですが、Cloud FunctionsでGoogleの外に通信するには料金がかかるみたい*1 )

Cloud Functions for Firebase

  • Realtime DatabaseとかCloud Storageの更新をトリガーにして処理を実行する(ざっくり)
  • ユースケースとしては公式ドキュメントに色々ある *2

全ユーザにPush送信

サクッと全ユーザにRealtime Databaseの更新を通知する。お知らせ一覧の更新を通知したりするのがユースケース的にありそう。

Realtime Databaseの準備

今回は例として、↓のようなDBを用意。

f:id:hataaaaa:20170914072139p:plain 

クライアント側の準備

プッシュ送信に使うFirebase Cloud Messaging APIには全体送信オプションみたいなものは無いようです。なので、例えば「all」というトピックをクライアントから購読するようにしておき、このトピックに対してPushを送信することで全体送信を実現する感じにします。AdminSDKからトピック購読させても良いかもですが、今回はこだわらない感じで。

 

Cloud Functionsの設定

導入は https://firebase.google.com/docs/functions/get-started あたり。

実行するコードは↓

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.dbNotification = functions.database.ref('/notifications/{notificationId}').onWrite(event => {
  const notificationId = event.params.notificationId;
  const notificationPromise = admin.database().ref(`/notifications/${notificationId}`).once("value");

  return Promise.all([notificationPromise]).then(results => {
    const notification = results[0];
    const payload = {
      notification: {
        title: `${notification.child('title').val()}`,
        body: `${notification.child('url').val()}`
      }
    }
    return admin.messaging().sendToTopic('all', payload);
  });
});

 

簡単!

おわりに

今回はRealtime Database × Cloud FunctionsでPushを送るだけだったが、

  • Realtime Databaseはアプリが起動しているとき
  • Pushはアプリが起動してないとき

と、手軽にどちらの起動状態に対してもアクションできるのは良いなーと思った。まだCloud Functionsはベータ版なので、これからに期待。

 

参考リンク