Skip to content

Commit

Permalink
Add some more OptionT instances.
Browse files Browse the repository at this point in the history
Added `Eq`, `PartialOrder`, `Order`.
Added `SemigroupK`, `MonoidK`
Added `Foldable`, `Traverse`
Added `MonadError`, `MonadCombine` and `MonadFilter` (the last two
instances are dissabled, because they violate the same laws as the
equivalent XorT instances).

Rewrote the tests to use `ListWrapper`.
I used the ListWrapper methods added by @yilinwei in typelevel#1106.
  • Loading branch information
peterneyens committed Jun 10, 2016
1 parent 88cbe95 commit 67c7f39
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 56 deletions.
177 changes: 142 additions & 35 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cats
package data

import std.option.{catsStdInstancesForOption => optionInstance}

/**
* `OptionT[F[_], A]` is a light wrapper on an `F[Option[A]]` with some
* convenient methods for working with this nested structure.
Expand Down Expand Up @@ -29,11 +31,7 @@ final case class OptionT[F[_], A](value: F[Option[A]]) {
flatMapF(a => f(a).value)

def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): OptionT[F, B] =
OptionT(
F.flatMap(value){
case Some(a) => f(a)
case None => F.pure(None)
})
OptionT(F.flatMap(value)(_.fold(F.pure[Option[B]](None))(f)))

def transform[B](f: Option[A] => Option[B])(implicit F: Functor[F]): OptionT[F, B] =
OptionT(F.map(value)(f))
Expand All @@ -45,10 +43,7 @@ final case class OptionT[F[_], A](value: F[Option[A]]) {
F.map(value)(_.getOrElse(default))

def getOrElseF(default: => F[A])(implicit F: Monad[F]): F[A] =
F.flatMap(value){
case Some(a) => F.pure(a)
case None => default
}
F.flatMap(value)(_.fold(default)(F.pure))

def collect[B](f: PartialFunction[A, B])(implicit F: Functor[F]): OptionT[F, B] =
OptionT(F.map(value)(_.collect(f)))
Expand Down Expand Up @@ -91,6 +86,24 @@ final case class OptionT[F[_], A](value: F[Option[A]]) {
XorT(cata(Xor.Right(right), Xor.Left.apply))

def show(implicit F: Show[F[Option[A]]]): String = F.show(value)

def compare(that: OptionT[F, A])(implicit o: Order[F[Option[A]]]): Int =
o.compare(value, that.value)

def partialCompare(that: OptionT[F, A])(implicit p: PartialOrder[F[Option[A]]]): Double =
p.partialCompare(value, that.value)

def ===(that: OptionT[F, A])(implicit eq: Eq[F[Option[A]]]): Boolean =
eq.eqv(value, that.value)

def traverse[G[_], B](f: A => G[B])(implicit F: Traverse[F], G: Applicative[G]): G[OptionT[F, B]] =
G.map(F.traverse(value)(o => optionInstance.traverse(o)(f)))(OptionT.apply)

def foldLeft[B](b: B)(f: (B, A) => B)(implicit F: Foldable[F]): B =
F.foldLeft(value, b)((b, o) => optionInstance.foldLeft(o, b)(f))

def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[F]): Eval[B] =
F.foldRight(value, lb)((o, lb) => optionInstance.foldRight(o, lb)(f))
}

object OptionT extends OptionTInstances {
Expand Down Expand Up @@ -132,12 +145,38 @@ object OptionT extends OptionTInstances {
def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa)(Some(_)))
}

private[data] sealed trait OptionTInstances2 {
implicit def catsDataFunctorForOptionT[F[_]:Functor]: Functor[OptionT[F, ?]] =
new Functor[OptionT[F, ?]] {
override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] =
fa.map(f)
}
private[data] sealed trait OptionTInstances extends OptionTInstances1 {
implicit def catsDataMonadRecForOptionT[F[_]](implicit F0: MonadRec[F]): MonadRec[OptionT[F, ?]] =
new OptionTMonadRec[F] { implicit val F = F0 }

implicit def catsDataFoldableForOptionT[F[_]](implicit F0: Foldable[F]): Foldable[OptionT[F, ?]] =
new OptionTFoldable[F] { implicit val F = F0 }

implicit def catsDataOrderForOptionT[F[_], A](implicit F0: Order[F[Option[A]]]): Order[OptionT[F, A]] =
new OptionTOrder[F, A] { implicit val F = F0 }

implicit def catsDataShowForOptionT[F[_], A](implicit F: Show[F[Option[A]]]): Show[OptionT[F, A]] =
functor.Contravariant[Show].contramap(F)(_.value)
}

private[data] sealed trait OptionTInstances1 extends OptionTInstances2 {
/* TODO violates right absorbtion, right distributivity, and left distributivity -- re-enable when MonadCombine laws are split in to weak/strong
implicit def catsDataMonadCombineForOptionT[F[_]](implicit F0: Monad[F]): MonadCombine[OptionT[F, ?]] =
new OptionTMonadCombine[F] { implicit val F = F0 }
*/

