Skip to content

Commit

Permalink
Adds a new LiftTrans typeclass
Browse files Browse the repository at this point in the history
This typeclass adds a `liftT` function which is similar to the liftM
function on the scalaz MonadTrans typeclass, however this one takes into
account that you might not need the power of a monad in order to be able
to lift into a transformer, for example, we are able to Lift any M[A]
into a Kleisli[M, B, A] regardless of whether or not M is a Monad,
Functor, or any other such thing. Thish would be useful in cases where
you are constructing an applicative computation using values for which
you don't have a monad.
  • Loading branch information
stew committed Feb 2, 2016
1 parent d374a93 commit c6750c3
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 0 deletions.
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/TransLift.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cats


/**
* A typeclass which abstracts over the ability to lift an M[A] into a
* MonadTransformer
*/
trait TransLift[MT[_[_], _], M[_]] {
/**
* Lift a value of type M[A] into a monad transformer MT[M, A]
*/
def liftT[A](ma: M[A]): MT[M,A]
}
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
override def contramap[A, B](fa: Kleisli[F, A, C])(f: (B) => A): Kleisli[F, B, C] =
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)
}
}

private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ private[data] sealed trait OptionTInstances1 {
override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] =
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)
}
}

private[data] sealed trait OptionTInstances extends OptionTInstances1 {
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/StateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ private[data] sealed abstract class StateTInstances {
override def map[A, B](fa: StateT[F, S, A])(f: A => B): StateT[F, S, B] =
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 -> _))
}

}

// To workaround SI-7139 `object State` needs to be defined inside the package object
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/data/StreamingT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,11 @@ private[data] sealed trait StreamingTInstances extends StreamingTInstances1 {
def compare(x: StreamingT[F, A], y: StreamingT[F, A]): Int =
x.toList compare y.toList
}

implicit def streamingTTransLift[M[_]: Applicative]: TransLift[StreamingT, M] =
new TransLift[StreamingT, M] {
def liftT[A](ma: M[A]): StreamingT[M, A] = StreamingT.single(ma)
}
}

private[data] sealed trait StreamingTInstances1 extends StreamingTInstances2 {
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/WriterT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 {
def bimap[A, B, C, D](fab: WriterT[F, A, B])(f: A => C, g: B => D): WriterT[F, C, D] =
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, _)))
}
}

private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 {
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala/cats/data/XorT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ private[data] abstract class XorTInstances extends XorTInstances1 {
new XorTTraverse[F, L] {
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))
}

}

private[data] abstract class XorTInstances1 extends XorTInstances2 {
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ trait AllSyntax
with SplitSyntax
with StreamingSyntax
with StrongSyntax
with TransLiftSyntax
with TraverseSyntax
with XorSyntax
with ValidatedSyntax
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package object syntax {
object split extends SplitSyntax
object streaming extends StreamingSyntax
object strong extends StrongSyntax
object transLift extends TransLiftSyntax
object traverse extends TraverseSyntax
object xor extends XorSyntax
object validated extends ValidatedSyntax
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala/cats/syntax/transLift.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cats
package syntax

trait TransLiftSyntax {
implicit def transLiftSyntax[M[_], A](ma: M[A]): TransLiftOps[M, A] = new TransLiftOps(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)
}
42 changes: 42 additions & 0 deletions tests/src/test/scala/cats/tests/TransLiftTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cats
package tests

import data.{OptionT,XorT,WriterT,StreamingT, Kleisli, StateT}

class TransLiftTests extends CatsSuite {

case class NoTypeclass[A](a: A)

case class JustFunctor[A](a: A)

implicit val jfFunctor: Functor[JustFunctor] = new Functor[JustFunctor] {
override def map[A,B](fa: JustFunctor[A])(f: A => B): JustFunctor[B] = JustFunctor(f(fa.a))
}

case class JustAp[A](a: A)

implicit val jfApp: Applicative[JustAp] = new Applicative[JustAp] {
override def pure[A](a: A): JustAp[A] = JustAp(a)
override def ap[A, B](ff: JustAp[A => B])(fa: JustAp[A]): JustAp[B] = JustAp(ff.a(fa.a))
override def product[A, B](fa: JustAp[A],fb: JustAp[B]): JustAp[(A, B)] = JustAp(fa.a -> fb.a)
override def map[A, B](fa: JustAp[A])(f: A => B): JustAp[B] = JustAp(f(fa.a))
}

test("transLift for XorT, OptionT, WriterT requires only Functor") {
val d: XorT[JustFunctor, Int, Int] = JustFunctor(1).liftT[({type λ[α[_], β] = XorT[α, Int, β]})#λ]
val c: OptionT[JustFunctor, Int] = JustFunctor(1).liftT[OptionT]
val a: WriterT[JustFunctor, Int, Int] = JustFunctor(1).liftT[({type λ[α[_], β] = WriterT[α, Int, β]})#λ]

}

test("transLift for StreamingT, StateT require Applicative Functor") {
import StreamingT._

val b: StreamingT[JustAp, Int] = JustAp(1).liftT[StreamingT]
val f: StateT[JustAp, Int, Int] = JustAp(1).liftT[({type λ[α[_], β] = StateT[α, Int, β]})#λ]
}

test("transLift for, Kleisli doesn't require anything of the wrapped value"){
val e: Kleisli[NoTypeclass, Int, Int] = NoTypeclass(1).liftT[({type λ[α[_], β] = Kleisli[α, Int, β]})#λ]
}
}

0 comments on commit c6750c3

Please sign in to comment.