Scala-cats: reader-composition - scala

import cats.data.ReaderT
import cats.instances.either._
trait Service1
trait Service2
case class Cats(name:String)
type FailFast[A] = Either[List[String], A]
type Env = (Service1, Service2, Cats)
type ReaderEnvFF[A] = ReaderT[FailFast, Env, A]
def toReaderEnvFF[A](input:A):ReaderEnvFF[A] =
ReaderT((_:Env) => Right(input))
def c:ReaderEnvFF[Cats] =
for {
cats <- toReaderEnvFF((_:Env)._3)
} yield cats // This line is 26
Error:
Error:(26, 11) type mismatch; found : T1.this.Env =>
com.savdev.Cats
(which expands to) ((com.savdev.Service1, com.savdev.Service2, com.savdev.Cats)) => com.savdev.Cats required: com.savdev.Cats }
yield cats
Can you please explain, why cats is not com.savdev.Cats? And why in the error, it says that it is expanded to a function with return method [Cats], bot not FailFast[Cats]
I try to apply exactly the same logic as here:
trait Service1 { def s1f = Option(10) }
trait Service2 {
type ReaderS1[A] = ReaderT[Option,Service1,A]
import cats.syntax.applicative._
import cats.instances.option._
def s2f:ReaderS1[Int] =
for {
r2 <- ReaderT((_: Service1).s1f)
r1 <- 1.pure[ReaderS1]
} yield r1 + r2
}
In this example I could convert function Service1.s1f to its result r2 and it works fine. Why I cannot, for instance write something like:
for {
cats <- ReaderT((_:Env)._3)
...

toReaderEnvFF((_: Env)._3) in cats <- toReaderEnvFF((_: Env)._3) is actually toReaderEnvFF[A]((_: Env)._3) for some type A. What is A now? Since (_: Env)._3 (aka input in toReaderEnvFF) is of type Env => Cats then type A is Env => Cats. So toReaderEnvFF((_: Env)._3) is of type ReaderEnvFF[Env => Cats] and cats in cats <- toReaderEnvFF((_: Env)._3) is of type Env => Cats.
In x <- SomeMonad[T] variable x is of type T (now SomeMonad is ReaderEnvFF, T is Env => Cats).
ReaderT((_: Service1).s1f) in your second example is of type ReaderT[Option, Service1, Int] so r2 in r2 <- ReaderT((_: Service1).s1f) is of type Int. But in your first example toReaderEnvFF((_: Env)._3) is of type ReaderEnvFF[Env => Cats] aka ReaderT[FailFast, Env, Env => Cats] so cats in cats <- toReaderEnvFF((_: Env)._3) is of type Env => Cats. That's the difference.
If you want to work with ReaderEnvFF[Cats] then you should change cats <- toReaderEnvFF(???). For example
def c:ReaderEnvFF[Cats] =
for {
cats <- toReaderEnvFF(Cats("aaa"))
} yield cats

Related

Scala: ReaderT composition with different contexts and dependencies

Example of s3f1 and s3f2 functions that return different ReaderT:
type FailFast[A] = Either[List[String], A]
trait Service1 { def s1f:Option[Int] = Some(10) }
trait Service2 { def s2f:FailFast[Int] = Right(20) }
import cats.instances.option._
def s3f1: ReaderT[Option, Service1, Int] =
for {
r1 <- ReaderT((_: Service1).s1f)
} yield r1 + 1
import cats.syntax.applicative._
import cats.instances.either._
type ReaderService2FF[A] = ReaderT[FailFast, Service2, A]
def s3f2: ReaderService2FF[Int] =
for {
r1 <- ReaderT((_: Service2).s2f)
r2 <- 2.pure[ReaderService2FF]
} yield r1 + r2
I try to compose these two functions that return readers with different F[_] context and dependencies: ReaderT[Option, Service1, Int] and ReaderT[FailFast, Service2, Int]
I have to combine somehow the F[_] context, which means combine FailFast with Option. I assume, it makes sense to combine it to FailFast[Option]:
type Env = (Service1, Service2)
type FFOption[A] = FailFast[Option[A]]
type ReaderEnvFF[A] = ReaderT[FFOption, Env, A]
How to compose s3f1 and s3f2:
def c: ReaderEnvFF[Int] =
for {
r1 <- //s3f1
r2 <- //s3f2
} yield r1 + r2
Since you try to compose monads FailFast and Option in FFOption, you should use one more monad transformer, so FFOption[A] should be OptionT[FailFast, A] rather than just FailFast[Option[A]].
import cats.instances.option._
import cats.instances.either._
import cats.syntax.applicative._
import cats.syntax.either._
import cats.syntax.option._
type Env = (Service1, Service2)
type FFOption[A] = OptionT[FailFast, A]
type ReaderEnvFF[A] = ReaderT[FFOption, Env, A]
def c: ReaderEnvFF[Int] =
for {
r1 <- ReaderT[FFOption, Env, Int](p => OptionT(Either.right(s3f1.run(p._1))))
r2 <- ReaderT[FFOption, Env, Int](p => OptionT(s3f2.run(p._2).map(_.some)))
} yield r1 + r2
This can be rewritten with with local and mapF:
def c: ReaderEnvFF[Int] =
for {
r1 <- s3f1.local[Env](_._1).mapF[FFOption, Int](opt => OptionT(opt.asRight))
r2 <- s3f2.local[Env](_._2).mapF[FFOption, Int](ff => OptionT(ff.map(_.some)))
} yield r1 + r2

Can't compile example from book Advanced Scala with Cats

I'm reading the book and playing with examples there. The problem is I can't reproduce this example:
type Logged[A] = Writer[List[String], A]
// Example method that returns nested monads:
def parseNumber(str: String): Logged[Option[Int]] =
util.Try(str.toInt).toOption match {
case Some(num) => Writer(List(s"Read $str"), Some(num))
case None => Writer(List(s"Failed on $str"), None)
}
// Example combining multiple calls to parseNumber:
def addNumbers(
a: String,
b: String,
c: String
): Logged[Option[Int]] = {
import cats.data.OptionT
// Transform the incoming stacks to work on them:
val result = for {
a <- OptionT(parseNumber(a))
b <- OptionT(parseNumber(b))
c <- OptionT(parseNumber(c))
} yield a + b + c
// Return the untransformed monad stack:
result.value
}
Compiling errors:
Error:(30, 9) could not find implicit value for parameter F: cats.Monad[advancedScalaWithCats.monadTransformation.Part4UsagePatterns.Logged]
b <- OptionT(parseNumber(b))
Error:(30, 9) not enough arguments for method flatMap: (implicit F: cats.Monad[advancedScalaWithCats.monadTransformation.Part4UsagePatterns.Logged])cats.data.OptionT[advancedScalaWithCats.monadTransformation.Part4UsagePatterns.Logged,Int].
Unspecified value parameter F.
b <- OptionT(parseNumber(b))
Add
import cats.instances.list._

Generic container type (higher kind) for "map" in scala

I am trying to define a case class that is parameterized with a container type (higher kind). This container type can be anything as long as it has a map method defined.
I would like to achieve this result suggested by the following code:
import scala.language.higherKinds
case class Test[A, C[A]](init: A, trans: Map[A,C[A]]) {
def convert[B](conv: A => B): Test[B, C[B]] = {
val _init = conv(init)
val _trans = trans map {case (k,v) => (conv(k) -> (v map {x => conv(x)})}
Test(_init, _trans)
}
}
the problem lies in the v map {x => conv(x)} part of the code. Since no bound was defined for C[A] it, obviously, does not compile.
The thing is that this container type can be an Id (Scalaz style, but with map instead of |>), and Option, or a collection (Seq, Set, List, etc.)
Is there any way to tell the scala compiler that the container type will must have the map method?
The best way to achieve what you want is by using the Functor type class.
For example here is an example using Cats.
(You could do the same using Scalaz).
import scala.language.higherKinds
import cats.Functor
import cats.syntax.functor.toFunctorOps
final case class Test[A, C[_]](init: A, trans: Map[A,C[A]])(implicit CFunctor: Functor[C]) {
def convert[B](conv: A => B): Test[B, C] = {
val _init = conv(init)
val _trans = trans map { case (k,v) => conv(k) -> v.map(x => conv(x)) }
Test(_init, _trans)
}
}
However, if for some reason you can't or don't want to use Cats/Scalaz, you could try Structural Types.
import scala.language.higherKinds
import scala.language.reflectiveCalls
final case class Test[A, C[A] <: { def map[B](f: A => B): C[B]}](init: A, trans: Map[A,C[A]]){
def convert[B](conv: A => B): Test[B, C] = {
val _init = conv(init)
val _trans = trans map { case (k,v) => conv(k) -> v.map(x => conv(x)) }
Test(_init, _trans)
}
}
Nevertheless, note that the last one will work for Option, but will fail for List, simple because the map method in List receives an implicit CanBuildFrom, and for that reason it isn't equals to the one you want.

Scala Future[Option[T]] Un Packing

In the following snippet,
trait MyType1; trait MyType2
import scala.concurrent.Promise
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
I pass in p1 and p2 to another function where I complete the Promise using a successful Future. After the call to this function, I try to read the value in the Promise:
trait Test {
// get the Future from the promise
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem <- f1
f1Elem <- someF1Elem
f2Elem <- f1Elem
} yield {
// do something with f1Elem and f2Elem
"..."
}
}
When I try to compile this, I get some compiler issues.
Error:(52, 19) type mismatch;
found : Option[Nothing]
required: scala.concurrent.Future[?]
flElem <- someF1Elem
^
IntelliJ shows no errors or what-so-ever and the types look aligned. But I'm not sure why the compiler is unhappy! Any clues?
Your for comprehension types must be consistent, so you cannot freely mix Option and Future in the way you do.
In your example, f1 returns a Future[Option[MyType1]] while f2 returns a Future[MyType2]
Remember that a for comprehension desugars to a series of flatMap/map and potentially withFilter.
Also the (simplified) signatures of flatMap for Future[A] and Option[A] are
def flatMap[B](f: A => Future[B]): Future[B]
def flatMap[B](f: A => Option[B]): Option[B]
The first two steps of the for-comprehension desugar to
f1.flatMap { someF1Elem =>
someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]`
...
}
}
see the error now?
Now, to fix this there's a few approaches you can follow. One very handy is to use Monad Transformers, which allow you to combine (for instance) Future and Option into a single monad type OptionT.
Specifically you can go back and forth from Future[Option[A]] to OptionT[Future, A]. The basic idea is that you can flatMap on the latter and extract a value of type A.
But before that, you need to make your types of the "right shape", so that both are a Future[Option[Something]]
Here's an example using scalaz
import scalaz._; import Scalaz._ ; import scalaz.OptionT._
import scala.concurrent.{ Promise, Future }
import scala.concurrent.ExecutionContext.Implicits.global
trait MyType1
trait MyType2
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
val f1 = p1.future
val f2 = p2.future
val res = for {
f1Elem <- optionT(f1)
f2Elem <- f2.liftM[OptionT]
} yield {
println(s"$f1Elem $f2Elem")
}
optionT builds an OptionT[Future, A] given a Future[Option[A]], while liftM[OptionT] achieves the same when you have a Future[A]
The type of res is OptionT[Future, Unit] in this case, and you can get a Future[Option[Unit]] by calling run on it.
Looking at "How does yield work", your for comprehension is equivalent to
f1.flatMap(someF1Elem: Option[MyType1] =>
someF1Elem.flatMap(f1Elem =>
f1Elem.map(f2Elem =>
"..."
)
)
)
Basically what happens is this: flatMap on a Future is defined to take a function that returns another Future.
This gives you a similar error:
<console>:64: error: value map is not a member of MyType1
f1Elem.map(f2Elem =>
^
<console>:63: error: type mismatch;
found : Option[Nothing]
required: scala.concurrent.Future[?]
someF1Elem.flatMap(f1Elem =>
^
If you want the operation to perform asynchronously, i.e. really mapping the Future instances, you will have to get back to a Future[Option[X]]. As Kim suggested, you can nest the comprehension:
import scala.concurrent.{Future, Promise}
def test: Future[Option[String]] = {
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
// ... run code that completes the promises
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem: Option[MyType1] <- f1
f2Elem <- f2
} yield {
for (f1Elem <- someF1Elem) yield "something"
}
}
You could also make the resulting Future fail when the option is not defined (NoSuchElementException)
def test: Future[String] = {
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
// ... run code that completes the promises
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem: Option[MyType1] <- f1
f2Elem <- f2
} yield {
val f1Elem = someF1Elem.get // may cause an exception and fail the future
"something"
}
}
In a for comprehension, the expressions on the right hand side of the <- have to have compatible types. That is because for comprehensions are basically syntactic sugar for nested flatMap calls. To solve this problem, you can either have a for comprehension nested within another for comprehension or you can use methods like get or map on the Options.
Another option is to use monad transformers, but that's beyond the scope of this answer.

Combine IO and State Monad in Scalaz

I am attempting to write a simple program using the State monad in scalaz that will modify some state based on input passed in by the user. How is the best accomplished. Currently I have:
import scalaz._
import Scalaz._
import effect._
import IO._
def acc = for {
i <- init
_ <- modify{s: Int => 100}
v <- readLn
_ <- modify{s: Int => v}
} yield ()
which throws:
<console>:25: error: polymorphic expression cannot be instantiated to expected type;
found : [B]scalaz.effect.IO[B]
required: scalaz.IndexedStateT[scalaz.Id.Id,Int,?,?]
You can't do this directly, but the monad transformer version isn't too terrible:
import scalaz._, Scalaz._, effect._, IO._
type IS[S, A] = StateT[IO, S, A]
type ISInt[A] = IS[Int, A]
val ms = MonadState[IS, Int]
import ms._
def acc = for {
i <- init
_ <- modify(s => 100)
v <- readLn.liftIO[ISInt]
_ <- modify(s => v.toInt)
} yield ()
This gives you a StateT[IO, Int, Unit] that you can turn into an IO action with acc.exec(whatever).
It's worth noting your code could be cleaned up a bit—the init is unnecessary, for example, and you might as well use put if you're throwing away the arguments in modify, etc. It's also worth noting that in practice something like an IORef might be more practical here.