Skip to content

Commit

Permalink
Merge pull request #1 from adelbertc/freet-either
Browse files Browse the repository at this point in the history
Freet either
  • Loading branch information
raulraja authored Aug 20, 2016
2 parents ba3c1d6 + d75686d commit c416127
Show file tree
Hide file tree
Showing 84 changed files with 1,834 additions and 477 deletions.
59 changes: 59 additions & 0 deletions bench/src/main/scala/cats/bench/MapMonoidBench.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cats.bench

import cats.instances.list._
import cats.instances.int._
import cats.instances.map._

import scalaz.std.anyVal._
import scalaz.std.list._
import scalaz.std.map._

import org.openjdk.jmh.annotations.{ Benchmark, Scope, State }

@State(Scope.Benchmark)
class MapMonoidBench {

val words: List[String] =
for {
c <- List("a", "b", "c", "d", "e")
t <- 1 to 100
} yield c * t

val maps: List[Map[String, Int]] = (words ++ words).map(s => Map(s -> 1))

@Benchmark def combineAllCats: Map[String, Int] =
cats.Monoid[Map[String, Int]].combineAll(maps)

@Benchmark def combineCats: Map[String, Int] =
maps.foldLeft(Map.empty[String, Int]) {
case (acc, m) => cats.Monoid[Map[String, Int]].combine(acc, m)
}

@Benchmark def combineScalaz: Map[String, Int] =
maps.foldLeft(Map.empty[String, Int]) {
case (acc, m) => scalaz.Monoid[Map[String, Int]].append(acc, m)
}

@Benchmark def combineDirect: Map[String, Int] =
maps.foldLeft(Map.empty[String, Int]) {
case (acc, m) => m.foldLeft(acc) {
case (m, (k, v)) => m.updated(k, v + m.getOrElse(k, 0))
}
}

@Benchmark def combineGeneric: Map[String, Int] =
combineMapsGeneric[String, Int](maps, 0, _ + _)

def combineMapsGeneric[K, V](maps: List[Map[K, V]], z: V, f: (V, V) => V): Map[K, V] =
maps.foldLeft(Map.empty[K, V]) {
case (acc, m) => m.foldLeft(acc) {
case (m, (k, v)) => m.updated(k, f(v, m.getOrElse(k, z)))
}
}

@Benchmark def foldMapCats: Map[String, Int] =
cats.Foldable[List].foldMap(maps)(identity)

@Benchmark def foldMapScalaz: Map[String, Int] =
scalaz.Foldable[List].foldMap(maps)(identity)
}
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM)
.settings(catsSettings)
.settings(noPublishSettings)
.settings(commonJvmSettings)
.settings(libraryDependencies ++= Seq(
"org.scalaz" %% "scalaz-core" % "7.2.5"))
.enablePlugins(JmhPlugin)

// cats-js is JS-only
Expand Down
16 changes: 8 additions & 8 deletions core/src/main/scala/cats/ApplicativeError.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cats

import cats.data.{Xor, XorT}
import cats.data.EitherT
import scala.util.{ Failure, Success, Try }
import scala.util.control.NonFatal

Expand Down Expand Up @@ -37,21 +37,21 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
def handleError[A](fa: F[A])(f: E => A): F[A] = handleErrorWith(fa)(f andThen pure)

/**
* Handle errors by turning them into [[cats.data.Xor.Left]] values.
* Handle errors by turning them into [[scala.util.Either]] values.
*
* If there is no error, then an [[cats.data.Xor.Right]] value will be returned instead.
* If there is no error, then an `scala.util.Right` value will be returned instead.
*
* All non-fatal errors should be handled by this method.
*/
def attempt[A](fa: F[A]): F[E Xor A] = handleErrorWith(
map(fa)(Xor.right[E, A])
)(e => pure(Xor.left(e)))
def attempt[A](fa: F[A]): F[Either[E, A]] = handleErrorWith(
map(fa)(Right(_): Either[E, A])
)(e => pure(Left(e)))

/**
* Similar to [[attempt]], but wraps the result in a [[cats.data.XorT]] for
* Similar to [[attempt]], but wraps the result in a [[cats.data.EitherT]] for
* convenience.
*/
def attemptT[A](fa: F[A]): XorT[F, E, A] = XorT(attempt(fa))
def attemptT[A](fa: F[A]): EitherT[F, E, A] = EitherT(attempt(fa))