implicit def catsDataTraverseForOptionT[F[_]](implicit F0: Traverse[F]): Traverse[OptionT[F, ?]] =
new OptionTTraverse[F] { implicit val F = F0 }

implicit def catsDataPartialOrderForOptionT[F[_], A](implicit F0: PartialOrder[F[Option[A]]]): PartialOrder[OptionT[F, A]] =
new OptionTPartialOrder[F, A] { implicit val F = F0 }
}

private[data] sealed trait OptionTInstances2 extends OptionTInstances3 {
/* TODO violates monadFilter right empty law -- re-enable when MonadFilter laws are split in to weak/strong
implicit def catsDataMonadFilterForOptionT[F[_]](implicit F0: Monad[F]): MonadFilter[OptionT[F, ?]] =
new OptionTMonadFilter[F] { implicit val F = F0 }
*/

// 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 catsDataTransLiftForOptionT: TransLift.Aux[OptionT, Functor] =
Expand All @@ -146,44 +185,112 @@ private[data] sealed trait OptionTInstances2 {

def liftT[M[_]: Functor, A](ma: M[A]): OptionT[M, A] = OptionT.liftF(ma)
}
}

private[data] sealed trait OptionTInstances1 extends OptionTInstances2 {
implicit def catsDataMonoidKForOptionT[F[_]](implicit F0: Monad[F]): MonoidK[OptionT[F, ?]] =
new OptionTMonoidK[F] { implicit val F = F0 }

implicit def catsDataEqForOptionT[F[_], A](implicit F0: Eq[F[Option[A]]]): Eq[OptionT[F, A]] =
new OptionTEq[F, A] { implicit val F = F0 }
}

private[data] sealed trait OptionTInstances3 extends OptionTInstances4 {
implicit def catsDataMonadForOptionT[F[_]](implicit F0: Monad[F]): Monad[OptionT[F, ?]] =
new OptionTMonad[F] { implicit val F = F0 }

implicit def catsDataMonadErrorForOptionT[F[_], E](implicit F0: MonadError[F, E]): MonadError[OptionT[F, ?], E] =
new OptionTMonadError[F, E] { implicit val F = F0 }

implicit def catsDataSemigroupK[F[_]](implicit F0: Monad[F]): SemigroupK[OptionT[F, ?]] =
new OptionTSemigroupK[F] { implicit val F = F0 }
}

private[data] sealed trait OptionTInstances extends OptionTInstances1 {
implicit def catsDataMonadRecForOptionT[F[_]](implicit F0: MonadRec[F]): MonadRec[OptionT[F, ?]] =
new OptionTMonadRec[F] { implicit val F = F0 }
private[data] sealed trait OptionTInstances4 {
implicit def catsDataFunctorForOptionT[F[_]](implicit F0: Functor[F]): Functor[OptionT[F, ?]] =
new OptionTFunctor[F] { implicit val F = F0 }
}

implicit def catsDataEqForOptionT[F[_], A](implicit FA: Eq[F[Option[A]]]): Eq[OptionT[F, A]] =
FA.on(_.value)
private[data] trait OptionTSemigroupK[F[_]] extends SemigroupK[OptionT[F, ?]] {
implicit def F: Monad[F]

implicit def catsDataShowForOptionT[F[_], A](implicit F: Show[F[Option[A]]]): Show[OptionT[F, A]] =
functor.Contravariant[Show].contramap(F)(_.value)
def combineK[A](x: OptionT[F, A], y: OptionT[F, A]): OptionT[F, A] = x orElse y
}

private[data] trait OptionTMonoidK[F[_]] extends MonoidK[OptionT[F, ?]] with OptionTSemigroupK[F] {
def empty[A]: OptionT[F, A] = OptionT.none[F, A]
}

private[data] trait OptionTFunctor[F[_]] extends Functor[OptionT[F, ?]] {
implicit def F: Functor[F]

override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f)
}

private[data] trait OptionTMonad[F[_]] extends Monad[OptionT[F, ?]] {
implicit val F: Monad[F]
implicit def F: Monad[F]

def pure[A](a: A): OptionT[F, A] = OptionT.pure(a)

def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] =
fa.flatMap(f)
def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = fa.flatMap(f)

override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f)
}

override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] =
fa.map(f)
private[data] trait OptionTMonadFilter[F[_]] extends MonadFilter[OptionT[F, ?]] with OptionTMonad[F] {
def empty[A]: OptionT[F, A] = OptionT(F.pure(None))
}

private[data] trait OptionTMonadCombine[F[_]] extends MonadCombine[OptionT[F, ?]] with OptionTMonad[F] with OptionTMonoidK[F]

