2013-08-08 12 views
5

Często potrzebujemy przechodzić przez informacje kontekstowe kodu, takie jak użytkownik, który wykonuje akcję. Używamy tego kontekstu do różnych czynności, takich jak sprawdzanie uprawnień. W tych przypadkach wartości domyślne mogą okazać się bardzo przydatne w zmniejszeniu kodu płyty kotła.scala niejawne wartości wyodrębnione w dopasowywaniu wzorca?

Powiedzmy mamy proste kontekst wykonania, które przechodzą wokół: case class EC(initiatingUser:User)

Możemy mieć poręczne strażników:

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 
} 

Niedawno znalazłem się w potrzebie, aby to zrobić, gdy pracuje z aktorami Akka ale używają dopasowywania wzorców i mam jeszcze znaleźć dobry sposób na to, aby implicite działały dobrze z ekstraktorami.

Najpierw trzeba by przekazać kontekst z każdym poleceniem, ale to jest proste:

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

Ale odbierać funkcja wygląda tak:

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

byłoby znacznie prostsze, jeśli wyodrębnionych zmiennych można oznaczyć jako niejawne, ale nie widziałem tej funkcji:

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

Ar Czy znasz jakieś alternatywne sposoby kodowania, aby zmniejszyć kod podstawowy?

+0

Możesz być zainteresowany w tym: http://stackoverflow.com/questions/6156656/how-to-pattern-match-a-class-with-multiple-argument-lists – gzm0

+0

To brzmi jak co GADTs w Haskell, jeśli weźmiesz pod uwagę podobieństwo kontekstów typeclass do implicite. Może to również zapewnić bardziej zasadniczy sposób wykonywania dopasowań podobnych do GADT w Scali, która działa dobrze. –

Odpowiedz

1

Myślę, że fakt dodawania wielu zestawów parametrów i implicite do klas przypadków, a także konieczność dodania nowego unapply może być oznaką, że idziesz niezbyt dobrą ścieżką. Chociaż tego typu rzeczy są możliwe, prawdopodobnie nie są dobrym pomysłem i może coś takiego jak wiele zestawów parametrów (i implicite) na lekcjach przypadku może zniknąć pewnego dnia. Przepisałem trochę twój przykład czymś bardziej standardowym. Nie mówię, że jest to idealne rozwiązanie, ale jest to bardziej w dół standardowej ścieżce:

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

Myślałem, że posiadanie drugiej listy parametrów dla klas przypadków jest popychaniem jej, ale starałem się jak najlepiej wykorzystać implikacje. Wydaje się, że kontekst wykonania jest idealnym przypadkiem użycia ich użycia. Jest to wartość, która musi być tam. Twój przykład to klasyczny sposób na to, i może powinienem się do tego przyzwyczaić. Zasadniczo jest to robione w Javie przez wieki (lub z _tym, który nie może być nazwany_, ThreadLocal). –

0

Dla niej, starałem się kontynuować początkowego podejścia, aby zobaczyć, jak daleko mogę go wziąć. Co skończyło się może być przydatna w niektórych przypadkach:

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

wtedy można go używać w aktora jak zamierzałem, z dodatkową korzyścią, że wynik funkcji odpowiedzi stanowi rodzaj sprawdzane.

Powiązane problemy