/**
* Recover from certain errors by mapping them to an `A` value.
Expand Down
17 changes: 8 additions & 9 deletions core/src/main/scala/cats/Bitraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,23 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
*
* scala> val rightSome: Option[String] Xor Option[Int] = Xor.right(Some(3))
* scala> val rightSome: Either[Option[String], Option[Int]] = Either.right(Some(3))
* scala> rightSome.bisequence
* res0: Option[String Xor Int] = Some(Right(3))
* res0: Option[Either[String, Int]] = Some(Right(3))
*
* scala> val rightNone: Option[String] Xor Option[Int] = Xor.right(None)
* scala> val rightNone: Either[Option[String], Option[Int]] = Either.right(None)
* scala> rightNone.bisequence
* res1: Option[String Xor Int] = None
* res1: Option[Either[String, Int]] = None
*
* scala> val leftSome: Option[String] Xor Option[Int] = Xor.left(Some("foo"))
* scala> val leftSome: Either[Option[String], Option[Int]] = Either.left(Some("foo"))
* scala> leftSome.bisequence
* res2: Option[String Xor Int] = Some(Left(foo))
* res2: Option[Either[String, Int]] = Some(Left(foo))
*
* scala> val leftNone: Option[String] Xor Option[Int] = Xor.left(None)
* scala> val leftNone: Either[Option[String], Option[Int]] = Either.left(None)
* scala> leftNone.bisequence
* res3: Option[String Xor Int] = None
* res3: Option[Either[String, Int]] = None
* }}}
*/
def bisequence[G[_]: Applicative, A, B](fab: F[G[A], G[B]]): G[F[A, B]] =
Expand Down
7 changes: 1 addition & 6 deletions core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cats

import scala.annotation.tailrec
import cats.data.Xor
import cats.syntax.all._

/**
Expand Down Expand Up @@ -302,11 +301,7 @@ private[cats] trait EvalInstances extends EvalInstances0 {
def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f)
def extract[A](la: Eval[A]): A = la.value
def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa))
def tailRecM[A, B](a: A)(f: A => Eval[A Xor B]): Eval[B] =
f(a).flatMap(_ match {
case Xor.Left(a1) => tailRecM(a1)(f) // recursion OK here, since flatMap is lazy
case Xor.Right(b) => Eval.now(b)
})
def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] = defaultTailRecM(a)(f)
}

implicit def catsOrderForEval[A: Order]: Order[Eval[A]] =
Expand Down
5 changes: 2 additions & 3 deletions core/src/main/scala/cats/FlatMap.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package cats

import cats.data.Xor
import simulacrum.typeclass

/**
Expand Down Expand Up @@ -93,7 +92,7 @@ import simulacrum.typeclass
flatMap(fa)(if (_) ifTrue else ifFalse)

/**
* Keeps calling `f` until a `[[cats.data.Xor.Right Right]][B]` is returned.
* Keeps calling `f` until a `scala.util.Right[B]` is returned.
*
* Based on Phil Freeman's
* [[http://functorial.com/stack-safety-for-free/index.pdf Stack Safety for Free]].
Expand All @@ -106,5 +105,5 @@ import simulacrum.typeclass
* using recursive flatMap. Such an implementation will only be stack safe if
* the Monad is trampolined.
*/
def tailRecM[A, B](a: A)(f: A => F[A Xor B]): F[B]
def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B]
}
27 changes: 12 additions & 15 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,8 @@ import simulacrum.typeclass
* For example:
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> val F = Foldable[List]
* scala> F.traverse_(List("333", "444"))(parseInt)
* res0: Option[Unit] = Some(())
Expand All @@ -208,19 +207,18 @@ import simulacrum.typeclass
/**
* Behaves like traverse_, but uses [[Unapply]] to find the
* [[Applicative]] instance for `G` - used when `G` is a
* type constructor with two or more parameters such as [[cats.data.Xor]]
* type constructor with two or more parameters such as [[scala.util.Either]]
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Xor[String, Int] =
* | try { Xor.Right(s.toInt) }
* | catch { case _: NumberFormatException => Xor.Left("boo") }
* scala> def parseInt(s: String): Either[String, Int] =
* | try { Right(s.toInt) }
* | catch { case _: NumberFormatException => Left("boo") }
* scala> val F = Foldable[List]
* scala> F.traverseU_(List("333", "444"))(parseInt)
* res0: Xor[String, Unit] = Right(())
* res0: Either[String, Unit] = Right(())
* scala> F.traverseU_(List("333", "zzz"))(parseInt)
* res1: Xor[String, Unit] = Left(boo)
* res1: Either[String, Unit] = Left(boo)
* }}}
*
* Note that using `traverse_` instead of `traverseU_` would not compile without
Expand Down Expand Up @@ -253,16 +251,15 @@ import simulacrum.typeclass
/**
* Behaves like sequence_, but uses [[Unapply]] to find the
* [[Applicative]] instance for `G` - used when `G` is a
* type constructor with two or more parameters such as [[cats.data.Xor]]
* type constructor with two or more parameters such as [[scala.util.Either]]
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> val F = Foldable[List]
* scala> F.sequenceU_(List(Xor.right[String, Int](333), Xor.Right(444)))
* res0: Xor[String, Unit] = Right(())
* scala> F.sequenceU_(List(Xor.right[String, Int](333), Xor.Left("boo")))
* res1: Xor[String, Unit] = Left(boo)
* scala> F.sequenceU_(List(Either.right[String, Int](333), Right(444)))
* res0: Either[String, Unit] = Right(())
* scala> F.sequenceU_(List(Either.right[String, Int](333), Left("boo")))
* res1: Either[String, Unit] = Left(boo)
* }}}
*
* Note that using `sequence_` instead of `sequenceU_` would not compile without
Expand Down
7 changes: 3 additions & 4 deletions core/src/main/scala/cats/Monad.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package cats

import cats.data.Xor
import simulacrum.typeclass

/**
Expand All @@ -22,9 +21,9 @@ import simulacrum.typeclass
* to write this method (all cats types have a stack safe version
* of this). When this method is safe you can find an `implicit r: RecursiveTailRecM`.
*/
protected def defaultTailRecM[A, B](a: A)(fn: A => F[A Xor B]): F[B] =
protected def defaultTailRecM[A, B](a: A)(fn: A => F[Either[A, B]]): F[B] =
flatMap(fn(a)) {
case Xor.Right(b) => pure(b)
case Xor.Left(nextA) => defaultTailRecM(nextA)(fn)
case Right(b) => pure(b)
case Left(nextA) => defaultTailRecM(nextA)(fn)
}
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/MonadCombine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import simulacrum.typeclass
* Fold over the inner structure to combine all of the values with
* our combine method inherited from MonoidK. The result is for us
* to accumulate all of the "interesting" values of the inner G, so
* if G is Option, we collect all the Some values, if G is Xor,
* if G is Option, we collect all the Some values, if G is Either,
* we collect all the Right values, etc.
*/
def unite[G[_], A](fga: F[G[A]])(implicit G: Foldable[G]): F[A] =
Expand Down
15 changes: 6 additions & 9 deletions core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> List("1", "2", "3").traverse(parseInt)
* res0: Option[List[Int]] = Some(List(1, 2, 3))
* scala> List("1", "two", "3").traverse(parseInt)
Expand All @@ -39,14 +38,13 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Xor[String, Int] = Xor.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number")
* scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number")
* scala> val ns = List("1", "2", "3")
* scala> ns.traverseU(parseInt)
* res0: Xor[String, List[Int]] = Right(List(1, 2, 3))
* scala> ns.traverse[Xor[String, ?], Int](parseInt)
* res1: Xor[String, List[Int]] = Right(List(1, 2, 3))
* res0: Either[String, List[Int]] = Right(List(1, 2, 3))
* scala> ns.traverse[Either[String, ?], Int](parseInt)
* res1: Either[String, List[Int]] = Right(List(1, 2, 3))
* }}}
*/
def traverseU[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] =
Expand All @@ -57,9 +55,8 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> val x = Option(List("1", "two", "3"))
* scala> x.traverseM(_.map(parseInt))
* res0: List[Option[Int]] = List(Some(1), None, Some(3))
Expand Down
21 changes: 8 additions & 13 deletions core/src/main/scala/cats/arrow/Choice.scala
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
package cats
package arrow

