introduction to spray at kansai functional programming
DESCRIPTION
Introduction to Spray at Kansai Functional ProgrammingTRANSCRIPT
関西関数型道場 2014-09-06
高速・軽量Webフレームワーク
で学ぶ
• Suguru Hamazaki (@hamazy)
• とあるマーケティング会社で勤務
• 先週まで WordPress ホスティングの会社にいました
• an open-source toolkit for building REST/HTTP-based integration layers
• on top of Scala and Akka
• asynchronous, actor-based, fast, lightweight, modular and testable
— from http://spray.io/
REST/HTTPによる統合レイヤーを構築するための、オープンソースなツールキット
Scala と Akka がベース
非同期、Actorベース、高速、軽量、モジュラー化されており、テスト可能
Akka is a toolkit and runtime for building highly concurrent, distributed,
and fault tolerant event-driven applications on the JVM.
— from http://akka.io/
Akka は並列度が高く、分散した、耐障害性の高い、イベント駆動型のアプリケーションを作成するツールキットであり、実行環境です。
Actor• 並行計算のモデル
• スレッドモデルよりも抽象度が高く、扱い易い
• イベント駆動型の受信ループでメッセージを非同期に処理
• Non-blocking で高速
• Akkaのactorは非常に軽量
• 1GBのメモリーにつき数百万のactorが動作
(demo)
Sprayのアプリで、 Actorがどのように使われるか見てみましょう
• ActorSystem から Actor を作った
• Spray が提供する Actor とメッセージをやりとりして、簡単な HTTP のサービスが出来た
• Http.Bind, Http.Bound, Http.Connected, Http.Register, HttpRequest, HttpResponse
• HTTPのイベントに対応したロジックはそれはそれでわかりやすい
• が、全部それで書くのはしんどい
• HTTPとアプリ、レイヤーは分けたい
Spray Routing
• ルーティングを定義
• どんなリクエストをどこで処理するか
• HTTPからアプリのレイヤーへの橋渡し
• 抽象化された柔軟な内部DSLで書ける
• 内部DSLなのでScalaの型チェックが入る
(demo)
簡単なルーティングの定義を 見てみましょう
• リクエストのメソッド名、パス、パラメーターで、処理するリクエストを抽出できた
• パス、パラメーターの構造に応じて、処理の型が決まった
• 型が合わないとコンパイルエラー
型安全は嬉しい
柔軟性も欲しい
complete の例
complete(StatusCodes.OK)
complete(HttpResponse(entity = "Hello"))
complete("Hello.") String 型StatusCode 型
HttpResponse 型⋮
もっと色々渡したい
• JSON オブジェクト
• XML (NodeSeq型)
• Future型
• 任意の型のドメインオブジェクト
渡す値の型が共通のインターフェイスを 継承することは、とても期待できない
さてどうしましょう
• 似たコードの繰り返しを防ぐ
• 型消去にまつわる問題を回避
• Future[String] と Future[NodeSeq] を区別してもらわないと困る
実際の complete の定義
def complete: (⇒ ToResponseMarshallable) ⇒ StandardRoute
ほぼこれに一致
def complete( marshallable: ⇒ ToResponseMarshallable): StandardRoute
(このメソッドを関数へ持ち上げると、先ほどのコードになる)
この関数で、 どうやって多様な型に対応しているのか?
def complete( marshallable: ⇒ ToResponseMarshallable): StandardRoute
Implicits
Implicits
• 暗黙の型変換 / 暗黙のパラメーター
• Haskell でいう型クラスを Scala で実現するためのもの
Haskell の型クラス
class Ord a where (<=) :: a -> a -> Bool
instance (Ord a, Ord b) => Ord (a, b) where (xa, xb) <= (ya, yb) = xa < xb || (xa == ya && xb <= yb)
順序という概念を定義
概念をペアについて実装したモデル
sort :: Ord a => [a] -> [a]
> sort [ (3, 5), (2, 4), (3, 4) ]
概念を前提とする関数を定義
関数をこのように使用できるのは、 ペアについて実装したモデルがあるため
Scala の暗黙パラメーターでtrait Ord[T] { def compare (a:T, b:T):Boolean}
implicit object intOrd extends Ord[Int] { def compare (a:Int, b:Int):Boolean = a <= b}
順序という概念を定義
概念を Int について実装したモデル
def sort[T](xs:List[T])(implicit ordT:Ord[T]):List[T]
> sort (List (3, 2, 1))List (1, 2, 3)
概念を前提とする関数を定義
関数をこのように使用できるのは、 ペアについて実装したモデルがあるため
更に、暗黙型変換でtrait Ordered[T] { def compare (o: T) : Boolean}
implicit def mkOrd[T](x:T)(implicit ev: Ord[T]): Ordered[T] = new Ordered[T] { def compare(o: T) = ev.compare(x, o) }
> 1.compare(2)true
OOP的に書けるようになった!
implicit のルール
• プレフィックスなしでアクセスできる、implicit な値 (もしくは変換) を探しにいく
• 目的の型 (変換の場合は変換元と変換先) のコンパニオンオブジェクトのメンバーから、implicit な値 (もしくは変換) を探しにいく
詳しくは、 http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html 参照
complete を もう一度見てみる
trait RouteDirectives {… def complete: (⇒ ToResponseMarshallable): StandardRoute…}
complete("Hello.") メソッド定義
メソッド呼び出し
ToResponseMarshallable という概念を String で実現したモデルが、どこ
かにあるはず
object ToResponseMarshallable { implicit def isMarshallable[T](value: T)(implicit marshaller: ToResponseMarshaller[T]): ToResponseMarshallable…}
ToResponseMarshaller という概念を String で実現したモデルが、どこかにあるはず
object ToResponseMarshaller extends BasicToResponseMarshallers with MetaToResponseMarshallers with LowPriorityToResponseMarshallerImplicits { def fromMarshaller[T] (status: StatusCode = StatusCodes.OK, headers: Seq[HttpHeader] = Nil) (implicit m: Marshaller[T]): ToResponseMarshaller[T]…}
Marshaller という概念を String で実現したモデルが、どこかにあるはず
object Marshaller extends BasicMarshallers with MetaMarshallers with MultipartMarshallers {…}
trait BasicMarshallers {… implicit val StringMarshaller: Marshaller[String]…}
Marshaller という概念を String で実現したモデルが、ここにあった!
• 結局、Marshaller[String]があれば充分だった
• 他にも様々な Marshaller が Spray に用意されている
• 独自の型で complete を利用する場合は、有効なスコープやその型のコンパニオンオブジェクトに implicit な Marshaller[T] を定義すればよい
まとめ• AkkaベースのSprayはActorを使って非同期、ノンブロッキングで動いている
• Spray のルーティング定義は型安全かつ柔軟な DSL で書ける
• それには Scala の implicit の仕組みを利用している
• Scala の implicit は Haskell の型クラスに相当する
Scala も Haskell もその他の言語も 一緒に勉強しましょう
今日はお話しできなかった事
• 型安全で柔軟な Spray Routing の DSL には、他に Shapeless が一役買っている
• C++ の Template Meta-Programming のような、コンパイル時における型レベルのプログラミング
See you soon!
参考情報
• The Magnet Pattern (from spray blog)
• Type Classes as Objects and Implicits (PDF)
• Spray on Akka (Typesafe webinar on 2013-11-19)
QA
Image Credits