Skip to content

Instantly share code, notes, and snippets.

@xuwei-k
Created September 20, 2014 18:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xuwei-k/051c3b00129b7a0dfcd6 to your computer and use it in GitHub Desktop.
Save xuwei-k/051c3b00129b7a0dfcd6 to your computer and use it in GitHub Desktop.
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.1.0"
//libraryDependencies += "org.typelevel" %% "scalaz-contrib-210" % "0.1.5" // for scalaz 7.0.x
scalaVersion := "2.10.4"
package either_and_future_sample
import scalaz._
import scala.concurrent.{ExecutionContext, Future}
import syntax.traverse._
import std.list._
import std.either._
import scalaz.std.scalaFuture._ // for scalaz 7.1.0
// import scalaz.contrib.std.scalaFuture._ // for scalaz 7.0.x
import either_and_future_sample.Main.{UserId, context}
sealed trait Error
/** 引数であたえられたUserIdのUserが、そもそも存在しなかった場合 */
final case class UserNotFound(userId: UserId) extends Error
/** データベースにつながらなかったとか、そういうの */
final case class ConnectionError(message: String) extends Error
final case class User(id: UserId, name: String)
object UsersRepository {
def followers(userId: UserId): Future[Either[Error, List[User]]] = ???
def followersNoFuture(userId: UserId): Error \/ List[User] = ???
// Futureでないversion
def followerFollower0(userId: UserId): Error \/ List[User] =
for{
a <- followersNoFuture(userId)
b = a.map(_.id)
c <- b.traverseU(followersNoFuture)
} yield c.flatten.distinct.filterNot(user => b.contains(user.id) || user.id == userId)
// Futureになったもの。Scalazもできるだけ使わないで書くとすると、foldLeftなどしないといけなくて辛い
def followerFollower1(userId: UserId): Future[Either[Error, List[User]]] = {
followers(userId).flatMap{
case Right(a) =>
val b = a.map(_.id)
Future.traverse(b)(followers).map{ c =>
c.foldLeft(Right(Nil): Either[Error, List[User]]){
case (Right(d), Right(e)) =>
Right(d ::: e)
case (e @ Left(_), _) =>
e
case (_, e @ Left(_)) =>
e
}.right.map{_.filterNot(user => b.contains(user.id) || userId == user.id)}
}
case Left(e) =>
Future.successful(Left(e))
}
}
// scalaz.Traverseなども使って書いたもの。traverseやsequence関数大量でこれも少し辛い。
// そもそもテストしてないから合ってるのか自信ない。
// もうちょっと綺麗に書けるというか、書き方かなり色々ある気がする。
def followerFollower2(userId: UserId): Future[Either[Error, List[User]]] = {
followers(userId).flatMap{
case Right(a) =>
val b = a.map(_.id)
Future.traverse(b)(followers).map{ x =>
x.traverse(_.sequenceU).flatten.sequenceU.map(
_.filterNot(user => b.contains(user.id) || userId == user.id)
)
}
case Left(e) =>
Future.successful(Left(e))
}
}
}
object UsersRepositoryScalaz {
def followers(userId: UserId): EitherT[Future, Error, List[User]] = ???
// なんということでしょう!
// この場合も、scalaz.EitherTを使えば、Futureなしversionと同じコードでいけますね?
def followerFollower(userId: UserId): EitherT[Future, Error, List[User]] =
for{
a <- followers(userId)
b = a.map(_.id)
c <- b.traverseU(followers)
} yield c.flatten.distinct.filterNot(user => b.contains(user.id) || user.id == userId)
}
object Main {
// ダミーの、単一スレッドでだけ使うExecutionContext
implicit val context: ExecutionContext = new ExecutionContext {
override def execute(runnable: Runnable): Unit = runnable.run()
override def reportFailure(t: Throwable): Unit = t.printStackTrace()
}
type UserId = Long
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment