Skip to content

Instantly share code, notes, and snippets.

@x7c1
Last active November 25, 2018 01:52
Show Gist options
  • Select an option

  • Save x7c1/f54f3e30b6e57a958a0010feb55f1f0f to your computer and use it in GitHub Desktop.

Select an option

Save x7c1/f54f3e30b6e57a958a0010feb55f1f0f to your computer and use it in GitHub Desktop.
examples without EitherT
// https://xuwei-k.hatenablog.com/entry/20140920/1411236567
import scala.concurrent.{ExecutionContext, Future}
trait UserId
sealed trait Error
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]]] = ???
}
class Example1(implicit ec: ExecutionContext) {
import UsersRepository.followers
def isFriends(user1: UserId, user2: UserId): Future[Either[Error, Boolean]] = {
followers(user1).flatMap {
case Right(a) =>
followers(user2).map {
case Right(b) =>
Right(a.exists(_.id == user2) && b.exists(_.id == user1))
case Left(e) =>
Left(e)
}
case Left(e) =>
Future.successful(Left(e))
}
}
}
class Example2(implicit ec: ExecutionContext) {
import UsersRepository.followers
private type F[L, R] = Future[Either[L, R]]
def map2[L, R1, R2, R3](
a1: => F[L, R1],
a2: => F[L, R2])(f: (R1, R2) => R3): F[L, R3] = {
a1 flatMap {
case Right(value) => a2.map(_.map(f(value, _)))
case Left(error) => Future successful Left(error)
}
}
def isFollower(u1: UserId, u2: UserId): Future[Either[Error, Boolean]] = {
followers(u1).map(_.map(_.exists(_.id == u2)))
}
def isFriends(user1: UserId, user2: UserId): Future[Either[Error, Boolean]] = {
map2(
isFollower(user2, user1),
isFollower(user1, user2),
)(_ && _)
}
}
class Example3(implicit ec: ExecutionContext) {
import UsersRepository.followers
def isFriends(user1: UserId, user2: UserId): Future[Either[Error, Boolean]] = {
followers(user1).flatMap {
case Right(a) if a.exists(_.id == user2) =>
followers(user2).map(_.map(_.exists(_.id == user1)))
case Right(_) =>
Future successful Right(false)
case Left(e) =>
Future successful Left(e)
}
}
}
class Example4(implicit ec: ExecutionContext) {
import UsersRepository.followers
def followerFollower(userId: UserId): Future[Either[Error, List[User]]] = {
followers(userId).flatMap {
case Right(a) =>
val ids = a.map(_.id)
Future.traverse(ids)(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 => ids.contains(user.id) || userId == user.id)
}
}
case Left(e) =>
Future.successful(Left(e))
}
}
}
class Example5(implicit ec: ExecutionContext) {
import UsersRepository.followers
case class LoadedUsers(errors: List[Error] = Nil, users: List[User] = Nil) {
def discard(ids: List[UserId]): LoadedUsers = copy(
users = users.filterNot(ids contains _.id)
)
def add(loaded: Either[Error, List[User]]): LoadedUsers = loaded match {
case Left(error) =>
copy(errors = errors :+ error)
case Right(xs) =>
copy(users = users ::: xs)
}
}
def followerFollower(userId: UserId): Future[LoadedUsers] = {
def retrieve(ids: List[UserId]): Future[LoadedUsers] = {
Future
.traverse(ids)(followers)
.map(_.foldLeft(LoadedUsers()) { _ add _ })
.map(_ discard userId :: ids)
}
followers(userId).flatMap {
case Right(users) => retrieve(users.map(_.id))
case Left(error) => Future successful LoadedUsers(List(error))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment