I have the following function:
def function(i: Int): IO[Either[String, Option[Int]]] = ???
I want a function of the form:
def foo(either: Either[String, Option[Int]]): IO[Either[String, Option[Int]]]
and I want it to have the following behavior:
def foo1(either: Either[String, Option[Int]])
: IO[Either[String, Option[Int]]] = either match {
case Right(Some(i)) => bar(i)
case Right(None) => IO.pure(None.asRight)
case Left(s) => IO.pure(s.asLeft)
}
I want to do it less explicitly, so I tried EitherT:
def foo2(either: Either[String, Option[Int]]):
IO[Either[String, Option[Int]]] = {
val eitherT = for {
maybe <- EitherT.fromEither[IO](either)
int <- EitherT.fromOption(maybe, "???")
x <- EitherT(bar(int))
} yield x
eitherT.value
}
but this means that Right(None) will be mapped to IO(Left("???")) which is not what I want.
is there an alternative formulation with EitherT without a match expression that is equivalent to the foo1 implementation?
more importantly, how would an implementation that uses map/traverse/biTraverse/etc. (and doesn't match on any of option/eithers) look like?
p.s. The intention here is to define a "map" function for the following type:
trait Lookup[F[_], K, A] {
def get(key: K): F[Either[FormatError, Option[A]]]
}
without match
import cats._
import cats.data._
import cats.implicits._
def bar[F[_] : Applicative](i: Int): F[Either[String, Option[Int]]] =
(i + 1).some.asRight[String].pure[F]
def foo[F[_] : Applicative : Monad : FlatMap](either: Either[String, Option[Int]]): F[Either[String, Option[Int]]] =
OptionT(EitherT(either.pure[F])).flatMap { i =>
OptionT(EitherT(bar[F](i)))
}.value.value
foo[Id](1.some.asRight)
//res0: cats.Id[Either[String,Option[Int]]] = Right(Some(2))
import cats.Applicative
import cats.syntax.applicative._
def bar[F[_]](i: Int): F[Either[String, Option[Int]]] = ???
def foo[F[_] : Applicative](either: Either[String, Option[Int]]): F[Either[String, Option[Int]]] =
either match {
case Right(Some(i)) => bar(i)
case a => a.pure[F]
}
With MonadError we can:
eliminate 1 transformer during business logic implementation, so only OptionT is needed in def foo
do not make decision upfront how we want to handle errors, so user should choose concrete EitherT:
import cats._
import cats.data._
import cats.implicits._
import monix.eval._
type ErrorHandler[F[_]] = MonadError[F, String]
def bar[F[_] : ErrorHandler : Applicative](i: Int): F[Option[Int]] =
if (i > 0) (i + 1).some.pure[F] else implicitly[ErrorHandler[F]].raiseError("error")
def foo[F[_] : ErrorHandler : Applicative : FlatMap](option: Option[Int]): F[Option[Int]] =
OptionT(option.pure[F]).flatMap { i =>
OptionT(bar[F](i))
}.value
type Effect[A] = EitherT[Task, String, A]
import monix.execution.Scheduler.Implicits.global
foo[Effect](1.some).value.runSyncUnsafe()
//Either[String, Option[Int]] = Right(Some(2))
foo[Effect](0.some).value.runSyncUnsafe()
//Either[String, Option[Int]] = Left("error")
foo[Effect](none).value.runSyncUnsafe()
//Right(None)
Related
I'm reading the scala-with-cats book and follow it's exercise. When I come to the case study: data validation, I encounter some problems.
Here is my entire code (just the same with the book):
package org.scala.ch10.final_recap
import cats.Semigroup
import cats.data.Validated
import cats.data.Validated._
import cats.data.Kleisli
import cats.data.NonEmptyList
import cats.instances.either._
import cats.syntax.apply._
import cats.syntax.semigroup._
import cats.syntax.validated._
sealed trait Predicate[E, A] {
import Predicate._
def and(that: Predicate[E, A]): Predicate[E, A] =
And(this, that)
def or(that: Predicate[E, A]): Predicate[E, A] =
Or(this, that)
/**
* This part is for Kleislis
* #return
*/
def run(implicit s: Semigroup[E]): A => Either[E, A] =
(a: A) => this(a).toEither
def apply(a: A)(implicit s: Semigroup[E]): Validated[E, A] =
this match {
case Pure(func) =>
func(a)
case And(left, right) => (left(a), right(a)).mapN((_, _) => a)
case Or(left, right) =>
left(a) match {
case Valid(_) => Valid(a)
case Invalid(e1) =>
right(a) match {
case Valid(_) => Invalid(e1)
case Invalid(e2) => Invalid(e1 |+| e2)
}
}
}
}
object Predicate {
final case class And[E, A](left: Predicate[E, A], right: Predicate[E, A]) extends Predicate[E, A]
final case class Or[E, A](left: Predicate[E, A], right: Predicate[E, A]) extends Predicate[E, A]
final case class Pure[E, A](func: A => Validated[E, A]) extends Predicate[E, A]
def apply[E, A](f: A => Validated[E, A]): Predicate[E, A] = Pure(f)
def lift[E, A](err: E, fn: A => Boolean): Predicate[E, A] = Pure(a => if(fn(a)) a.valid else err.invalid)
}
object FinalRecapPredicate {
type Errors = NonEmptyList[String]
def error(s: String): NonEmptyList[String] = NonEmptyList(s, Nil)
type Result[A] = Either[Errors, A]
type Check[A, B] = Kleisli[Result, A, B]
def check[A, B](func: A => Result[B]): Check[A, B] = Kleisli(func)
def checkPred[A](pred: Predicate[Errors, A]): Check[A, A] =
Kleisli[Result, A, A](pred.run)
def longerThan(n: Int): Predicate[Errors, String] =
Predicate.lift(
error(s"Must be longer than $n characters"),
str => str.length > n
)
val alphanumeric: Predicate[Errors, String] =
Predicate.lift(
error(s"Must be all alphanumeric characters"),
str => str.forall(_.isLetterOrDigit)
)
def contains(char: Char): Predicate[Errors, String] =
Predicate.lift(
error(s"Must contain the character $char"),
str => str.contains(char)
)
def containsOnce(char: Char): Predicate[Errors, String] =
Predicate.lift(
error(s"Must contain the character $char only once"),
str => str.count(_ == char) == 1
)
val checkUsername: Check[String, String] = checkPred(longerThan(3) and alphanumeric)
val splitEmail: Check[String, (String, String)] = check(_.split('#') match {
case Array(name, domain) =>
Right((name, domain))
case _ =>
Left(error("Must contain a single # character"))
})
val checkLeft: Check[String, String] = checkPred(longerThan(0))
val checkRight: Check[String, String] = checkPred(longerThan(3) and contains('.'))
val joinEmail: Check[(String, String), String] =
check {
case (l, r) => (checkLeft(l), checkRight(r)).mapN(_ + "#" + _)
}
val checkEmail: Check[String, String] = splitEmail andThen joinEmail
final case class User(username: String, email: String)
def createUser(username: String, email: String): Either[Errors, User] =
(checkUsername.run(username),
checkEmail.run(email)).mapN(User)
def main(args: Array[String]): Unit = {
println(createUser("", "noel#underscore.io#io"))
}
}
It supposes the code should end up with the error message Left(NonEmptyList(Must be longer than 3 characters), Must contain a single # character) But what I actually is Left(NonEmptyList(Must be longer than 3 characters))
Obviously, it does not work as expected. It fails fast instead of accumulating errors... How to fix that plz? (I've spent hours now and can't get a workaround)
This is the "problematic" part:
def createUser(username: String, email: String): Either[Errors, User] =
(checkUsername.run(username),
checkEmail.run(email)).mapN(User)
You are combining a tuple of Results, where
type Result[A] = Either[Errors, A]
This means you are really doing a mapN on a pair of Eithers, an operation provided by the Semigroupal type class. This operation will not accumulate results.
There are several reasons for this, but one that I find particularly important is the preserving of behaviour if we find ourselves using a Semigroupal / Applicative which also happens to be a Monad. Why is that a problem? Because Monads are sequencing operations, making each step depend on the previous one, and having "fail early" semantics. When using some Monad, one might expect those semantics to be preserved when using constructs from the underlying Applicative (every Monad is also an Applicative). In that case, if the implementation of Applicative used "accumulation" semantics instead of "fail early" semantics, we would ruin some important laws like referential transparency.
You can use a parallel version of mapN, called parMapN, whose contract guarantees that the implementation will be evaluating all results in parallel. This means that it definitely cannot be expected to have the "fail early" semantics, and accumulating results is fine in that case.
Note that Validated accumulates results as well, usually in a NonEmptyList or a NonEmptyChain. This is probably why you expected to see your accumulated results; the only problem is, you were not using Validated values in the "problematic" part of your code, but raw Eithers instead.
Here's some simple code that demonstrates the above concepts:
import cats.data._
import cats.implicits._
val l1: Either[String, Int] = Left("foo")
val l2: Either[String, Int] = Left("bar")
(l1, l2).mapN(_ + _)
// Left(foo)
(l1, l2).parMapN(_ + _)
// Left(foobar)
val v1: ValidatedNel[String, Int] = l1.toValidatedNel
val v2: ValidatedNel[String, Int] = l2.toValidatedNel
(v1, v2).mapN(_ + _)
// Invalid(NonEmptyList(foo, bar))
We have the following typeclass
trait Mapper[A,M[_]]{
def map[B](a:M[A], f:A => B):M[B]
}
For which we would like to provide instances for specific M. A naive implementation would look like the following:
def materializeMapperImpl[A : c.WeakTypeTag,M[_]](c:blackbox.Context)(implicit mTypeTag:c.WeakTypeTag[M[A]]): c.Expr[Mapper[A,M]] = {
import c.universe._
val aType = weakTypeOf[A]
//println(mTypeTag.toString())
c.Expr[Mapper[A,M]]{
q"""
new Mapper[$aType,$mTypeTag]{
def map[B](a1:$mTypeTag, f: $aType => B): $mTypeTag = {
???
}
}
"""
}
}
However, this won't compile because the mTypeTag is not refined in its generic arguments and would rather look as List rather than List[Int] and List[String]. How do we refine the mTypeTag, setting its argument to A and B in the macro?
package so
import scala.language.experimental.macros
import scala.language.higherKinds
import scala.reflect.macros.blackbox
trait Mapper[A, M[_]] {
def map[B](a: M[A], f: A => B): M[B]
}
object Mappers {
def f[A, M[_]]: Mapper[A, M] = macro impl[A, M]
def impl[A, M[_]](c: blackbox.Context)
(implicit
a: c.WeakTypeTag[A],
m: c.WeakTypeTag[M[_]]
): c.Tree = {
import c.universe._
val ma = m.tpe match {
case TypeRef(pre, sym, args) =>
import compat._
TypeRef(pre, sym, List(a.tpe))
}
//attention it is : new packageName.className {}
val tree =
q"""
new so.Mapper[${a.tpe},$m]{
override def map[B](a:$ma,f:($a) => B) = ???
}
"""
q"$tree"
}
}
//test
Mappers.f[Int,Option]
The ordering of implicit seems to matter when using shapeless.
Look at the example code below where it will not work.
import shapeless._
case class Userz(i: Int, j: String, k: Option[Boolean])
object r {
def func(): Userz = {
val a = Userz(100, "UserA", Some(false))
val b = Userz(400, "UserB", None)
val genA = Generic[Userz].to(a)
val genB = Generic[Userz].to(b)
val genC = genA zip genB
val genD = genC.map(Mergerz)
val User = Generic[Userz].from(genD)
return User
}
}
object Mergerz extends Poly1 {
implicit def caseInt = at[(Int, Int)] {a => a._1 + a._2}
implicit def caseString = at[(String, String)] {a => a._1 + a._2}
implicit def caseBoolean = at[(Option[Boolean], Option[Boolean])] {a => Some(a._1.getOrElse(a._2.getOrElse(false)))}
}
r.func()
And the code below which will work
import shapeless._
case class Userz(i: Int, j: String, k: Option[Boolean])
object Mergerz extends Poly1 {
implicit def caseInt = at[(Int, Int)] {a => a._1 + a._2}
implicit def caseString = at[(String, String)] {a => a._1 + a._2}
implicit def caseBoolean = at[(Option[Boolean], Option[Boolean])] {a => Some(a._1.getOrElse(a._2.getOrElse(false)))}
}
object r {
def func(): Userz = {
val a = Userz(100, "UserA", Some(false))
val b = Userz(400, "UserB", None)
val genA = Generic[Userz].to(a)
val genB = Generic[Userz].to(b)
val genC = genA zip genB
val genD = genC.map(Mergerz)
val User = Generic[Userz].from(genD)
return User
}
}
r.func()
My question is, why does the order matters? I already tried importing the implicits which doesn't work.
Because type inference within a file basically works top-to-bottom. When it's typechecking func, it hasn't gotten to caseInt etc and doesn't know they are suitable. Annotating their types should work as well, and is generally recommended for implicits, but it may be problematic when using a library like Shapeless: see Shapeless not finding implicits in test, but can in REPL for an example.
A function which I cannot change returns Scalaz Reader,
type Action[A] = Reader[Session, A]
def findAccount(s: String): Action[Account] =
Reader((session: Session) => Account(s))
I want create a new function based on findAccount(...) to return ReaderT[Option, Session, A] as in
type ActionT[A] = ReaderT[Option, Session, A]
def findAccountT(s: String): ActionT[Account] = findAccount(s).map(Option(_))
because eventually I want to do this,
def findAccBalT(accountNumber: String) = for {
acc <- findAccountT(accountNumber)
bal <- findBalanceT(acc)
} yield bal
How do I proceed? Does it make sense? Thanks
Full disclosure,
import scalaz._
import Scalaz._
trait Session {
def doSomething(): Unit
}
case class Account(number: String) extends AnyVal
case class Amount(value: Int, currency: String)
case class DBSession() extends Session {
override def doSomething = println("writing to db")
}
type Action[A] = Reader[Session, A]
type ActionT[A] = ReaderT[Option, Session, A]
def findAccount(s: String): Action[Account] =
Reader((session: Session) => Account(s))
def findBalance(account: Account): Action[Amount] =
Reader((session: Session) => Amount(333, "S$"))
// failed
def findAccountT(s: String): ActionT[Account] = findAccount(s).map(Option(_))
// failed
def findBalanceT(account: Account): ActionT[Amount] = findBalance(account).map(Option(_))
// failed
def findAccBalT(accountNumber: String) = for {
acc <- findAccountT(accountNumber)
bal <- findBalanceT(acc)
} yield bal
Short answer: You can use mapK.
Reader[A] is a type alias for ReaderT[Id, A] and ReaderT is an alias for Kleisli. Id[A] is the same as A.
In the Kleisli ScalaDoc we find mapK :
def mapK[N[_], C](f: (M[B]) => N[C]): Kleisli[N, A, C]
Since we know that Reader[Session, A] is the same as Kleisli[Id, Session, A] we can use mapK to go to Kleisli[Option, Session, A] :
import scalaz._, Scalaz._
type Session = String
type Action[A] = Reader[Session, A]
type ActionT[A] = ReaderT[Option, Session, A]
val action: Action[String] = Reader(s => s)
val actionT: ActionT[String] = action mapK Option.apply
How can I apply implement the following function?
def wrapIntoOption(state: State[S, A]): State[Option[S], Option[A]]
The bigger picture is this:
case class Engine(cylinders: Int)
case class Car(engineOpt: Option[Engine])
val engineOptLens = Lens.lensu((c, e) => c.copy(engineOpt = e), _.engineOpt)
def setEngineCylinders(count: Int): State[Engine, Int] = State { engine =>
(engine.copy(cylinders = count), engine.cylinders)
}
def setEngineOptCylinders(count: Int): State[Option[Engine], Option[Int]] = {
??? // how to reuse setEngineCylinders?
}
def setCarCylinders(count: Int): State[Car, Option[Int]] = {
engineOptLens.lifts(setEngineOptCylinders)
}
Is there nicer way to deal with Option properties?
One way to create the wrapIntoOption function would be to call run on the passed State[S, A] and convert the resulting (S, A) tuple into (Option[S], Option[A]).
import scalaz.State
import scalaz.std.option._
import scalaz.syntax.std.option._
def wrapIntoOption[S, A](state: State[S, A]): State[Option[S], Option[A]] =
State(_.fold((none[S], none[A])){ oldState =>
val (newState, result) = state.run(oldState)
(newState.some, result.some)
})
You could then define setEngineOptCylinders as :
def setEngineOptCylinders(count: Int): State[Option[Engine], Option[Int]] =
wrapIntoOption(setEngineCylinders(count))
Which you can use as :
scala> val (newEngine, oldCylinders) = setEngineOptCylinders(8).run(Engine(6).some)
newEngine: Option[Engine] = Some(Engine(8))
oldCylinders: Option[Int] = Some(6)