功能性存储库 - 基于 find()/create() 方法构建 get()
Functional reposiotory - build get() based on find()/create() methods
我有这样的代数
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() 方法。我得到了这样的东西
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
}
我很难在单个 for comprehension 块中混合不同的包装器
But I wonder if it would be possible to implement it as generic method within the trait itself.
不完全是。 Scala 2 不允许特征具有参数,但您可以使用抽象 class 代替。您可以不完全使用 trait,也可以使用默认的 class 和所有可派生的实现,例如:
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)
}
}
这与我们在摘要 class 示例中所做的完全不同,b/c ShoppingCarts
的 use site 将被迫有一个可用的 monad 而不是 construction site,如果实现者想要重写该方法,即使未使用 Monad[F]
也必须完全复制签名。
您还可以模拟特征参数对抽象隐式定义的作用:
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
成员时,您更有可能 运行 陷入隐含范围的技术问题。
I struggle how to mix different wrappers within single for comprehension block
你不知道。不允许混合。不要对列表使用 for-comprehension,只对 F
使用它。在一些更复杂的情况下,您可能想要嵌套理解,或使用 monad 转换器,但在这里您只需要在 F 中工作。我也不确定 add
的 return 类型是什么,但假设它是 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)
}
另外第二个问题下次再单独问
我有这样的代数
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() 方法。我得到了这样的东西
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
}
我很难在单个 for comprehension 块中混合不同的包装器
But I wonder if it would be possible to implement it as generic method within the trait itself.
不完全是。 Scala 2 不允许特征具有参数,但您可以使用抽象 class 代替。您可以不完全使用 trait,也可以使用默认的 class 和所有可派生的实现,例如:
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)
}
}
这与我们在摘要 class 示例中所做的完全不同,b/c ShoppingCarts
的 use site 将被迫有一个可用的 monad 而不是 construction site,如果实现者想要重写该方法,即使未使用 Monad[F]
也必须完全复制签名。
您还可以模拟特征参数对抽象隐式定义的作用:
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
成员时,您更有可能 运行 陷入隐含范围的技术问题。
I struggle how to mix different wrappers within single for comprehension block
你不知道。不允许混合。不要对列表使用 for-comprehension,只对 F
使用它。在一些更复杂的情况下,您可能想要嵌套理解,或使用 monad 转换器,但在这里您只需要在 F 中工作。我也不确定 add
的 return 类型是什么,但假设它是 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)
}
另外第二个问题下次再单独问