package tofu.env import cats.arrow.{ArrowChoice, FunctionK, Profunctor} import cats.effect.IO import cats.{Applicative, Monad, Parallel, ~>} import monix.eval.{Task, TaskLift} import monix.execution.Scheduler import tofu.lift.{UnliftIO, UnsafeExecFuture} import tofu.optics.Contains import tofu.syntax.funk._ import scala.concurrent.Future private[env] trait EnvInstances { self: Env.type => private object anyEnvInstance extends EnvFunctorstance[Any] final implicit def envInstance[E]: EnvFunctorstance[E] = anyEnvInstance.asInstanceOf[EnvFunctorstance[E]] private object envParallelInstance extends Applicative[Env[Any, *]] { override def pure[A](x: A): Env[Any, A] = Env.pure(x) override def ap[A, B](ff: Env[Any, A => B])(fa: Env[Any, A]): Env[Any, B] = Env.parMap2(ff, fa)(_.apply(_)) override def map2[A, B, Z](fa: Env[Any, A], fb: Env[Any, B])(f: (A, B) => Z): Env[Any, Z] = Env.parMap2(fa, fb)(f) override val unit: Env[Any, Unit] = Env.unit override def map[A, B](fa: Env[Any, A])(f: A => B): Env[Any, B] = fa.map(f) override def replicateA[A](n: Int, fa: Env[Any, A]): Env[Any, List[A]] = fa.mapTask(t => Task.parSequence(Iterable.fill(n)(t)).map(_.toList)) } private object anyEnvParallelInstance extends Parallel[Env[Any, *]] { type F[a] = Env[Any, a] override def applicative: Applicative[Env[Any, *]] = envParallelInstance override def monad: Monad[Env[Any, *]] = anyEnvInstance override val sequential: ~>[Env[Any, *], Env[Any, *]] = FunctionK.id override val parallel: ~>[Env[Any, *], Env[Any, *]] = FunctionK.id } final implicit def envParallelInstance[E]: Parallel[Env[E, *]] = anyEnvParallelInstance.asInstanceOf[Parallel[Env[E, *]]] final implicit val envProfuctorInstance: Profunctor[Env] with ArrowChoice[Env] = new Profunctor[Env] with ArrowChoice[Env] { override def choose[A, B, C, D](f: Env[A, C])(g: Env[B, D]): Env[Either[A, B], Either[C, D]] = Env { case Left(a) => f.run(a).map(Left(_)) case Right(b) => g.run(b).map(Right(_)) } override def lift[A, B](f: A => B): Env[A, B] = Env(a => Task.pure(f(a))) override def first[A, B, C](fa: Env[A, B]): Env[(A, C), (B, C)] = fa.first[C] override def second[A, B, C](fa: Env[A, B]): Env[(C, A), (C, B)] = fa.second[C] override def compose[A, B, C](f: Env[B, C], g: Env[A, B]): Env[A, C] = f.compose(g) override def rmap[A, B, C](fab: Env[A, B])(f: B => C): Env[A, C] = fab.map(f) override def lmap[A, B, C](fab: Env[A, B])(f: C => A): Env[C, B] = fab.localP(f) override def id[A]: Env[A, A] = Env.context override def dimap[A, B, C, D](fab: Env[A, B])(f: C => A)(g: B => D): Env[C, D] = fab.dimap(f)(g) override def split[A, B, C, D](f: Env[A, B], g: Env[C, D]): Env[(A, C), (B, D)] = f.split(g) override def left[A, B, C](fab: Env[A, B]): Env[Either[A, C], Either[B, C]] = fab.left[C] override def right[A, B, C](fab: Env[A, B]): Env[Either[C, A], Either[C, B]] = fab.right[C] override def choice[A, B, C](f: Env[A, C], g: Env[B, C]): Env[Either[A, B], C] = f.choice(g) override def merge[A, B, C](f: Env[A, B], g: Env[A, C]): Env[A, (B, C)] = Env.parZip2(f, g) } final implicit def envUnliftSubContext[E, E1: E Contains *]: EnvUnliftSubContext[E, E1] = new EnvUnliftSubContext def envUnsafeExecFuture[E](implicit sc: Scheduler): UnsafeExecFuture[Env[E, *]] = new UnsafeExecFuture[Env[E, *]] { def lift[A](fa: Future[A]): Env[E, A] = Env.fromFuture(fa) def unlift: Env[E, Env[E, *] ~> Future] = Env.fromFunc(r => makeFunctionK(_.run(r).runToFuture)) } implicit def envUnliftIO[E](implicit toIO: TaskLift[IO]): UnliftIO[Env[E, *]] = new UnliftIO[Env[E, *]] { def lift[A](fa: IO[A]): Env[E, A] = Env.fromIO(fa) def unlift: Env[E, Env[E, *] ~> IO] = Env.fromFunc(r => funK(_.run(r).to[IO])) } }