函数库-基于find()/create()方法构建get()



我有这样的代数

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]]
}
}

我提出了以下实施方案。但我想知道是否有可能将其作为特性本身中的通用方法来实现。我试图将上下文绑定到函子以访问映射,但这不是有效的构造。

override def get(id: String): ScRepoState[ShoppingCart] =
find(id).flatMap {
case Some(sc) => sc.pure[ScRepoState]
case None => create(id) *> get(id)
}

另一个问题是实现addMany((metdhod。我有类似的东西

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
}

我很难在单个理解块中混合不同的包装

但我想知道是否有可能将其作为特性本身中的泛型方法来实现。

不完全是。Scala2不允许traits有参数,但您可以使用抽象类。您可以不完全使用trait,也可以为所有可派生的实现创建一个默认类,例如:

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)
}
}

这是我喜欢的方法,但也有其他直接改变特征的选择。


您可以将Monad参数添加到方法中:

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)
}
}

这与我们在抽象类示例中所做的非常不同。例如,b/cShoppingCarts使用站点将被迫具有可用的monad,而不是构造站点.并且,如果实现者想要覆盖该方法,则即使不使用Monad[F],也必须完全复制签名。


您还可以模拟特征参数对抽象隐式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]]
}

这是可行的,但在实现F成员时,您更有可能遇到具有隐含范围的技术问题。


我很难在单个理解块中混合不同的包装器

你没有。不允许混合。不要用于理解列表,只用于F。在一些更复杂的情况下,你可能想嵌套进行理解,或者使用monad转换器,但这里你只需要在F中工作。我也不确定add的返回类型是什么,但假设它是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)
}

另外,请下次单独提出第二个问题。

最新更新