Skip to content

Commit

Permalink
Merge branch 'master' into groupByNES-2282
Browse files Browse the repository at this point in the history
  • Loading branch information
kailuowang authored Jun 13, 2018
2 parents 7ce57d8 + c712a65 commit 28e2950
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 8 deletions.
7 changes: 6 additions & 1 deletion core/src/main/scala/cats/ApplicativeError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,12 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
* }}}
*/
def fromEither[A](x: E Either A): F[A] =
x.fold(raiseError, pure)
x match {
case Right(a) => pure(a)
case Left(e) => raiseError(e)
}


}

object ApplicativeError {
Expand Down
19 changes: 17 additions & 2 deletions core/src/main/scala/cats/Bitraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,26 @@ import simulacrum.typeclass
*/
@typeclass trait Bitraverse[F[_, _]] extends Bifoldable[F] with Bifunctor[F] { self =>

/** Traverse each side of the structure with the given functions */
/**
* Traverse each side of the structure with the given functions.
*
* Example:
* {{{
* scala> import cats.implicits._
*
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
*
* scala> ("1", "2").bitraverse(parseInt, parseInt)
* res0: Option[(Int, Int)] = Some((1,2))
*
* scala> ("1", "two").bitraverse(parseInt, parseInt)
* res1: Option[(Int, Int)] = None
* }}}
*/
def bitraverse[G[_]: Applicative, A, B, C, D](fab: F[A, B])(f: A => G[C], g: B => G[D]): G[F[C, D]]

/**
* Sequence each side of the structure with the given functions.
* Invert the structure from F[G[A], G[B]] to G[F[A, B]].
*
* Example:
* {{{
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ import Foldable.sentinel

def collectFirst[A, B](fa: F[A])(pf: PartialFunction[A, B]): Option[B] =
foldRight(fa, Eval.now(Option.empty[B])) { (a, lb) =>
// trick from TravsersableOnce
// trick from TraversableOnce, used to avoid calling both isDefined and apply (or calling lift)
val x = pf.applyOrElse(a, sentinel)
if (x.asInstanceOf[AnyRef] ne sentinel) Eval.now(Some(x.asInstanceOf[B]))
else lb
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,24 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
case r@Right(_) => F.pure(r.leftCast)
})

/** Combine `leftSemiflatMap` and `semiflatMap` together.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.data.EitherT
*
* scala> val eitherT: EitherT[List, String, Int] = EitherT[List, String, Int](List(Left("abc"), Right(123)))
* scala> eitherT.biSemiflatMap(string => List(string.length), int => List(int.toFloat))
* res0: cats.data.EitherT[List,Int,Float] = EitherT(List(Left(3), Right(123.0)))
* }}}
*/
def biSemiflatMap[C, D](fa: A => F[C], fb: B => F[D])(implicit F: Monad[F]): EitherT[F, C, D] =
EitherT(F.flatMap(value) {
case Left(a) => F.map(fa(a)) { c => Left(c) }
case Right(b) => F.map(fb(b)) { d => Right(d) }
})

def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int =
o.compare(value, that.value)

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 @@ -65,3 +65,4 @@ trait AllSyntaxBinCompat1
with BinestedSyntax
with ParallelFlatSyntax
with SetSyntax
with ValidatedExtensionSyntax
24 changes: 23 additions & 1 deletion core/src/main/scala/cats/syntax/applicativeError.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package cats
package syntax

import cats.data.EitherT
import cats.data.Validated.{Invalid, Valid}
import cats.data.{EitherT, Validated}

trait ApplicativeErrorSyntax {
implicit final def catsSyntaxApplicativeErrorId[E](e: E): ApplicativeErrorIdOps[E] =
Expand Down Expand Up @@ -42,6 +43,27 @@ final class ApplicativeErrorExtensionOps[F[_], E](F: ApplicativeError[F, E]) {
def fromOption[A](oa: Option[A], ifEmpty: => E): F[A] =
ApplicativeError.liftFromOption(oa, ifEmpty)(F)

/**
* Convert from cats.data.Validated
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.ApplicativeError
*
* scala> ApplicativeError[Option, Unit].fromValidated(1.valid[Unit])
* res0: scala.Option[Int] = Some(1)
*
* scala> ApplicativeError[Option, Unit].fromValidated(().invalid[Int])
* res1: scala.Option[Int] = None
* }}}
*/
def fromValidated[A](x: Validated[E, A]): F[A] =
x match {
case Invalid(e) => F.raiseError(e)
case Valid(a) => F.pure(a)
}

}

final class ApplicativeErrorIdOps[E](val e: E) extends AnyVal {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ package object syntax {
object traverse extends TraverseSyntax
object nonEmptyTraverse extends NonEmptyTraverseSyntax
object unorderedTraverse extends UnorderedTraverseSyntax
object validated extends ValidatedSyntax
object validated extends ValidatedSyntax with ValidatedExtensionSyntax
object vector extends VectorSyntax
object writer extends WriterSyntax
object set extends SetSyntax
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala/cats/syntax/validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ final class ValidatedIdSyntax[A](val a: A) extends AnyVal {
def invalid[B]: Validated[A, B] = Validated.Invalid(a)
def invalidNel[B]: ValidatedNel[A, B] = Validated.invalidNel(a)
}

trait ValidatedExtensionSyntax {
implicit final def catsSyntaxValidatedExtension[E, A](v: Validated[E, A]): ValidatedExtension[E, A] =
new ValidatedExtension(v)
}

final class ValidatedExtension[E, A](val self: Validated[E, A]) extends AnyVal {
def liftTo[F[_]](implicit F: ApplicativeError[F, E]): F[A] =
new ApplicativeErrorExtensionOps(F).fromValidated(self)
}
12 changes: 10 additions & 2 deletions docs/src/main/tut/datatypes/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ So, let's model our typed door:
import cats.Eval
import cats.data.IndexedStateT
def open: IndexedStateT[Eval, Open.type, Closed.type, Unit] = IndexedStateT.set(Closed)
def close: IndexedStateT[Eval, Closed.type, Open.type, Unit] = IndexedStateT.set(Open)
def open: IndexedStateT[Eval, Closed.type, Open.type, Unit] = IndexedStateT.set(Open)
def close: IndexedStateT[Eval, Open.type, Closed.type, Unit] = IndexedStateT.set(Closed)
```

We can now reject, at compile time, sequences of `open` and `close` that are invalid:
Expand All @@ -305,3 +305,11 @@ val valid = for {
```

Note that the inferred type of `valid` correctly models that this computation can be executed only with an initial `Closed` state.

```tut:book:fail
valid.run(Open)
```

```tut:book
valid.run(Closed)
```
18 changes: 18 additions & 0 deletions tests/src/test/scala/cats/tests/EitherTSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -471,4 +471,22 @@ class EitherTSuite extends CatsSuite {
}
}

test("biSemiflatMap consistent with leftSemiflatMap and semiFlatmap") {
forAll { (eithert: EitherT[List, String, Int], fa: String => List[Int], fb: Int => List[String]) =>
eithert.biSemiflatMap(fa, fb) should === (eithert.leftSemiflatMap(fa).semiflatMap(fb))
}
}

test("biSemiflatMap consistent with leftSemiflatMap") {
forAll { (eithert: EitherT[List, String, Int], fa: String => List[Int]) =>
eithert.biSemiflatMap(fa, List(_)) should ===(eithert.leftSemiflatMap(a => fa(a)))
}
}

test("biSemiflatMap consistent with semiflatMap") {
forAll { (eithert: EitherT[List, String, Int], fb: Int => List[String]) =>
eithert.biSemiflatMap(List(_), fb) should ===(eithert.semiflatMap(b => fb(b)))
}
}

}
6 changes: 6 additions & 0 deletions tests/src/test/scala/cats/tests/ValidatedSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,10 @@ class ValidatedSuite extends CatsSuite {
Validated.condNel(cond, s, i) should === (Either.cond(cond, s, i).toValidatedNel)
}
}

test("liftTo consistent with direct to Option") {
forAll { (v: Validated[Unit, Int]) =>
v.liftTo[Option] shouldBe v.toOption
}
}
}

0 comments on commit 28e2950

Please sign in to comment.