Last active
August 29, 2015 14:26
-
-
Save y-yu/b8cd50be6d2c6274ec2d to your computer and use it in GitHub Desktop.
FreeモナドとTagless FinalによるDependency InjectionのためのDSL ref: http://qiita.com/yyu/items/377513f17fec536b562e
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 Coproduct { | |
implicit def coproductFunctor[F[_], G[_]](implicit F: Functor[F], G: Functor[G]) = | |
new Functor[({type L[A] = Coproduct[F, G, A]})#L] { | |
def map[A, B](a: Coproduct[F, G, A])(f: A => B): Coproduct[F, G, B] = a.value match { | |
case Left(e) => Coproduct[F, G, B](Left(F.map(e)(f))) | |
case Right(e) => Coproduct[F, G, B](Right(G.map(e)(f))) | |
} | |
} | |
} |
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
sealed trait DeleteOfTwitter[A] | |
case class Delete[A](id: String, next: A) extends DeleteOfTwitter[A] |
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
case class Delete[A](id: String, next: A) extends DeleteOfTwitter[A] |
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
trait DeleteSYM { | |
def delete(id: Twitter[String]): Twitter[Boolean] | |
} |
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 DeleteSYMInterpreter { | |
type Twitter[A] = Reader[UseWSClient with UseOAuthCred, A] | |
implicit val deleteInterpreter = new DeleteSYM[Twitter] { | |
def delete(id: Twitter[String]): Twitter[Boolean] = | |
for { | |
idStr <- id | |
env <- ask | |
} yield { | |
val res = Await.result( | |
env.client.url(s"https://api.twitter.com/1.1/statuses/destroy/${idStr}.json") | |
.sign(env.cred) | |
.post(Map("id" -> Seq(idStr))), | |
Duration.Inf | |
) | |
res.status == 200 | |
} | |
} | |
def delete(id: Twitter[String])(implicit T: DeleteSYM[Twitter]): Twitter[Boolean] = | |
T.delete(id) | |
} |
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
sealed trait Twitter[A] | |
case class Fetch[A](screenName: String, next: WSResponse => A) extends Twitter[A] | |
case class Update[A](status: String, next: A) extends Twitter[A] | |
case class Delete[A](id: String, next: A) extends Twitter[A] |
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
Warning:(14, 47) match may not be exhaustive. | |
It would fail on the following input: Delete(_, _) | |
def map[A, B](a: Twitter[A])(f: A => B) = a match { | |
^ |
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
more(coproduct(left(Update("new tweet", more(coproduct(right(Delete("<id>", done())))))))) |
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
sealed trait Twitter[A] | |
case class Fetch[A](screenName: String, a: Future[WSResponse] => A) extends Twitter[A] | |
case class Update[A](status: String, a: A) extends Twitter[A] |
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
def string(str: String)(implicit T: TwitterSYM[Twitter]): Twitter[String] = | |
T.string(str) | |
def fetch(screenName: Twitter[String])(implicit T: TwitterSYM[Twitter]): Twitter[WSResponse] = | |
T.fetch(screenName) | |
def getScreeName(res: Twitter[WSResponse])(implicit T: TwitterSYM[Twitter]): Twitter[String] = | |
T.getScreenName(res) | |
def update(status: Twitter[String])(implicit T: TwitterSYM[Twitter]): Twitter[String] = | |
T.update(status) |
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
update( | |
getScreeName(fetch(string("_yyu_"))) | |
).run(DefaultEnvironment.defaultEnvironment) |
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
delete( | |
update( | |
getScreeName(fetch(string("_yyu_"))) | |
) | |
).run(DefaultEnvironment.defaultEnvironment) |
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
let a = string("_yyu_") in | |
let b = fetch(a) in | |
let c = getScreeName(b) in | |
let d = update(c) in delete(d) |
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
let (string("_yyu_")) (in (a => | |
let (fetch(a)) (in (b => | |
let (getScreeName(b)) (in (c => | |
let (update(c)) (in (d => | |
delete(d) | |
)))))))).run(DefaultEnvironment.defaultEnvironment) |
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
type TwitterWithDelete[A] = Coproduct[Twitter, DeleteOfTwitter, A] |
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
case class More[F[_]: Functor, A](k: F[Free[F, A]]) extends Free[F, A] |
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
case class Done[F[_]: Functor, A](a: A) extends Free[F, A] | |
case class More[F[_]: Functor, A](k: F[Free[F, A]]) extends Free[F, A] |
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 Inject { | |
implicit def reflexive[F[_]: Functor] = new Inject[F, F] { | |
def inj[A](a: F[A]): F[A] = a | |
} | |
implicit def left[F[_]: Functor, G[_]: Functor] = | |
new Inject[F, ({type L[A] = Coproduct[F, G, A]})#L] { | |
def inj[A](a: F[A]): Coproduct[F, G, A] = Coproduct[F, G, A](Left(a)) | |
} | |
implicit def right[F[_]: Functor, G[_]: Functor, H[_]: Functor](implicit I: Inject[F, G]) = | |
new Inject[F, ({type L[A] = Coproduct[H, G, A]})#L] { | |
def inj[A](a: F[A]): Coproduct[H, G, A] = Coproduct[H, G, A](Right(I.inj(a))) | |
} | |
} |
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
trait LetInSYM[R[_]] { | |
def let[A, B](a: => R[A])(l: R[A => B]): R[B] | |
def in[A, B](a: R[A] => R[B]): R[A => B] | |
} |
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 LetInSYMInterpreter { | |
type Twitter[A] = Reader[UseWSClient with UseOAuthCred, A] | |
implicit val letInInterpreter = new LetInSYM[Twitter] { | |
def let[A, B](ta: => Twitter[A])(tf: Twitter[A => B]): Twitter[B] = | |
for { | |
a <- ta | |
f <- tf | |
} yield f(a) | |
def in[A, B](f: Twitter[A] => Twitter[B]): Twitter[A => B] = { | |
reader(e => (x: A) => f(pure(x)).run(e)) | |
} | |
} | |
def let[A, B](a: => Twitter[A])(f: Twitter[A => B])(implicit T: LetInSYM[Twitter]): Twitter[B] = | |
T.let(a)(f) | |
def in[A, B](f: Twitter[A] => Twitter[B])(implicit T: LetInSYM[Twitter]): Twitter[A => B] = | |
T.in(f) | |
} |
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
case class Fetch[A](screenName: String, next: WSResponse => A) extends Twitter[A] | |
case class Update[A](status: String, next: A) extends Twitter[A] |
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
trait TwitterSYM[R[_]] { | |
def string(str: String): R[String] | |
def fetch(screenName: R[String]): R[WSResponse] | |
def getScreenName(str: R[WSResponse]): R[String] | |
def update(status: R[String]): R[String] | |
} |
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 TwitterSYMInterpreter { | |
type Twitter[A] = Reader[UseWSClient with UseOAuthCred, A] | |
implicit val twitterSYMInterpreter = new TwitterSYM[Twitter] { | |
def string(str: String): Twitter[String] = pure(str) | |
def fetch(screenName: Twitter[String]): Twitter[WSResponse] = | |
for { | |
sn <- screenName | |
env <- ask | |
} yield { | |
Await.result( | |
env.client.url("https://api.twitter.com/1.1/users/show.json") | |
.withQueryString("screen_name" -> sn) | |
.sign(env.cred) | |
.get(), | |
Duration.Inf | |
) | |
} | |
def getScreenName(res: Twitter[WSResponse]): Twitter[String] = | |
for { | |
raw <- res | |
env <- ask | |
} yield (raw.json \ "screen_name").as[String] | |
def update(status: Twitter[String]): Twitter[String] = | |
for { | |
s <- status | |
env <- ask | |
} yield { | |
val res = Await.result( | |
env.client.url("https://api.twitter.com/1.1/statuses/update.json") | |
.sign(env.cred) | |
.post(Map("status" -> Seq(s))), | |
Duration.Inf | |
) | |
(res.json \ "id_str").as[String] | |
} | |
} | |
} |
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
def inject[A](a: Twitter[A])(implicit I: Inject[Twitter, TwitterWithDelete]) = | |
I.inj(a) | |
def inject[A](a: DeleteOfTwitter[A])(implicit I: Inject[DeleteOfTwitter, TwitterWithDelete]) = | |
I.inj(a) | |
val example2: Free[TwitterWithDelete, Unit] = | |
more(inject(Update("new tweet", more(inject(Delete("<id>", done())))))) |
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
def runTwitterWithDelete[A](dsl: Free[TwitterWithDelete, A], env: UseWSClient with UseOAuthCred): Unit = dsl match { | |
case Done(a) => () | |
case More(x) => x.value match { | |
case Left(a) => a match { | |
case Fetch(screenName, f) => | |
for { | |
fws <- fetchUserByScreenName(screenName).run(env) | |
} yield runTwitterWithDelete(f(fws), env) | |
case Update(status, next) => | |
for { | |
_ <- updateStatus(status).run(env) | |
} yield runTwitterWithDelete(next, env) | |
} | |
case Right(b) => b match { | |
case Delete(id, next) => ??? | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment