2013-08-08 20 views
5

Oft müssen wir die Code-Kontextinformationen wie den Benutzer, der die Aktion ausführt, übergeben. Wir verwenden diesen Kontext für verschiedene Dinge wie Berechtigungsprüfungen. In diesen Fällen können sich implizite Werte als sehr nützlich erweisen, um den Kesselplattencode zu reduzieren.scala implizite extrahierte Werte im Mustervergleich?

Lassen Sie uns sagen, dass wir einen einfachen Ausführungskontext haben, dass wir um passieren: case class EC(initiatingUser:User)

Wir können praktisch Wachen haben:

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 
} 

ich mich vor kurzem in Not gefunden, dies zu tun, wenn sie mit Akka Schauspieler arbeiten, aber Sie verwenden Mustererkennung und ich muss noch eine gute Möglichkeit finden, um zu bewirken, dass die Arbeit mit Extraktoren gut funktioniert.

Zunächst müßten Sie den Kontext mit jedem Befehl übergeben, aber das ist einfach:

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

Aber die Empfangsfunktion wie folgt aussieht:

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

Es wäre viel einfacher, wenn extrahierten Variablen könnte als implizit markiert sein, aber ich habe diese Funktion nicht gesehen:

Ar Sind Ihnen alternative Möglichkeiten zur Kodierung bekannt, so dass der Vorlageindex reduziert wird?

+0

Sie könnten daran interessiert sein: http://stackoverflow.com/questions/6156656/how-to-pattern-match-a-class-with-multiple-argument-lists – gzm0

+0

Das klingt wie das, was GADTs tun Sie in Haskell, wenn Sie die Ähnlichkeit von typeclass Kontexten zu implicits betrachten. Es könnte auch eine prinzipielle Möglichkeit bieten, GADT-ähnliche Mustervergleiche in Scala durchzuführen, die gut funktionieren. –

Antwort

1

Ich denke, dass die Tatsache, dass Sie mehrere Param-Sets und implicits zu Fallklassen hinzufügen und auch eine neue unapply hinzufügen, Anzeichen dafür sein können, dass Sie einen nicht so guten Weg gehen. Während diese Arten von Dingen möglich sind, sind sie wahrscheinlich keine gute Idee und vielleicht könnte so etwas wie mehrere Parametersätze (und implicits) auf Fallklassen eines Tages weggehen. Ich habe dein Beispiel ein bisschen mit etwas mehr Standard umgeschrieben. Ich sage nicht, es ist eine perfekte Lösung, aber es ist mehr auf dem Standard-Pfad:

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

Ich dachte, dass es eine zweite Parameterliste für Fallklassen gibt, aber ich habe versucht, die Implikate optimal zu nutzen. Es scheint, dass der Ausführungskontext der perfekte Anwendungsfall für deren Verwendung ist. Es ist ein Wert, der gerade da sein muss. Ihr Beispiel ist der klassische Weg, und vielleicht sollte ich dabei bleiben. Dies ist im Grunde so, wie es seit Jahren in Java gemacht wurde (oder mit _das das nicht named_, ThreadLocal sein darf). –

0

Aus Gründen der es, ich mit dem ursprünglichen Ansatz fortzusetzen versuchte weit zu sehen, wie ich es nehmen kann. Was ich am Ende mit könnte nützlich sein, in einigen Fällen:

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

dann kann ich es in dem Schauspieler benutzen, wie ich gedacht, mit dem zusätzlichen Vorteil, dass das Ergebnis der Antwortfunktion Typ-geprüft ist.