Last active
April 2, 2019 13:53
-
-
Save neko-kai/e070d96e3b3b10a6f200542714585639 to your computer and use it in GitHub Desktop.
Tagless final for ZIO via quantified constraints
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package quantified | |
import cats.Monad | |
import scala.language.implicitConversions | |
/** | |
* C[_] constraint applied to type F[_, _] quantified in first parameter, i.e. | |
* | |
* {{{ | |
* forall E. C[F[E, ?]] | |
* }}} | |
* | |
* Usage: | |
* | |
* {{{ | |
* import quantified._ | |
* import cats.syntax.all._ | |
* | |
* def poly[F[_, _]: Monad2] = abc[F].flatMap(cba[F]) *> 123.pure[F[Nothing, ?]] | |
* }}} | |
* | |
* With covariance: | |
* | |
* {{{ | |
* import quantified._ | |
* import quantified.Monad2._ | |
* import cats.syntax.all._ | |
* | |
* def nothing[F[+ _, +_] : Monad2]: F[Nothing, Unit] = ().pure[F[Nothing, ?]] | |
* def throwable[F[+ _, +_] : Monad2]: F[Throwable, Unit] = nothing | |
* def runtime[F[+ _, +_] : Monad2]: F[RuntimeException, Unit] = nothing | |
* def test[F[+_, +_]: Monad2]: F[Throwable, Int] = | |
* for { | |
* x <- 2.pure[F[Nothing, ?]] | |
* _ <- nothing | |
* _ <- runtime | |
* _ <- throwable | |
* y <- 7.pure[F[Nothing, ?]] | |
* } yield x + y | |
* }}} | |
* | |
* */ | |
final class Quant[+C[_[_]], F[_, _]] private (private val erased: C[F[Any, ?]]) extends AnyVal { | |
type State = Defined | |
@inline def underlying[E]: C[F[E, ?]] = erased.asInstanceOf[C[F[E, ?]]] | |
} | |
object Quant extends QuantImplicits { | |
@inline final def apply[C[_[_]], F[_, _]](implicit q: Quant[C, F]): Quant[C, F] = q | |
final def apply[C[_[_]]]: QuantApply[C] = new QuantApply[C]() | |
private[Quant] final class QuantApply[C[_[_]]](private val dummy: Boolean = false) extends AnyVal { | |
def apply[F[_, _], E](c: C[F[E, ?]]): Quant[C, F] = new Quant[C, F](c.asInstanceOf[C[F[Any, ?]]]) | |
} | |
private sealed trait Private { | |
private[Quant] type T | |
} | |
private[Quant] final val Private = new Private {} | |
/** If somehow there's an instance that's valid for a fresh private type T >: Nothing <: Any, | |
it means that this instance must be a polymorphic instance valid for all type parameters X >: Nothing <: Any **/ | |
implicit def fromImplicitDef[C[_[_]], F[_, _]](implicit lp: shapeless.LowPriority, c: C[F[Private.T, ?]]): Quant[C, F] { type State = Derived } = | |
Quant[C].apply(c.asInstanceOf[C[F[Any, ?]]]).asInstanceOf[Quant[C, F] { type State = Derived }] | |
} | |
trait QuantImplicits extends Any { | |
final type Monad2[F[_, _]] = Quant[Monad, F] | |
object Monad2 { | |
final def apply[F[_, _]: Monad2]: Monad2[F] = implicitly[Quant.Monad2[F]] | |
implicit final class Monad2CovariantFlatMap[F[+_, _], E, A](self: F[E, A])(implicit ev: Monad2[F] { type State = Defined }) { | |
@inline def flatMap[E1 >: E, B](f: A => F[E1, B]): F[E1, B] = | |
apply[F].apply[E1].flatMap(self)(f) | |
@inline def *>[E1 >: E, B](f: => F[E1, B]): F[E1, B] = | |
flatMap[E1, B](_ => f) | |
} | |
} | |
@inline implicit final def instantiate[C[_[_]], F[_, _], E](implicit lp: shapeless.LowPriority, q: Quant[C, F] { type State = Defined }): C[F[E, ?]] = q.underlying | |
// Also works via Param: | |
// @inline implicit final def instantiate[C[_[_]], F[_, _], E](implicit lp: shapeless.LowPriority, c: Param[Lambda[E => C[F[E, ?]]]] { type State = Defined }): C[F[E, ?]] = c.underlying | |
@inline implicit final def conversion[C[_[_]], F[_, _], E](q: Quant[C, F]): C[F[E, ?]] = q.underlying | |
} | |
private[quantified] trait Defined | |
private[quantified] trait Derived | |
/** Universally quantified instance for C */ | |
final class Param[C[_]](private val erased: C[_]) { | |
type State = Defined | |
def underlying[A]: C[A] = erased.asInstanceOf[C[A]] | |
} | |
object Param extends ParamImplicits { | |
def apply[C[_]: Param]: Param[C] = implicitly | |
private sealed trait Private { | |
private[Param] trait T extends Any | |
} | |
private[Param] final val Private = new Private {} | |
/** If there's somehow an instance for a fresh private type >: Nothing <: Any, | |
it means that this instance is a polymorphic instance valid for all type parameters >: Nothing <: Any **/ | |
implicit def fromImplicitDef[C[_]](implicit lp: shapeless.LowPriority, c: C[Private.T]): Param[C] { type State = Derived } = | |
new Param(c).asInstanceOf[Param[C] { type State = Derived }] | |
} | |
trait ParamImplicits { | |
@inline implicit final def instantiate[C[_], A](implicit lp: shapeless.LowPriority, c: Param[C] { type State = Defined }): C[A] = c.underlying | |
@inline implicit final def conversion[C[_], A](c: Param[C]): C[A] = c.underlying | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment