Combine different "containers" in cats XorT - scala

For example, we have some services with different "containers" Future and Option:
//first service with Future
class FirstService {
getData(): XorT[Future, ServiceError, SomeData]
}
//second service with Optin
class SecondService {
getData(): XorT[Option, ServiceError, SomeData]
}
How do we can combine them to use one for comprehension to avoid type mismatch?
val result = for {
data1 <- firstService.getData()
data2 <- secondService.getData() // type mismatch required XorT[Future, ServiceError, SomeData]
} yield mergeResult(data1, data2)

XorT[F, A, B] is just a convenient wrapper over F[A Xor B], so you question essentially is: how to combine a Future and an Option. Because you still have to return a Future in some form, this mainly becomes : how to handle the Option.
There are several possibilities :
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.XorT
import cats.implicits._
type ServiceError = String
type FutureErrorOr[A] = XorT[Future, ServiceError, A]
val fXorT: FutureErrorOr[Int] = XorT.right(Future.successful(1))
val oXorT: XorT[Option, ServiceError, Int] = XorT.right(1.some)
Turn the Option into a Future (None to Future.failed) :
val opt2fut: FutureErrorOr[Int] =
XorT(oXorT.value.fold(
Future.failed[ServiceError Xor Int](new NoSuchElementException())(
Future.successful _))
for { a <- fXort; b <- opt2fut } yield a + b
Turn the Option into a ServiceError Xor ? (None to Xor.Left) :
val opt2xor: FutureErrorOr[Int] =
XorT.fromXor[Future](oXorT.value.getOrElse("no elem".left))
for { a <- fXort; b <- opt2xor } yield a + b
Change your return type to XorT[Future, ServiceError, Option[X]] (this might not be useful if you need to use the X in the rest of the for comprehension) :
val optInside: FutureErrorOr[Option[Int]] =
XorT.fromXor[Future](oXorT.value.sequenceU)
for { a <- fXorT; b <- optInside } yield b.map(_ + a)

One of the possible ways to solve this problem make common Container monad for different types (Future, Option):
trait Container[+A] {
def map[B](f: A => B): Container[B]
def flatMap[B](f: A => Container[B]): Container[B]
}
// Empty container for value
class EmptyContainer[+A](value: A) extends Container[A] {
override def map[B](f: (A) => B): Container[B] = new EmptyContainer[B](f(value))
override def flatMap[B](f: (A) => Container[B]): Container[B] = f(value)
}
// Implement container for Option
class OptionContainer[+A](option: Option[A]) extends Container[A] {
override def map[B](f: (A) => B): Container[B] = new OptionContainer[B](option.map(f))
override def flatMap[B](f: (A) => Container[B]): Container[B] = option match {
case Some(value) => f(value)
case None => new OptionContainer[B](None)
}
}
// Implement container for Future
class FutureContainer[+A](future: Future[A]) extends Container[A] {
override def map[B](f: (A) => B): Container[B] = new FutureContainer[B](future.map(f))
// TODO: can be better!!!
override def flatMap[B](f: (A) => Container[B]): Container[B] = {
val promise = Promise[B]()
future.onComplete {
case Success(a) => f(a).map(b => promise.success(b))
case Failure(exception) => promise.failure(exception)
}
new FutureContainer[B](promise.future)
}
}
You can add an own implementation for any others types.
// Monad for Container
object Container {
implicit def monad = new Monad[Container] {
def flatMap[A, B](fa: Container[A])(f: (A) => Container[B]): Container[B] = fa.flatMap(f)
def pure[A](x: A): Container[A] = new EmptyContainer[A](x)
}
}
Our service now has view:
class SomeContainerService {
def getSomeDate(): XorT[Container, Error, SomeData] =
XorT.right(Option(SomeData()).toContainer)
def getRemoteDate(): XorT[Container, Error, SomeData] =
XorT.right(Future(SomeData()).toContainer)
}
Extensions methods for both future and option:
def toContainer = OptionContainer(option)
def toContainer = FutureContainer(future)
And for-comprehension work fine:
val result: XorT[Container, Error, SomeData] = for {
data1 <- someContainerService.getRemoteDate() // future
data2 <- someContainerService.getSomeDate() // option
} yield {
mergeResult(data1, data2)
}

Related

Is it possible to optimise a Free Monad program before execution?

I've recently picked up the Free Monad pattern using cats in an attempt to create a DSL which can be "simplified" before execution. For example, let's say I create a language for interacting with lists:
sealed trait ListAction[A]
case class ListFilter[A](in: List[A], p: A => Boolean) extends ListAction[List[A]]
case class ListMap[A, B](in: List[A], f: A => B) extends ListAction[List[B]]
type ListProgram[A] = Free[ListAction, A]
Before executing any program built with these actions, I want to optimise it by transforming subsequent filters into a single filter and transforming subsequent maps into a single map in order to avoid iterating over the list multiple times:
// Pseudo code - doesn't compile, just illustrates my intent
def optimise[A](program: ListProgram[A]): ListProgram[A] = {
case ListFilter(ListFilter(in, p1), p2) => optimise(ListFilter(in, { a: A => p1(a) && p2(a) }))
case ListMap(ListMap(in, f1), f2) => optimise(ListMap(in, f2 compose f1))
}
Is this possible using the Free Monad, either by inspecting the last action when adding to the program or by optimising as above? Thanks very much.
Below is the code I've been using to create my programs:
trait ListProgramSyntax[A] {
def program: ListProgram[List[A]]
def listFilter(p: A => Boolean): ListProgram[List[A]] = {
program.flatMap { list: List[A] =>
Free.liftF[ListAction, List[A]](ListFilter(list, p))
}
}
def listMap[B](f: A => B): ListProgram[List[B]] = program.flatMap { list =>
Free.liftF(ListMap(list, f))
}
}
implicit def syntaxFromList[A](list: List[A]): ListProgramSyntax[A] = {
new ListProgramSyntax[A] {
override def program: ListProgram[List[A]] = Free.pure(list)
}
}
implicit def syntaxFromProgram[A](existingProgram: ListProgram[List[A]]): ListProgramSyntax[A] = {
new ListProgramSyntax[A] {
override def program: ListProgram[List[A]] = existingProgram
}
}
For example:
val program = (1 to 5).toList
.listMap(_ + 1)
.listMap(_ + 1)
.listFilter(_ % 3 == 0)
EDIT: After my colleague searched for "Free Monad optimize" using the American spelling we found a good answer to this question asserting it is not possible to do this before interpretation.
However, it must surely be possible to interpret the program to produce an optimised version of it and then interpret that to retrieve our List[A]?
I've managed to get what I want by just defining my "program" structure in a recursive ADT:
sealed trait ListAction[A]
case class ListPure[A](list: List[A]) extends ListAction[A]
case class ListFilter[A](previous: ListAction[A], p: A => Boolean) extends ListAction[A]
case class ListMap[A, B](previous: ListAction[A], f: A => B) extends ListAction[B]
trait ListActionSyntax[A] {
def previousAction: ListAction[A]
def listFilter(p: A => Boolean): ListFilter[A] = ListFilter(previousAction, p)
def listMap[B](f: A => B): ListMap[A, B] = ListMap(previousAction, f)
}
implicit def syntaxFromList[A](list: List[A]): ListActionSyntax[A] = {
new ListActionSyntax[A] {
override def previousAction: ListAction[A] = ListPure(list)
}
}
implicit def syntaxFromProgram[A](existingProgram: ListAction[A]): ListActionSyntax[A] = {
new ListActionSyntax[A] {
override def previousAction: ListAction[A] = existingProgram
}
}
def optimiseListAction[A](action: ListAction[A]): ListAction[A] = {
def trampolinedOptimise[A](action: ListAction[A]): Eval[ListAction[A]] = {
action match {
case ListFilter(ListFilter(previous, p1), p2) =>
Eval.later {
ListFilter(previous, { e: A => p1(e) && p2(e) })
}.flatMap(trampolinedOptimise(_))
case ListMap(ListMap(previous, f1), f2) =>
Eval.later {
ListMap(previous, f2 compose f1)
}.flatMap(trampolinedOptimise(_))
case ListFilter(previous, p) =>
Eval.defer(trampolinedOptimise(previous)).map { optimisedPrevious =>
ListFilter(optimisedPrevious, p)
}
case ListMap(previous, f) =>
Eval.defer(trampolinedOptimise(previous)).map { optimisedPrevious =>
ListMap(optimisedPrevious, f)
}
case pure: ListPure[A] => Eval.now(pure)
}
}
trampolinedOptimise(action).value
}
def executeListAction[A](action: ListAction[A]): List[A] = {
def trampolinedExecute[A](action: ListAction[A]): Eval[List[A]] = {
action match {
case ListPure(list) =>
Eval.now(list)
case ListMap(previous, f) =>
Eval.defer(trampolinedExecute(previous)).map { list =>
list.map(f)
}
case ListFilter(previous, p) =>
Eval.defer(trampolinedExecute(previous)).map { list =>
list.filter(p)
}
}
}
trampolinedExecute(action).value
}
This has the downside that I don't get stack-safety for free and have to ensure my optimisation and execution methods are properly trampolined.

Stacking Free Monads

I'm learning about the Free monads, and I've put together a simple example in Scala where I use them to define two domain specific languages.
The first monad deals with the side effects of a repository. I have implemented an interpreter that uses the state monad to manage the state, but in a real program I'd use a database.
The second monad deals with IO.
import cats.data.State
import cats.{Id, ~>}
import cats.free.Free
import cats.free.Free.liftF
final case class Todo(title: String, body: String)
def represent(todo: Todo) = s"${todo.title}: ${todo.body}"
sealed trait CRUDActionA[T]
final case class Find(key: String) extends CRUDActionA[Option[Todo]]
final case class Add(data: Todo) extends CRUDActionA[Unit]
type CRUDAction[T] = Free[CRUDActionA, T]
def find(key: String): CRUDAction[Option[Todo]] = liftF[CRUDActionA, Option[Todo]](Find(key))
def add(data: Todo): CRUDAction[Unit] = liftF[CRUDActionA, Unit](Add(data))
type TodosState[A] = State[List[Todo], A]
val repository: CRUDActionA ~> TodosState = new (CRUDActionA ~> TodosState) {
def apply[T](fa: CRUDActionA[T]): TodosState[T] = fa match {
case Add(todo) => State.modify(todos => todos :+ todo)
case Find(title) => State.inspect(todos => todos find (_.title == title))
}
}
sealed trait IOActionA[T]
final case class Out(str: String) extends IOActionA[Unit]
type IOAction[T] = Free[IOActionA, T]
def out(str: String): IOAction[Unit] = liftF[IOActionA, Unit](Out(str))
val io: IOActionA ~> Id = new (IOActionA ~> Id) {
override def apply[A](fa: IOActionA[A]): Id[A] = fa match {
case Out(todo) => println(todo)
}
}
Then, I can put together these two "programs"
def addNewTodo: Free[CRUDActionA, Option[Todo]] = for {
_ <- add(Todo(title = "Must do", body = "Must do something"))
todo <- find("Must do")
} yield todo
def outProgram(todo: Todo): IOAction[Unit] = for {
_ <- out(represent(todo))
} yield ()
And run them doing
val (_, mayBeTodo) = (addNewTodo foldMap repository run List()).value
outProgram(mayBeTodo.get).foldMap(io)
I understand this is far from ideal, and I'd like to write a program as and an interpreter that supports:
def fullProgram = for {
_ <- add(Todo(title = "Must do", body = "Must do something"))
todo <- find("Must do") // This is an option!!!
_ <- out(represent(todo)) // But represent expects a Todo
} yield ()
So the questions are:
How can I stack the two monads together into a "fullProgram"
How can I compose the two interpreters into a new interpreter?
How do I deal with the Option[Todo] returned by find, and then passed to
represent
Answer to questions 1 & 2:
type TodoApp[A] = Coproduct[IOActionA, CRUDActionA, A]
class CRUDActions[F[_]](implicit I: Inject[CRUDActionA, F]) {
def find(key: String): Free[F, Option[Todo]] = Free.inject[CRUDActionA, F](Find(key))
def add(data: Todo): Free[F, Unit] = Free.inject[CRUDActionA, F](Add(data))
}
object CRUDActions {
implicit def crudActions[F[_]](implicit I: Inject[CRUDActionA, F]): CRUDActions[F] = new CRUDActions[F]
}
class IOActions[F[_]](implicit I: Inject[IOActionA, F]) {
def out(str: String): Free[F, Unit] = Free.inject[IOActionA, F](Out(str))
}
object IOActions {
implicit def ioActions[F[_]](implicit I: Inject[IOActionA, F]): IOActions[F] = new IOActions[F]
}
def fullProgram(implicit C : CRUDActions[TodoApp], I : IOActions[TodoApp]): Free[TodoApp, Unit] = {
for {
_ <- C.add(Todo(title = "Must do", body = "Must do something"))
todo <- C.find("Must do")
_ <- I.out(represent(todo.get))
} yield ()
}
object ConsoleCatsInterpreter extends (IOActionA ~> Id) {
def apply[A](i: IOActionA[A]) = i match {
case Out(prompt) => println(prompt).asInstanceOf[A]
}
}
object MutableListCrudInterpreter extends (CRUDActionA ~> Id) {
val data = new ListBuffer[Todo]
override def apply[A](fa: CRUDActionA[A]): Id[A] = fa match {
case Add(todo) => data.append(todo).asInstanceOf[A]
case Find(title) => data.find( _.title == title).asInstanceOf[A]
}
}
val interpreter: TodoApp ~> Id = ConsoleCatsInterpreter or MutableListCrudInterpreter
fullProgram.foldMap(interpreter)

How to test a function-like Monad with cats + discipline

I created a Monad-like type that is a lot like the Play Json Reads[T] type, called ReadYamlValue.
trait ReadYamlValue[T] {
def read(json: YamlValue): ReadResult[T]
// ... methods include map, flatMap, etc
}
I created a cat Monad instance for this:
implicit val ReadYamlValueMonad: Monad[ReadYamlValue] = new Monad[ReadYamlValue] {
override def flatMap[A, B](fa: ReadYamlValue[A])(f: A => ReadYamlValue[B]): ReadYamlValue[B] = {
fa flatMap f
}
override def tailRecM[A, B](a: A)(f: A => ReadYamlValue[Either[A, B]]): ReadYamlValue[B] = {
ReadYamlValue.read[B] { yaml =>
#tailrec def readB(reader: ReadYamlValue[Either[A, B]]): ReadResult[B] = {
reader.read(yaml) match {
case Good(Left(nextA)) => readB(f(nextA))
case Good(Right(b)) => Good(b)
case Bad(error) => Bad(error)
}
}
readB(f(a))
}
}
override def pure[A](x: A): ReadYamlValue[A] = ReadYamlValue.success(x)
}
And then I wanted to test it with the MonadLaws and ScalaCheck.
class CatsTests extends FreeSpec with discipline.MonadTests[ReadYamlValue] {
monad[Int, Int, Int].all.check()
}
But I get:
could not find implicit value for parameter EqFA: cats.Eq[io.gloriousfuture.yaml.ReadYamlValue[Int]]
How do I define Eq for what is effectively a function? Comparing equality of a function seems like it isn't what I want... Is my ReadYamlValue class not a Monad or even a Functor for that matter?
One way to do this is to generate an arbitrary sample and compare equality of the result:
implicit def eqReadYaml[T: Eq: FormatYamlValue: Arbitrary]: Eq[ReadYamlValue[T]] = {
Eq.instance { (a, b) =>
val badYaml = arbitrary[YamlValue].getOrThrow
val goodValue = arbitrary[T].getOrThrow
val goodYaml = Yaml.write(goodValue)
Seq(badYaml, goodYaml).forall { yaml =>
(a.read(yaml), b.read(yaml)) match {
case (Good(av), Good(bv)) => Eq.eqv(av, bv)
case (Bad(ae), Bad(be)) => Eq.eqv(ae, be)
case _ => false
}
}
}
}
But this seems like it is sidestepping the definition of equality a bit. Is there a better or more canonical way to do this?
It looks like using Arbitrary instances is how Circe does it:
https://github.com/travisbrown/circe/blob/master/modules/testing/shared/src/main/scala/io/circe/testing/EqInstances.scala
They take a stream of 16 samples and compare the results.

