タグ: Scala

play 2.5 のアレコレ

playがいろいろと変わっているので、いろいろと走り書き

logback.xml を使え

  • WARN がうるさい
  • logback.xml という名前でとりあえず次のようなファイルを作っとけばOK
  • https://www.playframework.com/documentation/2.5.x/SettingsLogger

GlobalSettings それ死んだよ

  • ドキュメント曰く、ライフサイクル系は Guice の eagerSingleton に便乗して実現するっぽい
  • JDBCConnectionPoolまわりなんかもここで Application が Inject されることを期待して、記述すればよいっぽさ
  • https://www.playframework.com/documentation/2.5.x/ScalaDependencyInjection#Stopping/cleaning-up

sbt 0.13.12 から build.sbt 使え

  • https://github.com/sbt/sbt/pull/2530
  • nrhd…

AWS Lambda を用いた Amazon SNS のエンドポイント整理

 Amazon SNS の Application に紐付いているエンドポイントには Enabled という Attribute がついています。文字どおり、有効なエンドポイントかどうかを表すものです。Amazon SNS は賢いので APNSGCM からのフィードバックを受けて、プッシュトークンが無効になった場合に自動的に false にしてくれます。しかし、基本的には Enabledfalse になったエンドポイントは不要です。したがって、これらを自動的に削除するような仕組みがあったほうがよさそうです。そこで、この記事では AWS Lambda を用いて、それを実現する方法について説明します。

AWS Lambda で動かすスクリプト

 コード自体は非常に簡単で以下のような形です。AWS Lambda Function を素早く立ち上げるとしたら本来は pythonnode.js のほうが良いかと思いますが、今回の作りたいものに関してはそれほど即時性を求められるようなものではないと思うので、好きな言語(もちろん Lambda でうごくもの)で問題ないと思います。

 json4s とか typesafe config とか scala logging とか使ってます。よい塩梅に jar に固めて、 Lambda Function を作って、アップロードしてください。これで AWS Lambda 側の準備は完了です。

Amazon SNS 側の連携設定

Amazon SNS側の作業は次の2点になります。

  1. 先ほど作った AWS Lambda Function の実行をフックさせるためトピックを作成し、購読(Subscribe)させる
  2. エンドポイントの整理をしたい対象の Application の持つエンドポイントの状態変化をフックして、トピックに通知を送るように設定する

それぞれについて見ていきましょう。

トピックおよび、サブスクリプションの作成

 トピックの作成は通常どおり普通にやってあげれば大丈夫です。とりあえず適当に「endpoint-updated-topic」とでも名前をつけておきましょう。続いて作成したトピックを先ほど作成した Lambda Function に購読させましょう。以下のような形で Protocol で AWS Lambda を選択すれば Endpoint のプルダウンリストに先ほど作成した Lambda Function の ARN がでてくるでしょう。

スクリーンショット 2016-05-26 0.41.37

Application のイベント設定

 最後に Amazon SNS の Application に紐づくエンドポイントの Attributes が変化したイベントをトピックに通知させるように設定します。 Application 一覧の画面で設定対象のアイテムを選択し Actions から configure events を選択してください。

スクリーンショット 2016-05-26 0.46.00

 そして、次に表示されるダイアログの Endpoint Updated の項目に、先ほどつくったトピックの ARN を入力すれば、すべての作業は完了です。

スクリーンショット 2016-05-26 0.48.25

動作確認テスト

 設定が終わったら適当に Application へ Endpoint を追加してみて、Edit Endpoint Attributes から Enable を false にしてみてください。きっと自動的にエンドポイントが削除されるはずです。

スクリーンショット 2016-05-26 0.50.55

 Application エンドポイントの Attribute 変更以外にも、作成イベント、削除イベントなどをフックすることができるので、やり方次第では様々な連携を AWS Lambda を用いて行うことができると思います。

無効になったSNSのエンドポイントを削除してくれる AWS Lambda コードを書いた

