2013-08-08 9 views
5

Spesso è necessario passare attraverso le informazioni di contesto del codice come l'utente che sta eseguendo l'azione. Usiamo questo contesto per varie cose come i controlli di autorizzazione. In questi casi i valori impliciti possono rivelarsi molto utili per ridurre il codice della piastra della caldaia.scala valori impliciti estratti nella corrispondenza del modello?

Diciamo che abbiamo un contesto di semplice esecuzione che passiamo in giro: case class EC(initiatingUser:User)

Possiamo avere guardie a portata di mano:

def onlyAdmins(f: => T)(implicit context:EC) = context match{ 
    case EC(u) if(u.roles.contain(Role.ADMIN)) => f 
    case _ => throw new UnauthorizedException("Only admins can perform this action") 
} 

val result = onlyAdmins{ 
    //do something adminy 
} 

di recente ho trovato me stesso in bisogno di fare questo quando si lavora con gli attori Akka ma fanno uso di pattern matching e devo ancora trovare un buon modo per far funzionare bene gli impliciti con gli estrattori.

In primo luogo si avrebbe bisogno di passare il contesto di ogni comando, ma questo è facile:

case class DeleteCommand(entityId:Long)(implicit executionContext:EC) 
//note that you need to overwrite unapply to extract that context 

Ma la funzione di ricezione è simile al seguente:

class MyActor extends Actor{ 
    def receive = { 
    case DeleteCommand(entityId, context) => { 
     implicit val c = context 
     sender ! onlyAdmins{ 
     //do something adminy that also uses context 
     } 
    } 
    } 
} 

Sarebbe molto più semplice se le variabili estratte potrebbe essere contrassegnato come implicito ma non ho visto questa funzione:

def receive = { 
    case DeleteCommand(entityId, implicit context) => sender ! onlyAdmins{ 
    //do something adminy (that also uses context) 
    } 
} 

Ar Sei a conoscenza di altri modi alternativi di codifica, in modo da ridurre il codice dello standard?

+0

Potreste essere interessato a questo: http://stackoverflow.com/questions/6156656/how-to-pattern-match-a-class-with-multiple-argument-lists – gzm0

+0

questo suona come quello GADTs fare in Haskell, se si considera la somiglianza dei contesti di typeclass in impliciti. Potrebbe anche fornire un modo più efficace per fare corrispondenze di pattern simili a GADT in Scala che funzioni bene. –

risposta

1

Penso che il fatto che si aggiungano più set param e impliciti alle classi dei casi e che si debba aggiungere un nuovo unapply potrebbe essere il segnale che si sta imboccando un percorso non molto buono. Mentre questi tipi di cose sono possibili, probabilmente non sono una buona idea e forse qualcosa come set multipli di parametri (e impliciti) sulle case case potrebbero andare via un giorno. Ho riscritto il tuo esempio un po 'con qualcosa di più standard. Non sto dicendo che è una soluzione perfetta, ma è più lungo il percorso standard:

trait ContextCommand{ 
    def context:EC 
} 

case class DeleteCommand(entityId:Long, context:EC) extends ContextCommand 


def onlyAdmins[T](cmd:ContextCommand)(f: => T) = cmd.context match { 
    case EC(u) if(u.roles.contain(Role.ADMIN)) => f 
    case _ => throw new UnauthorizedException("Only admins can perform this action")  
} 

class MyActor extends Actor{ 
    def receive = { 
    case cmd @ DeleteCommand(entityId, ctx) => { 
     sender ! onlyAdmins(cmd){ 
     //do something adminy that also uses context 
     //Note, ctx available here via closure 
     } 
    } 
    } 
} 
+0

Ho pensato che avere un secondo elenco di parametri per le classi del caso lo sta spingendo, ma stavo cercando di utilizzare al meglio gli impliciti. Sembra che il contesto di esecuzione sia il caso d'uso perfetto per utilizzarli. È un valore che deve essere proprio lì_. Il tuo esempio è il modo classico per farlo, e forse dovrei attenermi ad esso. Questo è fondamentalmente come è stato fatto in Java per anni (o con _that che non deve essere chiamato_, ThreadLocal). –

0

Per il gusto di farlo, ho cercato di continuare con l'approccio iniziale per vedere fin dove posso prenderlo. Quello che ho finito con potrebbe essere utile in alcuni casi:

abstract class ContextCommand[T]{ 
    def context: EC 
    def reply(sender:ActorRef)(f: EC => T) = sender.!(
    try f(context) 
    catch{ 
     case e:Throwable => translateExceptionToFailure(e) 
    } 
) 
} 
trait ActorCommons[T]{ 
    case class GetCommand(val entityId:Long)(implicit val context: EC) 
    extends ContextCommand[Option[T]] 
} 

quindi posso usarlo nel l'attore come ho inteso, con l'ulteriore vantaggio che il risultato della funzione di risposta è di tipo controllato.

Problemi correlati