2015-08-13 10 views
6

Dato un insieme di ADT che hanno due sotto insiemi distinti Ad esempio:Scala utilizzando informe combinare coprodotti superiore kinded sopra una trasformazione naturale

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

Ora Supponiamo di avere due trasformazioni naturali, per qualche arbitraria Monade M [_]:

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

desidero unire in qualche modo queste due trasformazioni naturali in un unico trasformazione:

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

Tuttavia stiamo lottando per uscire dal blocco di partenza con avere coprodotti più elevati. Anche un punto nella giusta direzione sarebbe utile in questa fase.

risposta

1

Va bene questa è una domanda molto vecchio, ma al giorno d'oggi, i gatti ad esempio fornisce Coproduct e un metodo or su NaturalTransformation:

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

Così si potrebbe usare questo per fare (usando kind-projector per il tipo lambda ?)

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

EDIT: Ecco un esempio completo che definisce anche.210 (utilizzando Id invece di M per il tipo di controllo):

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) 

o nel caso in cui si vuole evitare l'intermedio 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

che sposta certamente sposta il problema in un luogo più facile, in cui ha bisogno per mappare un dominio -> Xor.Left [Comando] o Xor.Right [Query]. Questo potrebbe essere approssimativamente ottenuto con Typetags, ma approfittare dell'Adt sarebbe meglio –

+0

puoi facilmente comporre un altro '~>' per lavorare sul tipo 'Dominio' se vuoi come mostrato nella mia modifica – Markus1189

Problemi correlati