Functional reposiotory - build get() based on find()/create() methods - scala

I have algebra like this
object Algebra {
case class Product(id: String, description: String)
case class ShoppingCart(id: String, products: List[Product])
trait ShoppingCarts[F[_]] {
def create(id: String): F[Unit]
def get(id: String): F[ShoppingCart]
def find(id: String): F[Option[ShoppingCart]]
}
}
I came up with following implementation. But I wonder if it would be possible to implement it as generic method within the trait itself. I have tried to bound context to functor to gain access to map but this is not valid construct.
override def get(id: String): ScRepoState[ShoppingCart] =
find(id).flatMap {
case Some(sc) => sc.pure[ScRepoState]
case None => create(id) *> get(id)
}
Another problem is to implement addMany() metdhod. I got something like this
def addMany[F[_] : Monad](cartId: String, products: List[Product])(implicit shoppingCarts: ShoppingCarts[F]): F[ShoppingCart] = {
for {
cart <- shoppingCarts.get(cartId)
product <- products.pure[F]
newCart <- product.traverse(product => shoppingCarts.add(cart, product))
} yield newCart
}
I struggle how to mix differetnt wrappers within single for comprehension block

But I wonder if it would be possible to implement it as generic method within the trait itself.
Not quite. Scala 2 doesn't allow traits to have parameters, but you can use an abstract class instead. You can either not use trait entirely, or have a default class with all derivable implementations, e.g.:
abstract class DefaultShoppingCarts[F[_]: Monad] extends ShoppingCarts[F] {
override def get(id: String): F[ShoppingCart] =
find(id).flatMap {
case Some(sc) => sc.pure[F]
case None => create(id) >> get(id)
}
}
This is my preferred method, but there are other options for changing traits directly.
You can add a Monad parameter to a method:
trait ShoppingCarts[F[_]] {
def create(id: String): F[Unit]
def find(id: String): F[Option[ShoppingCart]]
def get(id: String)(implicit F: Monad[F]): F[ShoppingCart] =
find(id).flatMap {
case Some(sc) => sc.pure[F]
case None => create(id) >> get(id)
}
}
This is quite different from what we did in abstract class example, b/c the use site of ShoppingCarts will be forced to have a monad available instead of construction site, and the implementor, if they want to override the method, would have to replicate the signature exactly even if that Monad[F] is not used.
You can also emulate what trait parameters would do with abstract implicit defs:
trait ShoppingCarts[F[_]] {
implicit protected def F: Monad[F]
def create(id: String): F[Unit]
def get(id: String): F[ShoppingCart] =
find(id).flatMap {
case Some(sc) => sc.pure[F]
case None => create(id) >> get(id)
}
def find(id: String): F[Option[ShoppingCart]]
}
This works but you are more likely to run into technical issues with implicit scope when implementing the F member.
I struggle how to mix different wrappers within single for comprehension block
You don't. No mixing is allowed. Don't use for-comprehension for list, only use it for F. In some more complex cases you might want to nest for comprehensions, or use a monad transformer, but here you only need to work in F. I'm also not sure what's the return type of add, but assuming it's F[ShoppingCart]:
def addMany[F[_] : Monad](cartId: String, products: List[Product])(implicit shoppingCarts: ShoppingCarts[F]): F[ShoppingCart] = {
for {
cart <- shoppingCarts.get(cartId)
results <- products.traverse(product => shoppingCarts.add(cart, product))
// results is a list of intermediate carts, get the last one; fallback if list was empty
} yield results.lastOption.getOrElse(cart)
}
Also please ask second question separately next time.

Related

Correct way to approach splitting a trait