無効になったSNSのエンドポイント、別に放っておいてもよいのですが、定期的にお掃除したほうがよさそうだったので、お掃除用のAWS Lambdaコードを書きました。Amazon SNSは、アプリケーション(SNSのデバイストークンが登録できるやつ)に紐付いているエンドポイントの状態が変化したときに通知してくれる機能があります。その状態変化通知を受け取るトピックを適当に作っておいて、AWS Lambda コードがそいつを購読するようにしてあげればよいことになります。

ソースコード

Reactive Programming with Scala and Akka を読んでる #01

会社の近所でReactive Programming with Scala and Akka読書会なるものが開催されるようなので、参加することにした。とりあえず予習がてら初めの方をずらっとなめていってます。

1. Introducing Reactive Programming

最初に、リアクティブプログラミングが必要とされるまでの歴史なんかが書いてあった。ネットユーザー超増えてるので、データ転送量も指数関数的に増えてる。モバイルデバイスの利用も増えており、リアルタイムにデータを早く処理する必要も生じてきている云々。

Reactive とは

以下の4つの性質をもっているようなアプリケーション

  1. Responsive(高レスポンス)
  2. Resilient(耐障害性)
  3. Elastic(伸縮性)
  4. Message-driven(メッセージ工藤)

なるほどー。本読みながらまとめてくのめんどいな。気が向いたら更新します。

タグ付けできない休日

これを Swift で書こうとして無理だと悟って休日が終わりました。はぁ…。
はやく protocol 宣言に型変数をかけるようになってほしい。

Self = A を表現する言語機能が Swift には足りていないのが原因かなと思います。その部分を妥協するとしたら普通に Phantom Type 的な解決法として以下のような具合になるのかなぁと思います。

もちろん hogeId + 456 なんてものはできないですね…。残念。
しかも以下のようなことはできない。

ため息しかでない。

乱数生成を純粋化する

乱数の生成についての簡単な例を見てみよう。scala.util.Random が用意されているのでそれを使えば簡単だ。

nextInt メソッドは呼び出しのたびに返ってくる値が異なる。呼び出しのたびに rng の状態が遷移していることが想像出来る。たとえば、ランダム性を利用したメソッドのテストを書こうとした場合、テストを再現可能にする必要があるが scala.util.Random では難しそうである。仮に乱数ジェネレータを直接扱うとしても、ジェネレータの状態を揃えてあげる必要がある。こういった状態に対処するために、副作用を使用しないという原点に立ち返ろう。

純粋関数型の乱数生成

状態を遷移させるのではなく、新しい状態を返すという気持ちでやっていく。乱数ジェネレータの場合は、ジェネレータの nextInt を呼び出すと、値と新しいジェネレータを返すという感じになる。もちろん、もとのジェネレータは状態遷移しない。したがって、もとのジェネレータの nextInt を何回呼び出しても、同じ値と新しいジェネレータが返ってくる。コードで表現すると次のようになる。

こいつは以下のような挙動を示す。

見ての通り、nextInt は純粋な関数になった。

ステートフルなAPIと向き合う

乱数生成の例で見たようなことと同じような方法で、すべてのステートフルAPIは純粋化できる。基本的には状態を遷移させるのではなく、値と新しい状態を返してやるという方針になる。ただし普通にこれをやってみるとボイラープレートが多くて辛い気持ちになるだろう。たとえば RandomGenerator に対していろんな便利関数を定義してみる。同じことばっかりやっているように見えないだろうか?

すべて RandomGenerator => (A, RandomGenerator) な関数になっている。「状態を遷移させるのではなく、値と新しい状態を返してやる」という方針が関数に明確に現れてきている。この部分は繰り返し出現するのですこし抽象化しよう。type を使って RandomGenerator => (A, RandomGenerator)Rand[A] というエイリアスを張り付ける。すると見なれた mapmap2 などの高階関数が定義できて、かつそれを用いて上で定義した関数などを実装できるようになる。

遅延リストを作る

FP in Scala 第5章読書会の予習。Streamの作成については、半年前くらいに記事にまとめた。

リストの操作と無駄な中間リスト

リストへのmapやfilter操作の評価は、模式的には以下のような感じで行われる。

