Skip to content

Commit

Permalink
Merge pull request #853 from stew/translift
Browse files Browse the repository at this point in the history
Adds a new LiftTrans typeclass
  • Loading branch information
adelbertc committed Feb 10, 2016
2 parents 33ba89b + c6750c3 commit 6dbd4b0
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 @@ -458,6 +458,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 6dbd4b0

Please sign in to comment.