2016-06-14 4 views
1

タイプの正しい暗黙のクラスは、そのスーパークラスだけを知ることでどのようにして見つけることができますか?Scalaでは、スーパークラスだけを知っている型クラスを暗黙的に呼び出す方法は?

MainRunnerクラスでは、すべての型でパターンマッチングを行うAllCommandHandler型クラスを実装する必要があります。 AllCommandsHandlerを実装せずに、スーパータイプUserCommandの正しいタイプのクラスを見つける方法はありますか?私のコードの一部は、入力されたコマンドを検証した後、データベースから来るか生成されますのいずれかList[UserCommand]を取得するので、私はこれを必要と

val command: UserCommand = CreateUser("userId2") 
CommandHandlerRunner.processCommand(command) 

理由があります。あなたが(それはUserCommandsealedであることが重要である方法である)CreateUserUpdatePasswordのいずれかとしUserCommandを表すことができますので、下記のUserCommandため

私のコード

import annotation.implicitNotFound 

@implicitNotFound("No member of type class CommandHandler in scope for ${C}") 
trait CommandHandler[C <: UserCommand] { 
    def processCommand(command: C) 

} 

trait Command 

sealed trait UserCommand 

case class CreateUser(id: String) extends UserCommand 

case class UpdatePassword(id: String, password: String) extends UserCommand 

object CommandHandler { 

    implicit object CreateUserCommandHandler extends CommandHandler[CreateUser] { 
    override def processCommand(command: CreateUser) = println(command) 
    } 

    implicit object UpdateUserPasswordCommandHandler extends CommandHandler[UpdatePassword] { 
    override def processCommand(command: UpdatePassword) = println(command) 
    } 

    //I have to do this which is ugly. Is there a way I can invoke the right CommandHandler without doing the following ? 
    implicit object AllCommandsHandler extends CommandHandler[UserCommand] { 
    override def processCommand(command: UserCommand) = command match { 
     case command: CreateUser => 
     CommandHandlerRunner.processCommand(command) 
     case command: UpdatePassword => 
     CommandHandlerRunner.processCommand(command) 
     case _ => 
     sys.error("CommandHandler not found.") 
    } 
    } 
} 

object CommandHandlerRunner { 

    def processCommand[C <: UserCommand](command: C)(implicit commandHandler: CommandHandler[C]) = 
    commandHandler.processCommand(command) 
} 

object MainRunner extends App { 
    CommandHandlerRunner.processCommand(CreateUser("userId1")) 
    CommandHandlerRunner.processCommand(UpdatePassword("userId1", "newPassword")) 

    //In my application in certain situation I get a List[UserCommand] (database query) and I want to invoke processCommand their relevant 
    //handlers. How do I get this to work ? 
    val command: UserCommand = CreateUser("userId2") 
    CommandHandlerRunner.processCommand(command) 
} 
+1

'UserCommand'が封印された形質であった場合は、これは可能です。 –

+0

はい、それは封印された形質です。私はコードを更新しました。私は形を変えて読んでみるつもりです。 – hajime

答えて

4

CommandHandler導出が動作します。 UserCommandから不定形総称表現で

  • はその後CreateUser :+: UpdatePassword :+: CNilあります。
  • cnilCommandHandlercoproductConsCommandHandlerは、この汎用表現のインスタンスをCommandHandlerにすることができます。
  • genericCommandHandlerは、この汎用表現のインスタンスを使用してUserCommandGeneric[UserCommand]を使用)のインスタンスを提供します。今、私たちはあなたのUserCommandを使用することができます

    import shapeless._ 
    
    trait CommandHandler[C] { 
        def processCommand(command: C): Unit 
    } 
    
    object CommandHandler { 
        // make it possible to derive a CommandHandler instance for sealed traits 
        // like UserCommand using shapeless Coproduct and Generic 
    
        implicit val cnilCommandHandler: CommandHandler[CNil] = 
        new CommandHandler[CNil] { 
         override def processCommand(t: CNil): Unit =() 
        } 
    
        implicit def coproductConsCommandHandler[L, R <: Coproduct](implicit 
        lch: CommandHandler[L], 
        rch: CommandHandler[R] 
    ): CommandHandler[L :+: R] = 
        new CommandHandler[L :+: R] { 
         override def processCommand(t: L :+: R): Unit = t match { 
         case Inl(l) => lch.processCommand(l) 
         case Inr(r) => rch.processCommand(r) 
         } 
        } 
    
        implicit def genericCommandHandler[A, G](implicit 
        gen: Generic.Aux[A, G], 
        cch: Lazy[CommandHandler[G]] 
    ): CommandHandler[A] = 
        new CommandHandler[A] { 
         def processCommand(a: A): Unit = cch.value.processCommand(gen.to(a)) 
        } 
    } 
    

    sealed trait UserCommand 
    final case class CreateUser(id: String) extends UserCommand 
    final case class UpdatePassword(id: String, password: String) extends UserCommand 
    
    object UserCommand { 
        implicit val createUserCommandHandler: CommandHandler[CreateUser] = 
        new CommandHandler[CreateUser] { 
         override def processCommand(command: CreateUser) = println(command) 
        } 
    
        implicit val updateUserPasswordCommandHandler: CommandHandler[UpdatePassword] = 
        new CommandHandler[UpdatePassword] { 
         override def processCommand(command: UpdatePassword) = println(command) 
        } 
    } 
    

    ...あなたのCommandHandlerRunnerで、でもCommandHandlerから上限タイプ(<: UserCommandを)落とす


タイプがUserCommandの場合:

object CommandHandlerRunner { 
    def processCommand[C](command: C)(implicit commandHandler: CommandHandler[C]) = 
    commandHandler.processCommand(command) 
} 

val cmd1: UserCommand = CreateUser("foo") 
val cmd2: UserCommand = UpdatePassword("id", "open sesame") 

CommandHandlerRunner.processCommand(cmd) 
// CreateUser(foo) 

List(cmd1, cmd2).foreach(CommandHandlerRunner.processCommand[UserCommand] _) 
// CreateUser(foo) 
// UpdatePassword(id,open sesame) 

これはAllCommandHandlerよりもコードされている可能性がありますが、あなたが今、次のようにDBCommandを作成した場合:

sealed trait DBCommand 
final case class Get(id: Int) extends DBCommand 
final case class Save[A](a: A) extends DBCommand 

そしてGetSaveためCommandHandlerインスタンスを作成し、あなたもCommandHandler[DBCommand]になるだろう。

+0

うわー!これはちょうどうわーです! ...ウィザードリィ笑!どうもありがとうございます。私はこれの背後にある理論を理解しなければならないだろうが、うわー!!私はこれ以上upvoteことができれば願っています。 – hajime

+1

Wizardryは実際にシェイプレスでできることを説明する言葉ですが、実際にはこれらのことが意味をなさせるのに時間がかかります...それでもなお魔法のように感じます;-) –

+0

私の質問では、 'processCommand'関数に' state:S'パラメータを入れないのは残念ですが、実際の関数は 'def processCommand(command:C、state:S)です:S '。あなたのコードで空白を埋めるようにしていますが、 '' S:State'を追加する '' coproductConsCommandHandler'と '' genericCommandHandler''で何をすべきかわかりません。 – hajime

関連する問題