How to create a method which invokes another service and return a Future?

I want to define a method, which will return a Future. And in this method, it will call another service which returns also a Future.
We have defined a BusinessResult to represent Success and Fail:
object validation {
trait BusinessResult[+V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T]
def map[T](f: V => T): BusinessResult[T]
}
sealed case class Success[V](t:V) extends BusinessResult[V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T] = {
f(t)
}
def map[T](f: V => T): BusinessResult[T] = {
Success(f(t))
}
}
sealed case class Fail(e:String) extends BusinessResult[Nothing] {
def flatMap[T](f: Nothing => BusinessResult[T]):BusinessResult[T] = this
def map[T](f: Nothing => T): BusinessResult[T] = this
}
}
And define the method:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import validation._
def name: BusinessResult[String] = Success("my name")
def externalService(name:String):Future[String] = future(name)
def myservice:Future[Int] = {
for {
n <- name
res <- externalService(n)
} yield res match {
case "ok" => 1
case _ => 0
}
}
But which is not compilable. The code in myservice can't return a Future[Int] type.
I also tried to wrap the name with Future:
def myservice:Future[Int] = {
for {
nn <- Future.successful(name)
n <- nn
res <- externalService(n)
} yield res match {
case "ok" => 1
case _ => 0
}
}
Which is also not compilable.
I know there must be a lot of issues in this code. How can I adjust them to make it compilable?
If you change the n with some hardcoded string it works, the problem is that in the for comprehension the variable n has type BusinessResult[String], as you probably already know for comprehension desugarize to map, flatMap and filter so the first part n <- name desugarize to a map on name:
val test: BusinessResult[String] = name.map(x => x)
Intellij thinks n is a String but the scala compiler disagree:
type mismatch;
[error] found : validation.BusinessResult[Nothing]
[error] required: scala.concurrent.Future[Int]
[error] n <- name
[error] ^
Easy solution could be to add a getter method to get back the string and do something like Option does:
object validation {
trait BusinessResult[+V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T]
def map[T](f: V => T): BusinessResult[T]
def getVal: V
}
sealed case class Success[V](t:V) extends BusinessResult[V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T] = {
f(t)
}
def map[T](f: V => T): BusinessResult[T] = {
Success(f(t))
}
def getVal: V = t
}
sealed case class Fail(e:String) extends BusinessResult[Nothing] {
def flatMap[T](f: Nothing => BusinessResult[T]):BusinessResult[T] = this
def map[T](f: Nothing => T): BusinessResult[T] = this
def getVal = throw new Exception("some message")
}
}
def myservice: Future[Int] = {
val value = name.getVal
for {
res <- externalService(value)
} yield res match {
case "ok" => 1
case _ => 0
}
}
Note that you can't extract the name in the for comprehension since map on String return Chars

