Skip to content

Commit

Permalink
Merge pull request #940 from djspiewak/feature/translift-expression
Browse files Browse the repository at this point in the history
More flexible TransLift
  • Loading branch information
ceedubs committed Apr 3, 2016
2 parents 7e49a85 + 2dc050d commit 7cdd661
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 23 deletions.
19 changes: 17 additions & 2 deletions core/src/main/scala/cats/TransLift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
23 changes: 23 additions & 0 deletions core/src/main/scala/cats/Trivial.scala
Original file line number Diff line number Diff line change
@@ -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 {}
}
10 changes: 6 additions & 4 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)))
}

Expand Down
9 changes: 6 additions & 3 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
8 changes: 5 additions & 3 deletions core/src/main/scala/cats/data/StateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> _))
}

}
Expand Down
10 changes: 6 additions & 4 deletions core/src/main/scala/cats/data/WriterT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]] {
Expand Down
10 changes: 6 additions & 4 deletions core/src/main/scala/cats/data/XorT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

}
Expand Down
31 changes: 28 additions & 3 deletions core/src/main/scala/cats/syntax/transLift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}

0 comments on commit 7cdd661

Please sign in to comment.