Using Either in a Free monad - scala

I'm trying to use an Either as the result of an algebra using Free monad, like the following code
///// Command
sealed trait Command[A]
type Result[A] = Either[Exception, A]
object Command {
case class Tell(line: String) extends Command[Result[Unit]]
case class Ask(line: String) extends Command[Result[String]]
}
type App[A] = EitherK[Command, Log, A]
def program(implicit L: LogOps[App],
C: CommandOps[App]): Free[App, Unit] =
for {
_ <- L.show("start <ask>")
name <- C.ask("What's your name?")
_ <- L.show("start <tell>")
_ <- C.tell(s"Hi <$name>, nice to meet you!")
_ <- L.show("done.")
} yield ()
...
the problem is that the name is an Either, so I got the following output
L--- start <ask>
What's your name?
George
L--- start <tell>
Hi <Right(George)>, nice to meet you!
L--- done.
Any idea?
Thanks

Solution found in this link
Scala Free Monads with Coproduct and monad transformer
I had to define a function like following
def liftFE[F[_], T[_], A, B](f: T[Either[A, B]])(implicit I: InjectK[T, F]): EitherT[Free[F, *], A, B] = EitherT[Free[F, *], A, B](Free.liftF(I.inj(f)))
and use it instead of Free.inject like the follow
class CommandOps[F[_]](implicit I: InjectK[Command, F]) {
import Command.{Ask, Tell}
def tell(line: String): EitherT[Free[F, *], Exception, Unit] =
liftFE(Tell(line))
def ask(line: String): EitherT[Free[F, *], Exception, String] =
liftFE(Ask(line))
}
and change also 'program' result type
def program(implicit L: LogOps[App],
C: CommandOps[App]): EitherT[Free[App, *], Exception, Either[Exception, Unit]] =

Related

Leveraging a generic return type in Scala

So I would like to use a generic return type and be able to use the info of that type within the function. Not sure this is possible but here is what I would like:
def getStuff[A](a: MyObj, b: String): Option[A] = {
// do some stuff
A match {
case String => Some(a.getString(b))
case Integer => Some(a.getInt(b))
...
case _ => None
}
}
However, as you know, A match is not a possibility. Any ideas on how I could achieve this ?
This is a classic case for using a typeclass:
trait StuffGetter[T] { // typeclass
def get(obj: MyObj, s: String): Option[T]
}
implicit val stringGetter = new StuffGetter[String] {
def get(o: MyObj, s: String): Option[String] = ???
}
implicit val intGetter = new StuffGetter[Int] {
def get(o: MyObj, s: String): Option[Int] = ???
}
def getStuff[A](a: MyObj, b: String)(implicit ev: StuffGetter[A]): Option[A] =
ev.get(a, b)
val stuff0 = getStuff[String](obj, "Hello") // calls get on stringGetter
val stuff1 = getStuff[Int](obj, "World") // call get on intGetter
val stuff2 = getStuff[Boolean](obj, "!") // Compile-time error
The StuffGetter trait defines the operations that you want to perform on the generic type, and each implicit value of that trait provides the implementation for a specific type. (For a custom type these are typically place in the companion object for the type; the compiler will look there for them)
When getStuff is called the compiler will look for an implicit instance of StuffGetter with the matching type. This will fail if no such instance exists, otherwise it will be passed in the ev parameter.
The advantage of this is that the "match" is done at compile time and unsupported types are also detected at compile time.
Conceptually we can differentiate between pattern matching at run-time which looks something like this
def getStuff[A](...) =
A match {
...
}
and pattern matching at compile-time which looks something like this
def getStuff[A](...)(implicit ev: Foo[A]) = {
ev.bar(...)
}
Key concept to understand is that types do not exists at run-time because they get "erased" after compilation so there is not enough information to pattern match on types once the program is running. However at compile-time, that is before the program runs, types do exist and Scala provides means to ask the compiler to effectively pattern match on them via implicit/givens mechanism which looks something like so
// type class requirements for type A
trait StringConverter[A] {
def getOptValue(b: String): Option[A]
}
// evidence which types satisfy the type class
implicit val intStringConverter: StringConverter[Int] = (b: String) => b.toIntOption
implicit val strStringConverter: StringConverter[String] = (b: String) => Some(b)
implicit def anyStringConverter[A]: StringConverter[A] = (b: String) => None
// compile-time pattern matching on type A
def getStuff[A](b: String)(implicit ev: StringConverter[A]): Option[A] = {
ev.getOptValue(b)
}
getStuff[Int]("3") // : Option[Int] = Some(value = 3)
getStuff[String]("3") // : Option[String] = Some(value = "3")
getStuff[Double]("3") // : Option[Double] = None
This compile-time pattern matching is called type class pattern.
Understanding the distinction between types and classes is one of the fundamental concepts in Scala https://docs.scala-lang.org/tutorials/FAQ/index.html#whats-the-difference-between-types-and-classes and gorking it will help understand how to write type classes.
Using custom typeclass similar to Getter:
trait KeyedGetter[S, K, A]:
def get(s: S, key: K): Option[A]
case class MyObj(ints: Map[String, Int], strs: Map[String, String])
object MyObj:
given KeyedGetter[MyObj, String, Int] with
def get(m: MyObj, k: String) = m.ints.get(k)
given KeyedGetter[MyObj, String, String] with
def get(m: MyObj, k: String) = m.strs.get(k)
def getStuff[A](m: MyObj, key: String)(using g: KeyedGetter[MyObj, String, A]): Option[A] =
g.get(m, key)
Using class tags:
case class MyObj(ints: Map[String, Int], strs: Map[String, String])
import reflect._
def getStuff[A](m: MyObj, key: String)(using ct: ClassTag[A]): Option[A] = (ct match
case _ if ct == classTag[String] => m.strs.get(key)
case _ if ct == classTag[Int] => m.ints.get(key)
case _ => None
).asInstanceOf[Option[A]]
If the erased types are insufficient, for a similar approach with type tags see this answer (and ignore the rest).