I have 3 traits:
trait Worker
...
trait StringWorker extends Worker {
def workString(): Iterator[String]
}
...
trait IntWorker extends Worker {
def workInt(): Iterator[Int]
}
Some of my classes extend only StringWorker, while others extend both StringWorker and IntWorker
My code obtains the correct parser depending on some pattern matching like so:
def getCorrectWorkerProvider: () => Worker = {
case SomeCase => getStringWorkerProvider()
case _ => getIntWorkerProvider()
}
If I keep the traits the same as written above, then I'll pretty much always have to do something like this pseudocode:
if working with string, then getCorrectWorkerProvider().asInstanceOf[StringWorker].workString
if working with int, then getCorrectWorkerProvider().asInstanceOf[IntWorker].workInt
whereas if I changed the definition of the traits to something like this:
trait Worker {
def workString(): Iterator[String]
def workInt(): Iterator[Int]
}
trait StringWorker extends Worker
trait IntWorker extends Worker
then I would never have to use .asInstanceOf[SomeWorker] to invoke the correct method.
I believe the first way is more correct and intuitive as the methods are specific to a certain Worker, but the second way seems to be less of a headache.
What I believe you're you're looking for is called the typeclass pattern. Here's an article that explains the idea. Basically the way it works is you create a function that takes a value of type T and an implicit parameter which is the Worker[T], and then scala's implicit resolution mechanism finds the right Worker[T] for you.
Here's an example:
object Main {
def main(args: Array[String]): Unit = {
work("String")
work(3)
}
def work[T](value: T)(implicit worker: Worker[T]) = {
worker.work(value)
}
}
trait Worker[T]{
def work(t: T): Iterator[T]
}
object Worker {
implicit val stringWorker: Worker[String] = (t: String) => Iterator(t)
implicit val intWorker: Worker[Int] = (t: Int) => Iterator(t)
}

Summoning Scala implicits for subclasses of sealed abstract trait

I'm using two Scala libraries that both rely on implicit parameters to supply codecs/marshallers for case classes (the libraries in question are msgpack4s and op-rabbit). A simplified example follows:
sealed abstract trait Event
case class SomeEvent(msg: String) extends Event
case class OtherEvent(code: String) extends Event
// Assume library1 needs Show and library2 needs Printer
trait Show[A] { def show(a: A): String }
trait Printer[A] { def printIt(a: A): Unit }
object ShowInstances {
implicit val showSomeEvent = new Show[SomeEvent] {
override def show(a: SomeEvent) =
s"SomeEvent: ${a.msg}"
}
implicit val showOtherEvent = new Show[OtherEvent] {
override def show(a: OtherEvent) =
s"OtherEvent: ${a.code}"
}
}
The Printer for the one library can be generic provided there's an implicit Show for the other library available:
object PrinterInstances {
implicit def somePrinter[A: Show]: Printer[A] = new Printer[A] {
override def printIt(a: A): Unit =
println(implicitly[Show[A]].show(a))
}
}
I want to provide an API that abstracts over the details of the underlying libraries - callers should only need to pass the case class, internally to the API implementation the relevant implicits should be summoned.
object EventHandler {
private def printEvent[A <: Event](a: A)(implicit printer: Printer[A]): Unit = {
print("Handling event: ")
printer.printIt(a)
}
def handle(a: Event): Unit = {
import ShowInstances._
import PrinterInstances._
// I'd like to do this:
//EventHandler.printEvent(a)
// but I have to do this
a match {
case s: SomeEvent => EventHandler.printEvent(s)
case o: OtherEvent => EventHandler.printEvent(o)
}
}
}
The comments in EventHandler.handle() method indicate my issue - is there a way to have the compiler select the right implicits for me?.
I suspect the answer is no because at compile time the compiler doesn't know which subclass of Event handle() will receive, but I wanted to see if there's another way. In my actual code, I control & can change the PrinterInstances code, but I can't change the signature of the printEvent method (that's provided by one of the libraries)
*EDIT: I think this is the same as Provide implicits for all subtypes of sealed type. The answer there is nearly 2 years old, I'm wondering if it's still the best approach?
You have to do the pattern matching somewhere. Do it in the Show instance:
implicit val showEvent = new Show[Event] {
def show(a: Event) = a match {
case SomeEvent(msg) => s"SomeEvent: $msg"
case OtherEvent(code) => s"OtherEvent: $code"
}
}
If you absolutely need individual instances for SomeEvent and OtherEvent, you can provide them in a different object so they can be imported separately.
If Show is defined to be contravariant (i.e. as trait Show[-A] { ... }, with a minus on the generic type) then everything works out of the box and a Show[Event] is usable as a Show[SomeEvent] (and as a Show[OtherEvent] for that matter).
If Show is unfortunately not written to be contravariant, then we might have to do a little bit more juggling on our end than we'd like. One thing we can do is declare all of our SomeEvent values as simply Events, vis a vis val fooEvent: Event = SomeEvent("foo"). Then fooEvent will be showable.
In a more extreme version of the above trick, we can actually hide our inheritance hierarchy:
sealed trait Event {
def fold[X]( withSomeEvent: String => X,
withOtherEvent: String => X ): X
}
object Event {
private case class SomeEvent(msg: String) extends Event {
def fold[X]( withSomeEvent: String => X,
withOtherEvent: String => X ): X = withSomeEvent(msg)
}
private case class OtherEvent(code: String) extends Event {
def fold[X]( withSomeEvent: String => X,
withOtherEvent: String => X ): X = withOtherEvent(code)
}
def someEvent(msg: String): Event = SomeEvent(msg)
def otherEvent(code: String): Event = OtherEvent(code)
}
Event.someEvent and Event.otherEvent allow us to construct values, and fold allows us to pattern match.

