Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a MonadTrans typeclass #844

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/Monad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ import simulacrum.typeclass
@typeclass trait Monad[F[_]] extends FlatMap[F] with Applicative[F] {
override def map[A, B](fa: F[A])(f: A => B): F[B] =
flatMap(fa)(a => pure(f(a)))

/**
* Lift a value of type M[A] into a monad transformer MT[M, A]
*/
def liftM[MT[_[_], _], A](ma: F[A])(implicit MT: MonadTrans[MT]): MT[F, A] = MT.liftM(ma)(this)
}
26 changes: 26 additions & 0 deletions core/src/main/scala/cats/MonadTrans.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cats

/**
* A typeclass which abstracts over monad transformers, providing the
* ability to lift a monad, into the the monad transformer for another
* monad.
*/
trait MonadTrans[MT[_[_], _]] {
/**
* Lift a value of type M[A] into a monad transformer MT[M, A]
*/
def liftM[M[_]: Monad, A](ma: M[A]): MT[M, A]

/**
* Lift a value of type M[A] into a monad transformer MT[M, A] using
* Unapply, this will be useful when the M[A] monad is actually not
* in the * -> * shape. For example Xor[E,A].
*/
def liftMU[MA](ma: MA)(implicit U: Unapply[Monad, MA]): MT[U.M, U.A] = {
liftM[U.M, U.A](U.subst(ma))(U.TC)
}
}

object MonadTrans {
def apply[MT[_[_], _]](implicit MT: MonadTrans[MT]) = MT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return type

}
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 @@ -136,6 +136,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 val optionTMonadTrans: MonadTrans[OptionT] = new MonadTrans[OptionT] {
def liftM[M[_], A](ma: M[A])(implicit M: Monad[M]): OptionT[M, A] =
OptionT(M.map(ma)(Some.apply))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could just delegate to liftF

}
}

private[data] sealed trait OptionTInstances extends OptionTInstances1 {
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/StreamingT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,12 @@ private[data] sealed trait StreamingTInstances extends StreamingTInstances1 {
def compare(x: StreamingT[F, A], y: StreamingT[F, A]): Int =
x.toList compare y.toList
}

implicit val streamingTMonadTrans: MonadTrans[StreamingT] =
new MonadTrans[StreamingT] {
def liftM[M[_], A](ma: M[A])(implicit M: Monad[M]): 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 writerTMonadTrans[W](implicit W: Monoid[W]): MonadTrans[({type λ[α[_], β]=WriterT[α,W,β]})#λ] =
new MonadTrans[({type λ[α[_], β]=WriterT[α,W,β]})#λ] {
def liftM[M[_], A](ma: M[A])(implicit M: Monad[M]): WriterT[M,W,A] =
WriterT(M.map(ma)((W.empty, _)))
}
}

private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 {
Expand Down
6 changes: 6 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,12 @@ private[data] abstract class XorTInstances extends XorTInstances1 {
new XorTTraverse[F, L] {
val F0: Traverse[F] = F
}

implicit def xortTMonadTrans[A]: MonadTrans[({type λ[α[_], β] = XorT[α, A, β]})#λ] =
new MonadTrans[({type λ[α[_], β] = XorT[α, A, β]})#λ] {
def liftM[M[_], B](ma: M[B])(implicit M: Monad[M]): XorT[M, A, B] =
XorT(M.map(ma)(Xor.right))
}
}

private[data] abstract class XorTInstances1 extends XorTInstances2 {
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package syntax

trait AllSyntax
extends ApplySyntax
with MonoidalSyntax
with BifunctorSyntax
with CartesianSyntax
with CoflatMapSyntax
with ComonadSyntax
with ComposeSyntax
Expand All @@ -17,6 +17,7 @@ trait AllSyntax
with GroupSyntax
with InvariantSyntax
with ListSyntax
with MonadSyntax
with MonadCombineSyntax
with MonadFilterSyntax
with OptionSyntax
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/scala/cats/syntax/monad.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cats
package syntax

trait MonadSyntax1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be private?

implicit def monadSyntaxU[FA](fa: FA)(implicit U: Unapply[Monad, FA]): Monad.Ops[U.M, U.A] =
new Monad.Ops[U.M, U.A] {
val self = U.subst(fa)
val typeClassInstance = U.TC
}
}

trait MonadSyntax extends MonadSyntax1 {
implicit def monadSyntax[F[_], A](fa: F[A])(implicit F: Monad[F]): Monad.Ops[F, A] =
new Monad.Ops[F,A] {
val self = fa
val typeClassInstance = F
}
}
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package cats
package object syntax {
object all extends AllSyntax
object apply extends ApplySyntax
object monoidal extends MonoidalSyntax
object bifunctor extends BifunctorSyntax
object cartesian extends CartesianSyntax
object coflatMap extends CoflatMapSyntax
object coproduct extends CoproductSyntax
object comonad extends ComonadSyntax
object compose extends ComposeSyntax
object contravariant extends ContravariantSyntax
Expand All @@ -16,6 +17,7 @@ package object syntax {
object functor extends FunctorSyntax
object group extends GroupSyntax
object invariant extends InvariantSyntax
object monad extends MonadSyntax
object monadCombine extends MonadCombineSyntax
object monadFilter extends MonadFilterSyntax
object option extends OptionSyntax
Expand Down
20 changes: 20 additions & 0 deletions tests/src/test/scala/cats/tests/MonadTransTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cats
package tests

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

class MonadTransTests extends CatsSuite {

test("monadTrans syntax on monad works"){

val x: OptionT[List, Int] = List(1).liftM[OptionT]
x.value should === (List(Option(1)))
}

test("we have monadTrans for XorT, OptionT, StreamingT, WriterT"){
val a: WriterT[List, Int, Int] = List(1).liftM[({type λ[α[_], β] = WriterT[α, Int, β]})#λ]
val b: StreamingT[List, Int] = List(1).liftM[StreamingT]
val c: OptionT[List, Int] = List(1).liftM[OptionT]
val d: XorT[List, Int, Int] = List(1).liftM[({type λ[α[_], β] = XorT[α, Int, β]})#λ]
}
}