Open jorolicht opened 4 weeks ago
The same repro with Scala CLI:
//> using dep org.typelevel::cats-core:2.12.0
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.EitherT
import cats.syntax.all._
type FuEiErr[T] = Future[Either[Error, T]]
case class Error(msg: String)
def fA(x:Int): FuEiErr[Int] = Future(Right(x))
def fB(x:Int): FuEiErr[Int] = Future(Right(x))
@main def TestEitherT(): Unit =
(for
a <- EitherT(fA(7))
b <- EitherT(fB(42))
yield a+b).value.map {
case Left(err) => println(s"Error: ${err.msg}")
case Right(res) => println(s"Result: ${res}")
}
We need to minimize this without the cats-core
dependency.
@jorolicht would you be able to help?
We need to minimize this without the
cats-core
dependency. @jorolicht would you be able to help?
well, try to do so
Have to add/copy some code from the cats library, hope this helps:
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.ExecutionContext.Implicits.global
type FuEiErr[T] = Future[Either[Error, T]]
case class Error(msg: String)
def fA(x:Int): FuEiErr[Int] = Future(Right(x))
def fB(x:Int): FuEiErr[Int] = Future(Right(x))
implicit def monadInstancesForFuture(implicit ec: ExecutionContext): Monad[Future] =
new Monad[Future] {
def pure[A](x: A): Future[A] = Future.successful(x)
def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f)
}
implicit def functorInstancesForFuture(implicit ec: ExecutionContext): Functor[Future] =
new Functor[Future] { def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) }
object EitherUtil:
def leftCast[A, B, C](right: Right[A, B]): Either[C, B] =
right.asInstanceOf[Either[C, B]]
def rightCast[A, B, C](left: Left[A, B]): Either[A, C] =
left.asInstanceOf[Either[A, C]]
val unit = Right(())
trait FlatMap[F[_]]:
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
trait Functor[F[_]] { self =>
def map[A, B](fa: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends FlatMap[F]:
def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a)))
def pure[A](x: A): F[A]
final case class EitherT[F[_], A, B](value: F[Either[A, B]]):
def flatMap[AA >: A, D](f: B => EitherT[F, AA, D])(implicit F: Monad[F]): EitherT[F, AA, D] =
EitherT(F.flatMap(value) {
case l @ Left(_) => F.pure(EitherUtil.rightCast(l))
case Right(b) => f(b).value
})
def map[D](f: B => D)(implicit F: Functor[F]): EitherT[F, A, D] = bimap(identity, f)
def bimap[C, D](fa: A => C, fb: B => D)(implicit F: Functor[F]): EitherT[F, C, D] =
EitherT(
F.map(value) {
case Right(b) => Right(fb(b))
case Left(a) => Left(fa(a))
}
)
@main def TestEitherT(): Unit =
(for
a <- EitherT(fA(21)) // EitherT[Future,Error,Int](fA(21)) //ok
b <- EitherT(fB(21)) // EitherT[Future,Error,Int](fB(21)) //ok
yield a+b).value.map {
case Left(err) => println(s"Error: ${err.msg}")
case Right(res) => println(s"Answer: ${res}")
}
cleaner code for demo:
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Either, Left, Right}
type FuEiErr[T] = Future[Either[String, T]]
def fA(x:Int): FuEiErr[Int] = Future.successful(Right(x))
def fB(x:Int): FuEiErr[Int] = Future.successful(Right(x))
case class EitherT[F[_], A, B](value: F[Either[A, B]]):
def map[C](f: B => C)(using functor: Functor[F]): EitherT[F, A, C] = {
EitherT(functor.map(value)(_.map(f)))
}
def flatMap[C](f: B => EitherT[F, A, C])(using monad: Monad[F]): EitherT[F, A, C] =
EitherT(monad.flatMap(value) {
case Left(a) => monad.pure(Left(a))
case Right(b) => f(b).value
})
trait Functor[F[_]]:
def map[A, B](fa: F[A])(f: A => B): F[B]
trait Monad[F[_]] extends Functor[F]:
def pure[A](a: A): F[A]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a)))
// Example instances for Future
given futureMonad(using ec: ExecutionContext): Monad[Future] = new Monad[Future] {
def pure[A](a: A): Future[A] = Future.successful(a)
def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f)
}
@main def TestEitherT(): Unit =
(for
a <- EitherT(fA(21)) // error
b <- EitherT(fB(21)) // error
// a <- EitherT[Future,String,Int](fA(21)) //ok
// b <- EitherT[Future,String,Int](fB(21)) //ok
yield a+b).value.map {
case Left(msg) => println(s"Error: ${msg}")
case Right(res) => println(s"Answer: ${res}")
}
``
Compiler version
val scala3Version = "3.3.3" with cats core library libraryDependencies += "org.typelevel" %% "cats-core" % "2.12.0"
Output
Expectation
No compilation error, works in version 2.13