【问题标题】:Compositional batching using functional constructs使用功能构造的组合批处理
【发布时间】:2020-11-01 19:33:56
【问题描述】:

我正在尝试创建组合批处理机制。

流程必须是。

  1. s: Map[BatchFragmentId, FragmentRequest]的形式累积状态
  2. Map[BatchFragmentId, FragmentResponse]的形式计算结果
  3. 对某些结果 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) =&gt; C 类型吗?我在任何地方都没有看到A
  • 你在两个陈述中都是对的@user。组合应该是Batch[String]t 应该是(R, B) =&gt; 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


【解决方案1】:

在挖掘了猫提供的各种typeclass之后,我发现Applicative非常适合这个问题。

对于任何对结果感兴趣的人,我最终得到了以下结果。

type BatchFragmentId = String
trait FragmentRequest
trait FragmentResponse
type S = Map[BatchFragmentId, FragmentRequest]

object Batch {
  def make[A](s: S, f: Map[BatchFragmentId, FragmentResponse] => A): Batch[A] = Batch[A](
   transformer = f,
   state = s
  )
}

implicit object BatchApplicative extends Applicative[Batch] {
  override def pure[A](x: A): Batch[A] = Batch[A](
    transformer = _ => x,
    state =  Map.empty
  )

  override def ap[A, B](ff: Batch[A => B])(fa: Batch[A]): Batch[B] = {
    val f: Map[String, FragmentResponse] => B = (r: Map[BatchFragmentId, FragmentResponse]) => ff.transformer(r)(fa.transformer(r))
    Batch[B](
      transformer = f,
      state =  ff.state ++ fa.state
    )
  }
}

case class Batch[A] (
                      transformer: Map[BatchFragmentId, FragmentResponse] => A,
                      state: S
                    )

implicit class BatchOps[A](fa: Batch[A])(implicit A: Applicative[Batch]) {
  def <>[B](fb: Batch[B]): Batch[(A, B)] = A.product(fa, fb)
}

// Example
case class SomeFragmentRequest(x: Int) extends FragmentRequest
case class SomeFragmentResponse(x: Int) extends FragmentResponse

val id: BatchFragmentId = "some-id"
val exampleBatch = Batch.make[SomeFragmentResponse](
  s = Map(id -> SomeFragmentRequest(42)),
  f = responses => responses.get(id).map{ case x: SomeFragmentResponse => x }.get
)

// Example of composition
val o: Batch[(SomeFragmentResponse, SomeFragmentResponse)] = exampleBatch <> exampleBatch
// N arity
val o2: Batch[(SomeFragmentResponse, SomeFragmentResponse, SomeFragmentResponse, SomeFragmentResponse)] =
  Applicative[Batch].tuple4(exampleBatch, exampleBatch, exampleBatch, exampleBatch)

def run(s: S): Map[BatchFragmentId, FragmentResponse] = ???
def resolve[R](r: Map[BatchFragmentId, FragmentResponse], b: Batch[R]): R = b.transformer(r)

val res: (SomeFragmentResponse, SomeFragmentResponse) = resolve(run(o.state), o)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 2020-02-11
    • 2015-08-18
    • 2010-09-19
    相关资源
    最近更新 更多