2013-08-08 35 views
5

Chúng tôi thường cần phải đi qua thông tin ngữ cảnh mã như người dùng đang thực hiện hành động. Chúng tôi sử dụng ngữ cảnh này cho nhiều thứ khác nhau như kiểm tra ủy quyền. Trong những trường hợp này, các giá trị tiềm ẩn có thể chứng minh là rất hữu ích để giảm bớt mã tấm nồi hơi.Các giá trị trích xuất được trích xuất trong kết hợp mẫu?

Hãy nói rằng chúng tôi có một bối cảnh đơn giản thực hiện mà chúng ta vượt qua xung quanh: case class EC(initiatingUser:User)

Chúng ta có thể có lính gác tiện dụng:

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 
} 

thời gian gần đây tôi thấy mình cần phải làm điều này khi làm việc với các diễn viên Akka nhưng họ sử dụng kết hợp mẫu và tôi chưa tìm ra cách tốt để làm cho implic hoạt động tốt với các bộ giải nén.

Trước tiên, bạn sẽ cần phải vượt qua bối cảnh với mỗi lệnh, nhưng đó là dễ dàng:

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

Nhưng nhận chức năng trông như thế này:

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

Sẽ đơn giản hơn nhiều nếu biến chiết xuất có thể được đánh dấu là tiềm ẩn nhưng tôi chưa thấy tính năng này:

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

Ar e bạn biết về bất kỳ cách thay thế mã hóa này để nó làm giảm mã boilerplate?

+0

Bạn có thể quan tâm này: http://stackoverflow.com/questions/6156656/how-to-pattern-match-a-class-with-multiple-argument-lists – gzm0

+0

này nghe có vẻ giống như những gì GADTs làm trong Haskell, nếu bạn xem xét sự giống nhau của bối cảnh typeclass đối với implicits. Nó cũng có thể cung cấp một cách nguyên tắc hơn để thực hiện mô hình giống như GADT phù hợp trong Scala hoạt động tốt. –

Trả lời

1

Tôi nghĩ thực tế là bạn đang thêm nhiều bộ tham số và liên quan đến các lớp chữ hoa và cũng phải thêm unapply mới có thể là dấu hiệu cho thấy bạn đang đi xuống một con đường không tốt. Trong khi các loại điều này là có thể, chúng có lẽ không phải là một ý tưởng tốt và có thể một cái gì đó giống như nhiều bộ param (và implicits) về các trường hợp có thể biến mất một ngày. Tôi viết lại ví dụ của bạn một chút với một cái gì đó tiêu chuẩn hơn. Tôi không nói đó là một giải pháp hoàn hảo, nhưng đó là một giải pháp hoàn hảo hơn trên con đường tiêu chuẩn:

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

Tôi đã nghĩ rằng có một danh sách tham số thứ hai cho các trường hợp lớp học đang đẩy nó, nhưng tôi đã cố gắng để tận dụng tốt nhất của implicits. Dường như ngữ cảnh thực thi là trường hợp sử dụng hoàn hảo để sử dụng chúng. Đó là một giá trị mà _just cần phải có there_. Ví dụ của bạn là cách cổ điển để làm điều đó, và có lẽ tôi nên dính vào nó. Về cơ bản, đây là cách nó được thực hiện trong Java cho các lứa tuổi (hoặc với _that không được đặt tên_, ThreadLocal). –

0

Vì lợi ích của nó, tôi đã cố gắng tiếp tục với cách tiếp cận ban đầu để xem tôi có thể lấy bao xa. Những gì tôi đã kết thúc có thể hữu ích trong một số trường hợp:

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

thì tôi có thể sử dụng nó trong diễn viên như tôi dự định, với lợi ích bổ sung là kết quả của hàm trả lời được kiểm tra loại.

Các vấn đề liên quan