Cats - EitherT - Diverging Implicit Expansion in for comprehension w/o explicit typing? [duplicate]

Suppose I have the following setting:
def foo: Either[Error, A] = ???
def bar: EitherT[Future, Error, B] = ???
case class Baz(a: A, b: B)
How can I use for comprehension to instantiate the class Baz? I tried with:
val res = for {
a <- foo
b <- bar
} yield Baz(a, b)
but, the result has type Either[Error, Nothing]. I don't know what is the right return type in this case, but obviously I don't want Nothing...
What is the right way to combine Either and EitherT in for comprehension?
Use EitherT.fromEither function to create EitherT from Either
import cats.data._
import cats.implicits._
def foo[A]: Either[Error, A] = ???
def bar[B]: EitherT[Future, Error, B] = ???
case class Baz[A, B](a: A, b: B)
def res[A, B] = for {
a <- EitherT.fromEither[Future](foo[A])
b <- bar[B]
} yield Baz(a, b)

Scala Free Monads with Coproduct and monad transformer

I'm trying to start using free monads in my project and I'm struggling to make it elegant.
Let's say I have two contexts (in reality I have more) - Receipt and User - both have operations on a database and I would like to keep their interpreters separate and compose them at the last moment.
For this I need to define different operations for each and combine them into one type using Coproduct.
This is what I have after days of googling and reading:
// Receipts
sealed trait ReceiptOp[A]
case class GetReceipt(id: String) extends ReceiptOp[Either[Error, ReceiptEntity]]
class ReceiptOps[F[_]](implicit I: Inject[ReceiptOp, F]) {
def getReceipt(id: String): Free[F, Either[Error, ReceiptEntity]] = Free.inject[ReceiptOp, F](GetReceipt(id))
}
object ReceiptOps {
implicit def receiptOps[F[_]](implicit I: Inject[ReceiptOp, F]): ReceiptOps[F] = new ReceiptOps[F]
}
// Users
sealed trait UserOp[A]
case class GetUser(id: String) extends UserOp[Either[Error, User]]
class UserOps[F[_]](implicit I: Inject[UserOp, F]) {
def getUser(id: String): Free[F, Either[Error, User]] = Free.inject[UserOp, F](GetUser(id))
}
object UserOps {
implicit def userOps[F[_]](implicit I: Inject[UserOp, F]): UserOps[F] = new UserOps[F]
}
When I want to write a program I can do this:
type ReceiptsApp[A] = Coproduct[ReceiptOp, UserOp, A]
type Program[A] = Free[ReceiptsApp, A]
def program(implicit RO: ReceiptOps[ReceiptsApp], UO: UserOps[ReceiptsApp]): Program[String] = {
import RO._, UO._
for {
// would like to have 'User' type here
user <- getUser("user_id")
receipt <- getReceipt("test " + user.isLeft) // user type is `Either[Error, User]`
} yield "some result"
}
The problem here is that for example user in for comprehension is of type Either[Error, User] which is understandable looking at the getUser signature.
What I would like to have is User type or stopped computation.
I know I need to somehow use an EitherT monad transformer or FreeT, but after hours of trying I don't know how to combine the types to make it work.
Can someone help?
Please let me know if more details are needed.
I've also created a minimal sbt project here, so anyone willing to help could run it: https://github.com/Leonti/free-monad-experiment/blob/master/src/main/scala/example/FreeMonads.scala
Cheers,
Leonti
After long battle with Cats:
// Receipts
sealed trait ReceiptOp[A]
case class GetReceipt(id: String) extends ReceiptOp[Either[Error, ReceiptEntity]]
class ReceiptOps[F[_]](implicit I: Inject[ReceiptOp, F]) {
private[this] def liftFE[A, B](f: ReceiptOp[Either[A, B]]) = EitherT[Free[F, ?], A, B](Free.liftF(I.inj(f)))
def getReceipt(id: String): EitherT[Free[F, ?], Error, ReceiptEntity] = liftFE(GetReceipt(id))
}
object ReceiptOps {
implicit def receiptOps[F[_]](implicit I: Inject[ReceiptOp, F]): ReceiptOps[F] = new ReceiptOps[F]
}
// Users
sealed trait UserOp[A]
case class GetUser(id: String) extends UserOp[Either[Error, User]]
class UserOps[F[_]](implicit I: Inject[UserOp, F]) {
private[this] def liftFE[A, B](f: UserOp[Either[A, B]]) = EitherT[Free[F, ?], A, B](Free.liftF(I.inj(f)))
def getUser(id: String): EitherT[Free[F, ?], Error, User] = Free.inject[UserOp, F](GetUser(id))
}
object UserOps {
implicit def userOps[F[_]](implicit I: Inject[UserOp, F]): UserOps[F] = new UserOps[F]
}
Then you write program as you want:
type ReceiptsApp[A] = Coproduct[ReceiptOp, UserOp, A]
type Program[A] = Free[ReceiptsApp, A]
def program(implicit RO: ReceiptOps[ReceiptsApp], UO: UserOps[ReceiptsApp]): Program[Either[Error, String]] = {
import RO._, UO._
(for {
// would like to have 'User' type here
user <- getUser("user_id")
receipt <- getReceipt("test " + user.isLeft) // user type is `User` now
} yield "some result").value // you have to get Free value from EitherT, or change return signature of program
}
A little explanation. Without Coproduct transformer, functions would return:
Free[F, A]
Once we add Coproduct of operations into picture, return type becomes:
Free[F[_], A]
, which works fine until we try to transform it to EitherT. If there would not be Coproduct, EitherT would look like:
EitherT[F, ERROR, A]
Where F, is Free[F, A]. But if F is Coproduct and Injection is used, intuition leads to:
EitherT[F[_], ERROR, A]
Which is wrong obviously, here we have to extract type of Coproduct. Which would lead us with kind-projector plugin to:
EitherT[Free[F, ?], ERROR, A]
Or with lambda expression:
EitherT[({type L[a] = Free[F, a]})#L, ERROR, A]
Now it is correct type to which we can lift with:
EitherT[Free[F, ?], A, B](Free.liftF(I.inj(f)))
If needed, we can simplify return type to:
type ResultEitherT[F[_], A] = EitherT[Free[F, ?], Error, A]
And use it in functions like:
def getReceipt(id: String): ResultEitherT[F[_], ReceiptEntity] = liftFE(GetReceipt(id))
The Freek library implements all the machinery required to solve your problem:
type ReceiptsApp = ReceiptOp :|: UserOp :|: NilDSL
val PRG = DSL.Make[PRG]
def program: Program[String] =
for {
user <- getUser("user_id").freek[PRG]
receipt <- getReceipt("test " + user.isLeft).freek[PRG]
} yield "some result"
As you rediscovered yourself, Free monads and the likes are not extensible without going through the complexity of coproducts. If you are looking for an elegant solution, I would suggest you have a look at Tagless Final Interpreters.

Implicit method for typeclass not found

This is continuation of Diverging implicit expansion for type class. I've came up with another version that still doesn't compile, but now for another reason, hence a different question. Here's what I did:
I've basically added a new typeclass NestedParser for the cases where some case class consists of other case classes.
trait Parser[A] {
def apply(s: String): Option[A]
}
trait NestedParser[A] {
def apply(s: String): Option[A]
}
object Parser {
def apply[A](s: String)(implicit parser: Parser[A]): Option[A] = parser(s)
}
object NestedParser {
def apply[A](s: String)(implicit parser: NestedParser[A]): Option[A] = parser(s)
}
I've changed the previous implicit function to return the NestedParser to avoid diverging implicit expansions. Otherwise it's same as before:
implicit def nestedCaseClassParser[A, B, C]
(
implicit pb: Parser[B],
pc: Parser[C],
gen: Generic.Aux[A, B :: C :: HNil]
): NestedParser[A] = new NestedParser[A] {
override def apply(s: String): Option[A] = {
val tmp = s.span(_ != '|') match {
case (h, t) =>
for {
a <- pb(h)
b <- pc(t.substring(1))
} yield a :: b :: HNil
}
tmp.map(gen.from)
}
}
Case classes are same as before:
case class Person(name: String, age: Int)
case class Family(husband: Person, wife: Person)
Now when I try to parse the Family, I get the following compile error:
scala> NestedParser[Family]("")
<console>:32: error: could not find implicit value for parameter parser: NestedParser[Family]
NestedParser[Family]("")
However, it doesn't make sense to me. The function above clearly provides the implicit instance of NestedParser. Why doesn't it satisfy the compiler?
Well, as far as I can see you are not providing any implicit Parser instances which nestedCaseClassParser requires.

scala bind type parameter of trait with type parameter of function

It is difficult to explain problem using few words, so I prepared pice of code to present issue.
Let's develop container of type Container[T1,T2] and implicits for wrapping any value in that container. If the value is type of Container[T1,T2] wrapping should return the same type. More over wrapping method should taking parameter of type T1 (the same as in container), and resulting with container with replaced T1 value.
Solution must conform generics and implicits manner.
Sounds little confusing, let's read the code :)
Container:
case class Container[T1, T2](t1: T1, t2: T2)
Trait with wrapping method:
trait ToContainer[A] {
def wrappingMethod[E](e: E): Container[E, A]
}
Object with implicits:
object ToContainers {
import language.implicitConversions
implicit def implicitMethod[A](a: => A) = new ToContainer[A] {
def wrappingMethod[E](e: E): Container[E, A] = Container(e, a)
}
implicit def implicitMethod2[E, A] (c: => Container[E, A])(implicit d:DummyImplicit): ToContainer[A] = new ToContainer[A] {
def wrappingMethod[EX](e: EX): Container[EX, A] = c.copy(e)
}
}
So that was the code.
The problem is that I need somehow bind type parameter EX of function def wrappingMethod[EX] to the parameter E of def implicitMethod2[E, A].
After this something like this should work (and works):
scala> import ToContainers._
import ToContainers._
scala> val c1: Container[String, Int] = 1234.wrappingMethod("abc")
c1: Container[String,Int] = Container(abc,1234)
scala> val c2: Container[String, Int] = c1.wrappingMethod("XXX")
c2: Container[String,Int] = Container(XXX,1234)
... but this must yield compilation error, and don't :(
(Take a look on types: c1 has [String, Int] and c3 has [Int,Int]. I want to prevent this.)
scala> val c3 = c1.wrappingMethod(0)
c3: Container[Int,Int] = Container(0,1234)
Any ideas great appreciated :)
BTW: I am using this version of scala:
Welcome to Scala version 2.10.0-M7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_07)
EDITED:
I've fiexed errors. Code is working now.
The sample code you provided does not work, so it's a bit difficult to provide a good answer. However, can't you just do something like:
implicit def implicitMethod2[E, A] (c: => Container[E, A])(implicit d:DummyImplicit): ToContainer[A] = new ToContainer[A] {
def wrappingMethod[E](e: E): Container[EX, A] = c.copy(e)
}
So simply replace the EX type parameter with E.
You need to move the type parameter E up to ToContainer:
trait ToContainer[E, A]
def wrappingMethod(e: E): Container[E, A]
}
object ToContainers {
import language.implicitConversions
implicit def implicitMethod[E, A](a: => A) = new ToContainer[E, A] {
def wrappingMethod(e: E): Container[E, A] = Container(e, a)
}
implicit def implicitMethod2[E, A] (c: => Container[E, A])(implicit d:DummyImplicit): ToContainer[E, A] = new ToContainer[E, A] {
def wrappingMethod(e: E): Container[E, A] = c.copy(e)
}
}