2013-08-08 3 views
5

Нам часто приходится проходить через контекстную информацию кода, такую ​​как пользователь, выполняющий действие. Мы используем этот контекст для различных вещей, таких как проверки полномочий. В этих случаях неявные значения могут оказаться очень полезными для уменьшения кода плиты котла.scala неявные извлеченные значения в сопоставлении с образцом?

Допустим, у нас есть простой контекст выполнения, который мы пройти вокруг: case class EC(initiatingUser:User)

Мы можем иметь под рукой охранникам:

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 
} 

Недавно я нашел себя в необходимости делать это при работе с Akka актерами, но они используют сопоставление с образцом, и я еще не нашел хороший способ сделать импликации хорошо работать с экстракторами.

Сначала вам нужно будет пройти контекст с каждой командой, но это легко:

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

Но получить функция выглядит следующим образом:

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

Было бы намного проще, если добытых переменных могут быть помечены как неявные, но я не видел эту функцию:

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

Ar Вы знаете какие-либо альтернативные способы кодирования, чтобы уменьшить код шаблона?

+0

Вы могли бы быть заинтересованы в этом: http://stackoverflow.com/questions/6156656/how-to-pattern-match-a-class-with-multiple-argument-lists – gzm0

+0

Это звучит как что GADTs делать в Haskell, если вы считаете, что сходство контекстов типов классов подразумевает. Это может также обеспечить более принципиальный способ сделать подобный GADT сопоставление шаблонов в Scala, который прекрасно работает. –

ответ

1

Я думаю, что факт, что вы добавляете несколько наборов параметров и имплицитов в классы классов, а также для добавления нового unapply, может быть признаком того, что вы идете по не очень хорошему пути. Хотя эти типы вещей возможны, они, вероятно, не очень хорошая идея, и, возможно, что-то вроде нескольких наборов параметров (и имплицитов) на классах случаев может уйти в один прекрасный день. Я немного переписал ваш пример с чем-то более стандартным. Я не говорю, что это идеальное решение, но это больше вниз по стандартному пути:

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

Я думал, что наличие второго списка параметров для классов case вызывает его, но я старался максимально использовать имплициты. Кажется, что контекст исполнения является идеальным вариантом использования для их использования. Это значение, которое просто должно быть там. Ваш пример - это классический способ сделать это, и, возможно, я должен придерживаться его. Это в основном то, как это было сделано в Java целую вечность (или с тем, что не должно быть named_, ThreadLocal). –

0

Ради этого, я попытался продолжить первоначальный подход, чтобы увидеть, насколько далеко я могу взять его. То, что я в конечном итоге с может быть полезно в некоторых случаях:

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

то я могу использовать его в качестве актера, я предполагал, с дополнительным преимуществом, что результат функции ответа типа проверяется.

Смежные вопросы