中間リストが無駄に生成されているので、mapやfilterといった高階関数をつかって合成性をおとさずに、つまりwhileループに頼らずなんとかしたい。そんなときに非正格性(non-strictness)を使うとよい。ということで「ワンランク上のリストの構築のしかた」を探っていく。

正格関数と非正格関数

「ワンランク上のリスト構築」をする前に正格性について確認を行っておく。
正格性とは関数の特性である。非正格関数では、引数の1つ以上を評価しないという選択が可能で、正格関数では引数が常に評価される。

Scalaの if 制御構造は非正格の一例。array.isEmpty の状態に応じて、then または else のステートメントが評価されない。

正確には、条件パラメータについては常に正格であり、各条件分岐後のステートメントは非正格である。Scalaで非正格関数を記述するためには、引数の一部を評価されない状態で受け取ればよい。具体的には以下のようにできる。

評価されない式をサンクとよぶ。これを評価するためには、空の引数リストを渡せばよい。() => A は実際には Function0[A] 型のシンタックスシュガーになっている。上の if2 関数はさらに簡単に以下のように書ける。

どちらの構文においても、評価されずに渡される引数は、関数の本体ないで参照されている場所ごとに毎回評価される。これを避けるためには、lazy キーワードを使って値を明示的にキャッシュすればよい。

遅延リストを作る

非正格にする方法はわかっているので、あとはリストでやったことに手を加えてやれば良い。
データ構造の定義は以下のとおり。

これを用いて、冒頭で示したリストへの操作の効率化を図っていくことを考える。

コンストラクタに潜む非効率性

Consデータコンストラクタを直接呼び出した場合、評価が無駄に走ることがある。

無駄な処理を減らすためにはキャッシュをさせてやればよい。以下のようにスマートコンストラクタを定義する。

Empty のスマートコンストラクタには型推論を助ける狙いもある。これらを用いると以下のようにできる。

各種関数の定義

foldRightmap, flatMap などを定義する

評価のされ方

以上のようにして定義した Stream の評価がどうされるかを最後に確認しておく。

Scalaのtraitを解剖してみた

Scalaのtraitを解剖してみた

これを jad すると

こうなりました

String Interpolationについては以下のスライドが詳しいです

Scalaの末尾再帰除去を確認する

Scalaの末尾再帰除去を実際にjadを使って確かめただけの簡単な記録。 jadの導入は以下のとおり。

簡単な階乗関数で確認すればわかりやすい

こんなのをコンパイルして jad でどうなっているか確認します。以下その結果。

factはそのまんま、fact2はちゃんと末尾再帰除去されている様子が見て取れます

Javaではどうなのか

Javaで同じことをしてみる

こいつは以下のような残念な結果に

こいつをどうするかについては次のURLが詳しい

Scalaの経路依存型(path-dependent type)とは?

Functional Programming in ScalaのWikiを翻訳している中で path-dependent types という単語が出てきてよくわからなかったので調べました。 調べたらhishidamaさんのページが出てきてくれたので良かった(?)です。 詳細はそちらを参照していただければという感じなんですが、以下一応自分なりに噛み砕いてまとめてみました。

経路依存型(path-dependent types)

普通にJavaで内部クラスを定義したとすると、以下のような感じになると思います。Scalaでも同様に書けると思います。

さてここから先、内部クラスのインスタンスを生成したときの挙動がJavaとScalaで変わるそうです。 まずはJavaの例を見てみましょう。

Javaでは class A の異なるインスタンスa1, a2を用いて、それぞれの内部クラス B のインスタンスを生成したとしても、それらは同じ型です。 つまり以下のようなことができるはずです。

しかしながらScalaで同様のことをやってみるとうまくいかないのです(やったことがなかったので気づかなかった!)。

また生成元の class A が同じインスタンスであっても、生成経路が異なると違う型として扱われるらしいです(これは驚きました)。

したがって同じ経路で生成された内部クラスのインスタンス同士の型でないと同じ型として認識されません。 くどいようですが、コードで示すと以下のような具合になります。

なるほどなぁ…って感じです。ちなみにパス依存で異った型として扱われている内部クラスを同じクラスとして扱いたかったら、外側のクラス名#内部クラス名にキャストすれば良いようです。勉強になりました…。