diff --git a/core/src/main/scala/cats/TransLift.scala b/core/src/main/scala/cats/TransLift.scala index 82c490b558..a41636a9bb 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[_]] = Unit =:= Unit`. 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, λ[X[_] => Unit =:= Unit]] // TODO we need a Trivial typeclass } diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index bbe8a9e246..af61308cfe 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[_]] = Unit =:= Unit + + def liftT[M[_], B](ma: M[B])(implicit ev: Unit =:= Unit): 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 93a904bc5d..9ba91b9d1a 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -129,9 +129,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 515d63f5a5..997e1da838 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -63,10 +63,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, _))) } } diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index d7ff05bf80..b0f5010bad 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -194,10 +194,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..4b8082ed9e 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -6,5 +6,24 @@ trait TransLiftSyntax { } 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) + def liftT[MT[_[_],_]](implicit extract: TLExtract[MT, M]): MT[M, A] = extract.TL.liftT(ma)(extract.TC) } + +trait TLExtract[MT[_[_], _], M[_]] { + val TL: TransLift[MT] + val TC: TL.TC[M] +} + +object TLExtract { + + implicit def extract1[MT[_[_], _], M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT, TC], TC0: TC[M]): TLExtract[MT, M] = new TLExtract[MT, M] { + val TL = TL0 + val TC = TC0 + } + + implicit def extract1Id[MT[_[_], _], M[_]](implicit TL0: TransLift.Aux[MT, λ[X[_] => Unit =:= Unit]], TC0: Unit =:= Unit): TLExtract[MT, M] = extract1[MT, M, λ[X[_] => Unit =:= Unit]] + + // sigh... + implicit def extract2[MT[_[_], _, _], Z, M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], TC], TC0: TC[M]): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, TC] + implicit def extract2Id[MT[_[_], _, _], Z, M[_]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], λ[X[_] => Unit =:= Unit]], TC0: Unit =:= Unit): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, λ[X[_] => Unit =:= Unit]] +} \ No newline at end of file