Matching Type Parameters on Traits

Let's say I have a trait like:
trait MyTrait[T, U <: SomeParentClass] {
def get(data: T): Option[U]
}
and a concrete implementation like:
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
To simplify things, let's also say we have the following types for U <: SomeParentClass:
TypeA
TypeB
TypeC
and some functions:
def str2TypeA(s: String): Option[TypeA] = ...
def str2TypeB(s: String): Option[TypeB] = ...
def str2TypeC(s: String): Option[TypeC] = ...
Then let's say I have:
val mySeq = Seq(
MyStringClass(str2TypeA),
MyStringClass(str2TypeB),
MyStringClass(str2TypeC)
)
What I want to do is filter mySeq based on the return type U. Something like:
mySeq.collect { case a: Function1[_, Option[TypeA]] => a}
I'm running into type erasure issues as expected. I'm curious what approaches might work well here to achieve my goal of filtering based on the type U.
You would usually use a TypeTag to handle cases where erasure gets in your way. They are essentially simple objects that contain all of the information available at compile-time, and they're easy to obtain either by requesting an implicit parameter, or just writing typeTag[T]. For example:
import scala.reflect.runtime.universe._
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])(implicit val utt: TypeTag[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
mySeq.collect {
case a: MyStringClass[Option[TypeA]] if (a.utt == typeTag[Option[Type[A]]) => a
}

Scala: overloading methods based on provided type

Consider a simple object that serves as a storage for some cohesive data discriminated by type. I want it to have an API which is:
consistent and concise;
compile-time safe.
I can easily provide such API for saving objects by using overloading:
object CatsAndDogsStorage {
def save(key: String, cat: Cat): Future[Unit] = { /* write cat to db */ }
def save(key: String, dog: Dog): Future[Unit] = { /* save dog to Map */ }
/* other methods */
}
But I cannot find a good way to declare such methods for loading objects. Ideally, I would want something like this:
// Futures of two unrelated objects
val catFuture: Future[Cat] = CatsAndDogsStorage.load[Cat]("Lucky")
val dogFuture = CatsAndDogsStorage.load[Dog]("Lucky")
I'm fairly new to Scala, but I know that I have these options (sorted from the least preferred):
1. Different method names
def loadCat(key: String): Future[Cat] = { /* ... */ }
def loadDog(key: String): Future[Dog] = { /* ... */ }
Not the most concise method. I dislike how if I decide to rename Cat to something else, I would have to rename the method too.
2. Runtime check for provided class
def load[T: ClassTag](key: String): Future[T] = classTag[T] match {
case t if t == classOf[Dog] => /* ... */
case c if c == classOf[Cat] => /* ... */
}
This one gives the desired syntax, but it fails in runtime, not compile time.
3. Dummy implicits
def load[T <: Cat](key: String): Future[Cat] = /* ... */
def load[T <: Dog](key: String)(implicit i1: DummyImplicit): Future[Dog]
This code becomes nightmare when you have a handful of types you need to support. It also makes it quite inconvenient to remove those types
4. Sealed trait + runtime check
sealed trait Loadable
case class Cat() extends Loadable
case class Dog() extends Loadable
def load[T <: Loadable: ClassTag](key: String): Future[T] = classTag[T] match {
case t if t == classOf[Dog] => /* ... */
case c if c == classOf[Cat] => /* ... */
}
This has the advantage of 2) while preventing user from asking anything besides Dog or Cat. Still, I would rather not change the object hierarchy. I can use union types to make the code shorter.
So, the last solution is okay, but it still feels hack-ish, and maybe there is another known way which I just cannot figure out.
Having functions with sligthly different name doing similar work but for differents type doesn't seem bad for me.
If you really want to have a facade API dispatching according the type you can use typeclasses.
trait SaveFn[T] extends (T => Future[Unit]) {}
object SaveFn {
implicit object SaveDog extends SaveFn[Dog] { def apply(dog: Dog): Future[Unit] = ??? }
implicit object SaveCat extends SaveFn[Dog] { def apply(cat: Cat): Future[Unit] = ??? }
}
object Storage {
def save[T : SaveFn](in: T): Future[Unit] = implicitly[SaveFn[T]](in)
}
For the .load case:
trait LoadFn[T] extends (String => Future[T]) {}
object LoadFn {
implicit object LoadDog extends LoadFn[Dog] { def apply(key: String): Future[Dog] = ??? }
implicit object LoadCat extends LoadFn[Cat] { def apply(key: String): Future[Cat] = ??? }
}
object Storage {
def load[T : LoadFn](key: String): Future[T] = implicitly[LoadFn[T]](key)
}
As for .load the inference cannot be found according the arguments as for .save, that's less nice to use: Storage.load[Dog]("dogKey")

Pattern matching on abstract types with a fold

This is a follow-up to a previous question I asked which was far from being complete. All the code that follows compiles and run fine in the Scala console.
Consider the following abstract data type, along with the operations it must support as a typeclass:
trait SIG {
type XOrY
type X <: XOrY
type Y <: XOrY
}
trait SIGOps[Sig <: SIG] {
def makeX(s: String): Sig#X
def makeY(i: Int): Sig#Y
// the disjunction is enforced with that fold
def fold[T](xy: Sig#XOrY)(isX: String => T, isY: Int => T): T
// the following is for convenience, as we want to mimick case classes
object X {
def apply(s: String): Sig#X = makeX(s)
def unapply(xy: Sig#XOrY): Option[String] = fold(xy)(s => Some(s), i => None)
}
object Y {
def apply(i: Int): Sig#Y = makeY(i)
def unapply(xy: Sig#XOrY): Option[Int] = fold(xy)(s => None, i => Some(i))
}
}
And now, here is a possible implementation for the signature. The typeclass instance is in the companion object for easy discovery.
trait EitherSig extends SIG {
type XOrY = scala.util.Either[String, Int]
type X = scala.util.Left[String, Int]
type Y = scala.util.Right[String, Int]
}
object EitherSig {
implicit object EitherSIGOps extends SIGOps[EitherSig] {
def makeX(s: String): EitherSig#X = scala.util.Left[String, Int](s)
def makeY(i: Int): EitherSig#Y = scala.util.Right[String, Int](i)
def fold[T](xy: EitherSig#XOrY)(isX: String => T, isY: Int => T): T = xy match {
case Left(s) => isX(s)
case Right(s) => isY(s)
}
}
}
And finally, here is how one can write code that depends on the abstract signature.
class Example[Sig <: SIG](implicit ops: SIGOps[Sig]) {
import ops._
def main(args: Array[String]): Unit = {
val xy: Sig#XOrY = X("foo")
xy match {
case X(s) => println("X: "+s)
// Scala does not see that the pattern matching is not exhaustive if when I comment the following line
// case Y(i) => println("Y: "+i)
}
}
}
object ConcreteExample extends Example[EitherSig]
And it works as expected:
scala> ConcreteExample.main(Array())
X: foo
The question is the following: how can I teach Scala to recognize when the pattern matching is not exhaustive as above?
There might be a way to communicate this information to the typechecker but I don't know how.
Jason Zaugg gave an answer on Twitter:
[...] we don't have an extension point for that. Only sealed hierarchies are considered.
Travis Brown proposed to Write a matchExhaustive macro that inspects the partial function but doesn't think that it's Worth the trouble when you've got the fold.
So no real solution for that question for now.