private[data] trait OptionTMonadRec[F[_]] extends MonadRec[OptionT[F, ?]] with OptionTMonad[F] {
implicit val F: MonadRec[F]
implicit def F: MonadRec[F]

def tailRecM[A, B](a: A)(f: A => OptionT[F, A Xor B]): OptionT[F, B] =
OptionT(F.tailRecM(a)(a0 => F.map(f(a0).value){
case None => Xor.Right(None)
case Some(Xor.Left(a1)) => Xor.Left(a1)
case Some(Xor.Right(b)) => Xor.Right(Some(b))
}))
OptionT(F.tailRecM(a)(a0 => F.map(f(a0).value)(
_.fold(Xor.right[A, Option[B]](None))(_.map(Some(_)))
)))
}

private trait OptionTMonadError[F[_], E] extends MonadError[OptionT[F, ?], E] with OptionTMonad[F] {
override def F: MonadError[F, E]

override def raiseError[A](e: E): OptionT[F, A] =
OptionT(F.map(F.raiseError[A](e))(Some(_)))

override def handleErrorWith[A](fa: OptionT[F, A])(f: E => OptionT[F, A]): OptionT[F, A] =
OptionT(F.handleErrorWith(fa.value)(f(_).value))
}

private[data] trait OptionTFoldable[F[_]] extends Foldable[OptionT[F, ?]] {
implicit def F: Foldable[F]

def foldLeft[A, B](fa: OptionT[F, A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)

def foldRight[A, B](fa: OptionT[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa.foldRight(lb)(f)
}

private[data] sealed trait OptionTTraverse[F[_]] extends Traverse[OptionT[F, ?]] with OptionTFoldable[F] {
implicit def F: Traverse[F]

override def traverse[G[_]: Applicative, A, B](fa: OptionT[F, A])(f: A => G[B]): G[OptionT[F, B]] =
fa traverse f
}

private[data] sealed trait OptionTEq[F[_], A] extends Eq[OptionT[F, A]] {
implicit def F: Eq[F[Option[A]]]

override def eqv(x: OptionT[F, A], y: OptionT[F, A]): Boolean = x === y
}

private[data] sealed trait OptionTPartialOrder[F[_], A] extends PartialOrder[OptionT[F, A]] with OptionTEq[F, A]{
override implicit def F: PartialOrder[F[Option[A]]]

override def partialCompare(x: OptionT[F, A], y: OptionT[F, A]): Double = x partialCompare y
}

private[data] sealed trait OptionTOrder[F[_], A] extends Order[OptionT[F, A]] with OptionTPartialOrder[F, A]{
override implicit def F: Order[F[Option[A]]]

override def compare(x: OptionT[F, A], y: OptionT[F, A]): Int = x compare y
}
30 changes: 25 additions & 5 deletions tests/src/test/scala/cats/tests/ListWrapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,21 @@ object ListWrapper {

def eqv[A : Eq]: Eq[ListWrapper[A]] = Eq[List[A]].on[ListWrapper[A]](_.list)

val foldable: Foldable[ListWrapper] =
new Foldable[ListWrapper] {
def foldLeft[A, B](fa: ListWrapper[A], b: B)(f: (B, A) => B): B =
Foldable[List].foldLeft(fa.list, b)(f)
val traverse: Traverse[ListWrapper] = {
val F = Traverse[List]

new Traverse[ListWrapper] {
def foldLeft[A, B](fa: ListWrapper[A], b: B)(f: (B, A) => B): B =
F.foldLeft(fa.list, b)(f)
def foldRight[A, B](fa: ListWrapper[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable[List].foldRight(fa.list, lb)(f)
F.foldRight(fa.list, lb)(f)
def traverse[G[_], A, B](fa: ListWrapper[A])(f: A => G[B])(implicit G0: Applicative[G]): G[ListWrapper[B]] = {
G0.map(F.traverse(fa.list)(f))(ListWrapper.apply)
}
}
}

val foldable: Foldable[ListWrapper] = traverse

val functor: Functor[ListWrapper] =
new Functor[ListWrapper] {
Expand Down Expand Up @@ -85,6 +92,19 @@ object ListWrapper {
}
}

val monadRec: MonadRec[ListWrapper] = {
val M = MonadRec[List]

new MonadRec[ListWrapper] {
def pure[A](x: A): ListWrapper[A] = ListWrapper(M.pure(x))
def flatMap[A, B](fa: ListWrapper[A])(f: A => ListWrapper[B]): ListWrapper[B] = ListWrapper(M.flatMap(fa.list)(a => f(a).list))
def tailRecM[A, B](a: A)(f: A => ListWrapper[cats.data.Xor[A,B]]): ListWrapper[B] =
ListWrapper(M.tailRecM(a)(a => f(a).list))
}
}

val flatMapRec: FlatMapRec[ListWrapper] = monadRec

val monad: Monad[ListWrapper] = monadCombine

val applicative: Applicative[ListWrapper] = monadCombine
Expand Down
Loading

0 comments on commit 67c7f39

Please sign in to comment.