Skip to content

Instantly share code, notes, and snippets.

@y-yu
Last active August 29, 2015 14:26
Show Gist options
  • Save y-yu/b8cd50be6d2c6274ec2d to your computer and use it in GitHub Desktop.
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
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)))
}
}
}
sealed trait DeleteOfTwitter[A]
case class Delete[A](id: String, next: A) extends DeleteOfTwitter[A]
case class Delete[A](id: String, next: A) extends DeleteOfTwitter[A]
trait DeleteSYM {
def delete(id: Twitter[String]): Twitter[Boolean]
}
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)
}
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]
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 {
^
more(coproduct(left(Update("new tweet", more(coproduct(right(Delete("<id>", done()))))))))
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]
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)
update(
getScreeName(fetch(string("_yyu_")))
).run(DefaultEnvironment.defaultEnvironment)
delete(
update(
getScreeName(fetch(string("_yyu_")))
)
).run(DefaultEnvironment.defaultEnvironment)
let a = string("_yyu_") in
let b = fetch(a) in
let c = getScreeName(b) in
let d = update(c) in delete(d)
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)
type TwitterWithDelete[A] = Coproduct[Twitter, DeleteOfTwitter, A]
case class More[F[_]: Functor, A](k: F[Free[F, A]]) extends Free[F, A]
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]
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)))
}
}
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]
}
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)
}
case class Fetch[A](screenName: String, next: WSResponse => A) extends Twitter[A]
case class Update[A](status: String, next: A) extends Twitter[A]
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]
}
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]
}
}
}
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()))))))
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