【发布时间】:2020-11-01 19:33:56
【问题描述】:
我正在尝试创建组合批处理机制。
流程必须是。
- 以
s: Map[BatchFragmentId, FragmentRequest]的形式累积状态 - 以
Map[BatchFragmentId, FragmentResponse]的形式计算结果 - 对某些结果 R 执行一系列形式为
f: Map[BatchFragmentId, FragmentResponse] => R的预定义计算。
我的解决方案
让Batch[R] 是一个monad,它包含一些累积状态s 和一些函数f。
map[B](g: R => B) 非常有意义,因为它只是自包含函数 f (Batch.make(f=f.andThen(g), s=s)) 的组合。
手头上的flatMap[B](f: R => Batch[B])没有多大意义,因为R需要计算完成,所以Batch[B]此时不能存在,因为s在执行时必须包含Batch[B]的状态响应计算。
修改flatMap:
def combine[B, C](that: Batch[B])(t: (A, B) => C): Batch[C] = {
val combined: Map[BatchFragmentId, FragmentRequest] = that.state ++ state
val g: Map[BatchFragmentId, FragmentResponse] => C =
(r: Map[BatchFragmentId, FragmentResponse]) => t(f(r), that.f(r))
Batch.make(combined, g)
}
这与 Scala 的 for 理解不能很好地配合,并且它的可读性比 for 理解下降得更快。
val b: Batch[String] = ???
val b2: Batch[String] = ???
val b3: Batch[Int] = ???
val combined: Batch[(String, String)] =
(b.combine(b2){ case (s1, s2) => s1 + s2 })
.combine(b3){ case (s1, i1) => s1 + i1.toString }
如果有几批,这可能会变得非常混乱。能够组成这些批次的理想方式是。
val o: Batch[(String, String)] = for {
_ <- put("key", "42")
x1 <- get("key")
_ <- put("key2", "24")
x2 <- get("key2")
} yield (x1, x2)
但任何优雅的语法都是一种解决方案。
我不太精通类别理论,并且在编写这种形式的代码方面经验有限,所以我不确定要针对这个特定问题研究什么类型类。
这甚至是解决此类问题的正确方法吗?我是否对问题进行了适当的建模?这个问题是否已经被很好地理解和概括了?
我也有猫供我使用。
【问题讨论】:
-
在您的第一个示例中,我看不出
combined的类型是Batch[(String, String)]。不应该也是Batch[String]吗?另外,您能否更好地解释您的第二个示例(带有理解)应该如何工作?我不知道它与第一个有什么关系。 -
另一个问题 -
t应该是(R, B) => C类型吗?我在任何地方都没有看到A -
你在两个陈述中都是对的@user。组合应该是
Batch[String]和t应该是(R, B) => C。我已经发布了完整代码的答案。 -
并非一切都是单子。如果有人想要针对不同类型类(Applicative、Comonad 等)使用类似于 for-comprehension 的语法,他可能需要一个编译器插件 github.com/bkirwi/applicative-syntax github.com/scala/scala/pull/5725 contributors.scala-lang.org/t/…
-
是的,我的回答也得出了同样的结论。感谢您的参考@dmytro-mitin
标签: scala functional-programming composition scala-cats category-theory