I have the following algebra in Scala (I am using the Tagless Final Pattern):
trait ShoppingCarts[F[_]] {
def create(id: String): F[Unit]
def find(id: String): F[Option[ShoppingCart]]
def add(sc: ShoppingCart, product: Product): F[ShoppingCart]
}
Using the above algebra, I created the following program:
def createAndToCart[F[_] : Monad : ShoppingCarts](product: Product, cartId: String): F[Option[ShoppingCart]] =
for {
_ <- ShoppingCarts[F].create(cartId)
maybeSc <- ShoppingCarts[F].find(cartId)
maybeNewScF = maybeSc.map(sc => ShoppingCarts[F].add(sc, product))
maybeNewSc <- maybeNewScF match {
case Some(d) => d.map(s1 => Option.apply(s1))
case _ => Monad[F].pure(Option.empty[ShoppingCart])
}
} yield maybeNewSc
I don't like the code very much in the for-comprehension construct that converts an Option[F[ShoppingCart]] into an F[Option[ShoppingCart]]. I know for sure that I can do better, but I don't know how to improve it.
I am using Cats.
You are looking for
traverseandsequence. What these functions do is "switch" the order of effects, so they are able to changeG[F[A]]toF[G[A]]if G has an instance ofApplicative.Optionhas such an instance in scope, so you'd be able to use it.traversetakes additional mapping function, but if you just want to "switch" effect, sosequencewill be way to go:or you can merge
mapandsequenceinto one step withtraverse:You can read more on
sequenceandtraversein cats docs.Another thing I would suggest you is checking out are monad transformers since they're making dealing with nested monads stacks like
F[Option[A]]a lot easier.