2015-08-13 20 views
6

給定一組的ADT具有兩個不同的子集 例如結合:斯卡拉使用不成形更高kinded副產品在改造自然

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

現在假設我有兩個自然變換,對於一些任意的單子中號[_]:

val commandHandler:Command ~> M 
val queryExecutor:Query ~> M 

我想這兩個自然變換莫名其妙地組合成一個單一的轉變:

val service:Domain ~> M = union(commandHandler, queryExecutor) 

然而,我們正在努力使具有更高皮的副產品脫穎而出。即使在正確的方向上的一個點將有助於在這個階段。

回答

1

好吧,這是一個非常古老的問題,但現在,例如貓提供CoproductorNaturalTransformation的方法:

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

所以,你可以用這個做(用kind-projector的類型拉姆達?

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

編輯:這裏是一個完整的例子,還定義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) 

或者如果你想避免中間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) 
+0

這肯定轉移轉移的問題,以更容易的地方,在那裏它需要映射域 - > Xor.Left [命令]或Xor.Right [查詢]。這大概可以通過類型標籤來實現,但利用Adt會更好 –

+0

如果您希望如我的編輯中所示,您可以輕鬆地編寫另一個'〜>'來處理'Domain'類型 – Markus1189