2015-08-13 13 views
6

eine Reihe von ADT gegeben, die Zum Beispiel zwei verschiedene Untersätze haben:Scala mit unförmigen höher kinded Koprodukte über eine natürliche Transformation zu kombinieren

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

Jetzt nehme ich habe zwei natürliche Transformationen, für einige willkürliche Monade M [_]:

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

möchte ich irgendwie diese beiden natürlichen Transformationen zu einer einzigen Transformation kombinieren:

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

Allerdings haben wir Schwierigkeiten, mit höherwertigen Koprodukten aus dem Startblock zu kommen. Selbst ein Punkt in die richtige Richtung wäre in diesem Stadium hilfreich.

Antwort

1

Okay, das ist eine sehr alte Frage, aber heutzutage, Katzen zum Beispiel bietet Coproduct und ein Verfahren or auf NaturalTransformation:

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

So könnte man diese (? mit kind-projector für den Typ Lambda) zu tun verwenden

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

EDIT: Hier ist ein vollständiges Beispiel, das auchdefiniert(mit Id statt M für die Typprüfung):

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) 

oder im Fall, dass Sie die Zwischen Coproduct vermeiden:

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

die sicherlich verschiebt verschiebt das Problem auf einen einfacheren Ort, wo es braucht eine Domäne zuordnen -> Xor.Left [Befehl] oder Xor.Right [Abfrage]. Das könnte grob mit Typetags erreicht werden, aber die Vorteile des Adt wäre besser –

+0

Sie können einfach ein anderes '~>' erstellen, um auf den 'Domain'-Typ zu arbeiten, wenn Sie möchten, wie in meiner Bearbeitung gezeigt – Markus1189