Scala使用无形结合较高的kinded副产品而不是自然转化

J P*_*lar 6 scala higher-kinded-types shapeless

给定一组具有两个不同子集的Adts例如:

sealed trait Domain[Y]
sealed trait Command[Y] extends Domain[Y]
sealed trait Query[Y] extends Domain[Y]

case class Add(value:String) extends Command[Ack]
case class Remove(value:String) extends Command[Ack]
case class Exists(value:String) extends Query[Boolean]
case object List extends Query[List[String]]
Run Code Online (Sandbox Code Playgroud)

现在假设我有两个自然变换,对于一些任意的Monad M [_]:

val commandHandler:Command ~> M
val queryExecutor:Query ~> M
Run Code Online (Sandbox Code Playgroud)

我希望以某种方式将这两个自然转换组合成一个转换:

val service:Domain ~> M = union(commandHandler, queryExecutor)
Run Code Online (Sandbox Code Playgroud)

然而,我们正在努力通过拥有更高级别的副产品来摆脱起跑线.在这个阶段,即使是正确方向的一点也会有所帮助.

Mar*_*189 1

好吧,这是一个非常古老的问题,但现在,例如猫提供了NaturalTransformationCoproduct方法:or

trait NaturalTransformation[F[_], G[_]] extends Serializable { self =>
  def or[H[_]](h: H ~> G): Coproduct[F, H, ?] ~> G = ???
}
Run Code Online (Sandbox Code Playgroud)

所以你可以用它来做(使用kind-projector作为 lambda 类型?

val combine: Coproduct[Command,Query,?] ~> M = commandHandler.or(queryExecutor)
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个完整的示例,还定义了union(使用Id而不是M进行类型检查):

import cats._
import cats.data._

trait Ack
sealed trait Domain[Y]
sealed trait Command[Y] extends Domain[Y]
sealed trait Query[Y] extends Domain[Y]

case class Add(value:String) extends Command[Ack]
case class Remove(value:String) extends Command[Ack]
case class Exists(value:String) extends Query[Boolean]
case object List extends Query[List[String]]

def commandHandler:Command ~> Id = ???
def queryExecutor:Query ~> Id = ???

def union: Domain ~> Coproduct[Command,Query,?] = new (Domain ~> Coproduct[Command,Query,?]) {
  def apply[A](fa: Domain[A]): Coproduct[Command,Query,A] = fa match {
    case command: Command[A] => Coproduct.left(command)
    case query: Query[A] => Coproduct.right(query)
  }
}

def result: Domain ~> Id = commandHandler.or(queryExecutor).compose(union)
Run Code Online (Sandbox Code Playgroud)

或者如果你想避免中间Coproduct

def unionDirect[M[_]](cmd: Command ~> M, qry: Query ~> M): Domain ~> M =
  new (Domain ~> M) {
    def apply[A](fa: Domain[A]): M[A] = fa match {
      case command: Command[A] => cmd(command)
      case query: Query[A] => qry(query)
    }
}

def resultDirect: Domain ~> Id = unionDirect(commandHandler,queryExecutor)
Run Code Online (Sandbox Code Playgroud)