diff --git a/core/src/main/scala/cats/TransLift.scala b/core/src/main/scala/cats/TransLift.scala index 82c490b558..7901c55925 100644 --- a/core/src/main/scala/cats/TransLift.scala +++ b/core/src/main/scala/cats/TransLift.scala @@ -5,9 +5,24 @@ package cats * A typeclass which abstracts over the ability to lift an M[A] into a * MonadTransformer */ -trait TransLift[MT[_[_], _], M[_]] { +trait TransLift[MT[_[_], _]] { + + /** + * The typeclass which constrains liftT as a function of the type + * constructor it is given. A safe "identity" value for this type + * if your transformer does not constrain its lifted effects would + * be `type TC[M[_]] = Trivial`. A more common constraint might be + * `type TC[M[_]] = Monad[M]`. + */ + type TC[M[_]] + /** * Lift a value of type M[A] into a monad transformer MT[M, A] */ - def liftT[A](ma: M[A]): MT[M,A] + def liftT[M[_]: TC, A](ma: M[A]): MT[M, A] +} + +object TransLift { + type Aux[MT[_[_], _], TC0[_[_]]] = TransLift[MT] { type TC[M[_]] = TC0[M] } + type AuxId[MT[_[_], _]] = Aux[MT, Trivial.PH1] } diff --git a/core/src/main/scala/cats/Trivial.scala b/core/src/main/scala/cats/Trivial.scala new file mode 100644 index 0000000000..13c2d4fb28 --- /dev/null +++ b/core/src/main/scala/cats/Trivial.scala @@ -0,0 +1,23 @@ +package cats + +/** + * The "Unit typeclass". The only instance of `Trivial` is given by + * `Trivial.manifest`, and this instance is guaranteed to be in the + * implicit scope. Several convenience type aliases are provided in + * companion object, covering a few common use cases and avoiding the + * need for unnecessary lambdas (e.g. if you want a trivial typeclass + * instance for a type constructor, you should use `Trivial.PH1`). + */ +sealed trait Trivial + +object Trivial { + type P1[A] = Trivial + type PH1[F[_]] = Trivial + type P1H1[F[_], A] = Trivial + type P2[A, B] = Trivial + type P2H1[F[_], A, B] = Trivial + type P3[A, B, C] = Trivial + type P3H1[F[_], A, B, C] = Trivial + + implicit val manifest: Trivial = new Trivial {} +} \ No newline at end of file diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index bbe8a9e246..f0e827d1e9 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -115,9 +115,11 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { fa.local(f) } - implicit def kleisliTransLift[M[_], A]: TransLift[({type λ[α[_], β] = Kleisli[α, A, β]})#λ, M] = - new TransLift[({type λ[α[_], β] = Kleisli[α, A, β]})#λ, M] { - def liftT[B](ma: M[B]): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) + implicit def kleisliTransLift[A]: TransLift.AuxId[Kleisli[?[_], A, ?]] = + new TransLift[Kleisli[?[_], A, ?]] { + type TC[M[_]] = Trivial + + def liftT[M[_], B](ma: M[B])(implicit ev: Trivial): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) } } @@ -244,7 +246,7 @@ private trait KleisliStrong[F[_]] extends Strong[Kleisli[F, ?, ?]] { private trait KleisliSemigroup[F[_], A, B] extends Semigroup[Kleisli[F, A, B]] { implicit def FB: Semigroup[F[B]] - override def combine(a: Kleisli[F, A, B], b: Kleisli[F, A, B]): Kleisli[F, A, B] = + override def combine(a: Kleisli[F, A, B], b: Kleisli[F, A, B]): Kleisli[F, A, B] = Kleisli[F, A, B](x => FB.combine(a.run(x), b.run(x))) } diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index cff671687c..4ab1d31eb7 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -136,9 +136,12 @@ private[data] sealed trait OptionTInstances1 { fa.map(f) } - implicit def optionTTransLift[M[_]: Functor]: TransLift[OptionT, M] = - new TransLift[OptionT, M] { - def liftT[A](ma: M[A]): OptionT[M, A] = OptionT.liftF(ma) + // do NOT change this to val! I know it looks like it should work, and really I agree, but it doesn't (for... reasons) + implicit def optionTTransLift: TransLift.Aux[OptionT, Functor] = + new TransLift[OptionT] { + type TC[M[_]] = Functor[M] + + def liftT[M[_]: Functor, A](ma: M[A]): OptionT[M, A] = OptionT.liftF(ma) } } diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 9e20c1cadb..439b86a6ff 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -143,9 +143,11 @@ private[data] sealed abstract class StateTInstances { fa.map(f) } - implicit def stateTLift[M[_], S](implicit M: Applicative[M]): TransLift[({type λ[α[_], β] = StateT[α, S, β]})#λ, M] = - new TransLift[({type λ[α[_], β] = StateT[α, S, β]})#λ, M] { - def liftT[A](ma: M[A]): StateT[M, S, A] = StateT(s => M.map(ma)(s -> _)) + implicit def stateTLift[S]: TransLift.Aux[StateT[?[_], S, ?], Applicative] = + new TransLift[StateT[?[_], S, ?]] { + type TC[M[_]] = Applicative[M] + + def liftT[M[_]: Applicative, A](ma: M[A]): StateT[M, S, A] = StateT(s => Applicative[M].map(ma)(s -> _)) } } diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 5cf55f8fd0..dacba37d28 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -65,10 +65,12 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { fab.bimap(f, g) } - implicit def writerTTransLift[M[_], W](implicit M: Functor[M], W: Monoid[W]): TransLift[({type λ[α[_], β] = WriterT[α,W,β]})#λ, M] = - new TransLift[({type λ[α[_], β] = WriterT[α,W,β]})#λ, M] { - def liftT[A](ma: M[A]): WriterT[M, W, A] = - WriterT(M.map(ma)((W.empty, _))) + implicit def writerTTransLift[W](implicit W: Monoid[W]): TransLift.Aux[WriterT[?[_], W, ?], Functor] = + new TransLift[WriterT[?[_], W, ?]] { + type TC[M[_]] = Functor[M] + + def liftT[M[_]: Functor, A](ma: M[A]): WriterT[M, W, A] = + WriterT(Functor[M].map(ma)((W.empty, _))) } implicit def writerTShow[F[_], L, V](implicit F: Show[F[(L, V)]]): Show[WriterT[F, L, V]] = new Show[WriterT[F, L, V]] { diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 71d4a97c28..4e0331f24a 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -196,10 +196,12 @@ private[data] abstract class XorTInstances extends XorTInstances1 { val F0: Traverse[F] = F } - implicit def xortTransLift[M[_],E](implicit M: Functor[M]): TransLift[({type λ[α[_], β] = XorT[α,E,β]})#λ, M] = - new TransLift[({type λ[α[_], β] = XorT[α,E,β]})#λ, M] { - def liftT[A](ma: M[A]): XorT[M,E,A] = - XorT(M.map(ma)(Xor.right)) + implicit def xortTransLift[E]: TransLift.Aux[XorT[?[_], E, ?], Functor] = + new TransLift[XorT[?[_], E, ?]] { + type TC[M[_]] = Functor[M] + + def liftT[M[_]: Functor, A](ma: M[A]): XorT[M,E,A] = + XorT(Functor[M].map(ma)(Xor.right)) } } diff --git a/core/src/main/scala/cats/syntax/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index c5fc39ef8d..d5570581d9 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -2,9 +2,34 @@ package cats package syntax trait TransLiftSyntax { - implicit def transLiftSyntax[M[_], A](ma: M[A]): TransLiftOps[M, A] = new TransLiftOps(ma) + implicit def transLiftSyntax[E](ma: E)(implicit U: Unapply[Trivial.PH1, E]): TransLiftOps[U.M, U.A] = new TransLiftOps(U.subst(ma)) } -final class TransLiftOps[M[_], A](val ma: M[A]) extends AnyVal { - def liftT[MT[_[_],_]](implicit TL: TransLift[MT, M]): MT[M,A] = TL.liftT(ma) +final class TransLiftOps[M0[_], A](val ma: M0[A]) extends AnyVal { + import TLExtract._ + + def liftT[MT0[_[_],_]](implicit extract: TLExtract[SingletonMT { type MT[F[_], B] = MT0[F, B] }, SingletonM { type M[B] = M0[B] }]): MT0[M0, A] = extract.TL.liftT(ma)(extract.TC) +} + +trait TLExtract[MTS <: TLExtract.SingletonMT, MS <: TLExtract.SingletonM] { + val TL: TransLift[MTS#MT] + val TC: TL.TC[MS#M] } + +object TLExtract { + + trait SingletonMT { + type MT[F[_], A] + } + + trait SingletonM { + type M[A] + } + + implicit def extract[MTS <: SingletonMT, MS <: SingletonM, TC[_[_]]](implicit TL0: TransLift.Aux[MTS#MT, TC], TC0: TC[MS#M]): TLExtract[MTS, MS] = new TLExtract[MTS, MS] { + val TL = TL0 + val TC = TC0 + } + + implicit def extractId[MTS <: SingletonMT, MS <: SingletonM](implicit TL0: TransLift.Aux[MTS#MT, Trivial.PH1]): TLExtract[MTS, MS] = extract[MTS, MS, Trivial.PH1] +} \ No newline at end of file