import cats.data.Xor

import simulacrum.typeclass

@typeclass trait Choice[F[_, _]] extends Category[F] {

/**
* Given two `F`s (`f` and `g`) with a common target type, create a new `F`
* with the same target type, but with a source type of either `f`'s source
* type OR `g`'s source type.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> val b: Boolean => String = _ + " is a boolean"
* scala> val i: Int => String = _ + " is an integer"
* scala> val f: (Boolean Xor Int) => String = Choice[Function1].choice(b, i)
* scala> val f: (Either[Boolean, Int]) => String = Choice[Function1].choice(b, i)
*
* scala> f(Xor.right(3))
* scala> f(Right(3))
* res0: String = 3 is an integer
*
* scala> f(Xor.left(false))
* scala> f(Left(false))
* res0: String = false is a boolean
* }}}
*/
def choice[A, B, C](f: F[A, C], g: F[B, C]): F[Xor[A, B], C]
def choice[A, B, C](f: F[A, C], g: F[B, C]): F[Either[A, B], C]

/**
* An `F` that, given a source `A` on either the right or left side, will
* return that same `A` object.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> val f: (Int Xor Int) => Int = Choice[Function1].codiagonal[Int]
* scala> val f: (Either[Int, Int]) => Int = Choice[Function1].codiagonal[Int]
*
* scala> f(Xor.right(3))
* scala> f(Right(3))
* res0: Int = 3
*
* scala> f(Xor.left(3))
* scala> f(Left(3))
* res1: Int = 3
* }}}
*/
def codiagonal[A]: F[Xor[A, A], A] = choice(id, id)
def codiagonal[A]: F[Either[A, A], A] = choice(id, id)
}
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/arrow/FunctionK.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package arrow

import cats.data.{Xor, Coproduct}
import cats.data. Coproduct

trait FunctionK[F[_], G[_]] extends Serializable { self =>
def apply[A](fa: F[A]): G[A]
Expand All @@ -17,8 +17,8 @@ trait FunctionK[F[_], G[_]] extends Serializable { self =>
def or[H[_]](h: FunctionK[H, G]): FunctionK[Coproduct[F, H, ?], G] =
new FunctionK[Coproduct[F, H, ?], G] {
def apply[A](fa: Coproduct[F, H, A]): G[A] = fa.run match {
case Xor.Left(ff) => self(ff)
case Xor.Right(gg) => h(gg)
case Left(ff) => self(ff)
case Right(gg) => h(gg)
}
}
}
Expand Down
Loading

0 comments on commit c416127

Please sign in to comment.