在Scala中如何隐式调用类型类只知道它的超类型?

时间:2016-06-14 15:12:40

标签: scala

如何只知道它的超类?我怎样才能找到正确的隐式CommandHandler类型?

在MainRunner类中,您可以看到以下代码要求我实现AllCommandHandler类型类,它在所有类型上进行模式匹配。有没有办法可以为超类型UserCommand找到合适的类型类而不实现AllCommandsHandler

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

我需要这个的原因是因为我的代码的某些部分得到List[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 个答案:

答案 0 :(得分:4)

以下CommandHandler的{​​{1}}推导有效,因为您可以将UserCommand表示为UserCommandCreateUser(这很重要UpdatePasswordUserCommand)。

  • 在无形状中,来自sealed的通用表示是UserCommand
  • CreateUser :+: UpdatePassword :+: CNilcnilCommandHandler可以为此通用表示获取coproductConsCommandHandler个实例。
  • CommandHandler使用此通用表示形式的实例为genericCommandHandler提供实例(使用UserCommand)。

Generic[UserCommand]CommandHandler):

中删除上限类型
<: 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)) } }

UserCommand

...使用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

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 CommandHandler创建Get个实例,您还可以获得Save