コード書きましょう。
現在、UnfilteredとJavaFX使ってなんか創ってます。
Unfiltered
コード例
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import unfiltered.request._ | |
import unfiltered.response._ | |
object Server extends App { | |
val hello = unfiltered.netty.cycle.Planify { | |
case _ => ResponseString("hello world") | |
} | |
unfiltered.netty.Http(8080).plan(hello).run() | |
} |
私はAsynchronousにHttpクライアント使ってほげほげしたいので、async版を使います。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import dispatch._ | |
object Server extends App { | |
def hello(client: nio.Http) = unfiltered.netty.async.Planify { | |
case req => req.respond(ResponseString("hello world")) | |
} | |
val client = new nio.Http | |
unfiltered.netty.Http(8080).plan(hello(client)).run() | |
client.shutdown() | |
} |
私のプロジェクトではこのサーバーを
- アプリケーションとして起動
- JavaFXの裏で起動
JavaFX
SwingはオワコンなのでJavaFX使いましょう。
コード例
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.application.Application | |
import javafx.stage.Stage | |
class Client extends Application { | |
def start(stage: Stage) { | |
stage.show | |
} | |
} | |
object Client extends App { | |
Application launch classOf[Client] | |
} |
そこで、runメソッドで行われているstart, stop, destroyを直接呼ぶことにします。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Client extends App { | |
val client = new nio.Http | |
val server = unfiltered.netty.Http(8000).plan(Server.hello(client)) | |
server.start() | |
Application launch classOf[Client] | |
server.stop() | |
server.destroy() | |
client.shutdown() | |
} |
抽象化
重複したところを考えます。
まずはサーバーを組み立てるところ。
ポートも自由に設定したい。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Server extends App { | |
def hello(client: nio.Http) = unfiltered.netty.async.Planify { | |
case req => req.respond(ResponseString("hello world")) | |
} | |
def server(port: Int)(client: nio.Http) = | |
unfiltered.netty.Http(port).plan(hello(client)) | |
val client = new nio.Http | |
server(8080)(client).run() | |
client.shutdown() | |
} | |
object Client extends App { | |
val client = new nio.Http | |
val server = Server.server(8000)(client) | |
server.start() | |
Application launch classOf[Client] | |
server.stop() | |
server.destroy() | |
client.shutdown() | |
} |
次にnio.Httpのところ。
shutdownは自動でやって欲しい。
幸い、unfiltered.netty.HttpとApplication.launchはThreadをwaitしてくれるのでローンパターン的にします。
util.control.Exceptionを使うと、try - catch - finallyを生で書く必要がないことがわかりますね!
ナチュラルにScalaz使っていますが、>>>はandThenです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scalaz._, Scalaz._ | |
import scala.util.control.Exception | |
object Server extends App { | |
def client[A](f: nio.Http => A) = { | |
val client = new nio.Http | |
(Exception ultimately client.shutdown())(f(client)) | |
} | |
def hello(client: nio.Http) = unfiltered.netty.async.Planify { | |
case req => req.respond(ResponseString("hello world")) | |
} | |
def server(port: Int)(client: nio.Http) = | |
unfiltered.netty.Http(port).plan(hello(client)) | |
client(server(8080) _ >>> (_.run())) | |
} | |
object Client extends App { | |
Server.client { c => | |
val server = Server.server(8000)(c) | |
server.start() | |
Application launch classOf[Client] | |
server.stop() | |
server.destroy() | |
} | |
} |
サーバーの方は十分綺麗になりました。
クライアントのサーバーの起動、終了の部分もローンパターンで書けます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Client extends App { | |
def run[A](a: => A)(server: unfiltered.netty.Http) = { | |
Exception ultimately { | |
server.stop() | |
server.destroy() | |
} apply { | |
server.start() | |
a | |
} | |
} | |
Server.client(Server.server(8000) _ >>> run(Application launch classOf[Client])) | |
} |
綺麗になりました。
Scalaz
さて、ここまでが私的Scalaプログラミングですが、まあ、普通過ぎて面白くないですね。
そこで、IOモナドを用いて実装してみます。
まずはサーバーのコード。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import effect._ | |
object Server extends SafeApp { | |
def hello(client: nio.Http) = unfiltered.netty.async.Planify { | |
case req => req.respond(ResponseString("hello world")) | |
} | |
def client[A](f: nio.Http => IO[A]) = | |
IO(new nio.Http).bracket(_.shutdown.point[IO])(f) | |
def server(port: Int)(client: nio.Http) = | |
unfiltered.netty.Http(port).plan(hello(client)) | |
override def runc = client(server(8080) _ >>> (_.run.point[IO])) | |
} |
SafeAppはIOモナドを暗黙的に実行するためのもので、runcをオーバーライドする事でアプリケーションを構築します。
IO#bracketはtry - finallyのようなもので、リソースを扱うときに使います。
次にクライアント。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Client extends SafeApp { | |
def run[A](a: IO[A])(server: unfiltered.netty.Http) = | |
IO(server.start).bracket_(IO(server.stop) >> IO(server.destroy))(a) | |
override def runc = | |
Server.client(Server.server(8000) _ >>> run((Application launch classOf[Client]).point[IO])) | |
} |
アンダーバーが付いた関数は、前の結果を捨てるという意味があります。
Haskellの習慣ですね。
はいおしまい。
IOモナドは便利なのですが、UnfilteredやJavaFXがIOモナド用いた設計をしているわけではないので、少しばかり面倒なところがあります。
告知
第二回スタートScalazやります、多分。
0 件のコメント:
コメントを投稿