how to implement this future/state concept as a monad in scala

I'm trying to implement a container for a match (like in sports) result so that I can create matches between the winners of other matches. This concept is close to what a future monads is as it contains a to be defined value, and also close to a state monad as it hides state change. Being mostly a begginer on the topic I have implemented an initial version in scala that is surely improvable. I added a get method that I'm not sure was a good idea, and so far the only way to create a value would be Unknown(null) which is not as elegant as I'd hoped. What do you think I could do to improve this design?
case class Unknown[T](t : T) {
private var value : Option[T] = Option(t)
private var applicatives: List[T => Unit] = Nil
def set(t: T) {
if (known) {
value = Option(t)
applicatives.foreach(f => f(t))
applicatives = Nil
} else {
throw new IllegalStateException
}
}
def get : T = value.get
def apply(f: T => Unit) = value match {
case Some(x) => f(x);
case None => applicatives ::= f
}
def known = value == None
}
UPDATE: a usage example of the current implementation follows
case class Match(val home: Unknown[Team], val visit: Unknown[Team], val result: Unknown[(Int, Int)]) {
val winner: Unknown[Team] = Unknown(null)
val loser: Unknown[Team] = Unknown(null)
result.apply(result => {
if (result._1 > result._2) {
home.apply(t => winner.set(t))
visit.apply(t => loser.set(t))
} else {
home.apply(t => loser.set(t))
visit.apply(t => winner.set(t))
}
})
}
And a test snippet:
val definedUnplayedMatch = Match(Unknown(Team("A")), Unknown(Team("B")), Unknown(null));
val definedPlayedMatch = Match(Unknown(Team("D")), Unknown(Team("E")), Unknown((1,0)));
val undefinedUnplayedMatch = Match(Unknown(null), Unknown(null), Unknown(null));
definedUnplayedMatch.winner.apply(undefinedUnplayedMatch.home.set(_))
definedPlayedMatch.winner.apply(undefinedUnplayedMatch.visit.set(_))
undefinedUnplayedMatch.result.set((3,1))
definedUnplayedMatch.result.set((2,4))
undefinedUnplayedMatch.winner.get must be equalTo(Team("B"));
undefinedUnplayedMatch.loser.get must be equalTo(Team("D"));
UPDATE - CURRENT IDEA : I haven't had much time to work on this because my laptop broke down, but I though it would be useful to write the monad I have so far for those who are interested:
sealed abstract class Determine[+A] {
def map[B](f: A => B): Determine[B]
def flatMap[B](f: A => Determine[B]): Determine[B]
def filter(p: A => Boolean): Determine[A]
def foreach(b: A => Unit): Unit
}
final case class Known[+A](value: A) extends Determine[A] {
def map[B](f: A => B): Determine[B] = Known(f(value))
def flatMap[B](f: A => Determine[B]): Determine[B] = f(value)
def filter(p: A => Boolean): Determine[A] = if (p(value)) this else Unknown
def foreach(b: A => Unit): Unit = b(value)
}
final case class TBD[A](definer: () => A) extends Determine[A] {
private var value: A = _
def map[B](f: A => B): Determine[B] = {
def newDefiner(): B = {
f(cachedDefiner())
}
TBD[B](newDefiner)
}
def flatMap[B](f: A => Determine[B]): Determine[B] = {
f(cachedDefiner())
}
def filter(p: A => Boolean): Determine[A] = {
if (p(cachedDefiner()))
this
else
Unknown
}
def foreach(b: A => Unit): Unit = {
b(cachedDefiner())
}
private def cachedDefiner(): A = {
if (value == null)
value = definer()
value
}
}
case object Unknown extends Determine[Nothing] {
def map[B](f: Nothing => B): Determine[B] = this
def flatMap[B](f: Nothing => Determine[B]): Determine[B] = this
def filter(p: Nothing => Boolean): Determine[Nothing] = this
def foreach(b: Nothing => Unit): Unit = {}
}
I got rid of the set & get and now the TBD class receives instead a function that will define provide the value or null if still undefined. This idea works great for the map method, but the rest of the methods have subtle bugs.
For a simple approach, you don't need monads, with partial application is enough:
//some utilities
type Score=(Int,Int)
case class MatchResult[Team](winner:Team,loser:Team)
//assume no ties
def playMatch[Team](home:Team,away:Team)(score:Score)=
if (score._1>score._2) MatchResult(home,away)
else MatchResult(away,home)
//defined played match
val dpm= playMatch("D","E")(1,0)
//defined unplayed match, we'll apply the score later
val dum= playMatch("A","B")_
// a function that takes the dum score and applies it
// to get a defined played match from an undefined one
// still is a partial application of match because we don't have the final result yet
val uumWinner= { score:Score => playMatch (dpm.winner,dum(score).winner) _ }
val uumLoser= { score:Score => playMatch (dpm.loser,dum(score).loser) _}
//apply the scores
uumWinner (2,4)(3,1)
uumLoser (2,4)(0,1)
//scala> uumWinner (2,4)(3,1)
//res6: MatchResult[java.lang.String] = MatchResult(D,B)
//scala> uumLoser (2,4)(0,1)
//res7: MatchResult[java.lang.String] = MatchResult(A,E)
This is a starting point, I'm pretty sure it can be further refined. Maybe there we'll find the elusive monad. But I think an applicative functor will be enough.
I'll give another pass later...