In Scala, how to implicitly call class classes, knowing only the super type?

How can I find the correct implicit type class CommandHandler, knowing only its superclass?

In the MainRunner class, you can see that the following code requires me to implement a type class AllCommandHandlerthat matches a pattern across all types. Is there a way to find the right type class for a super type UserCommandwithout implementation AllCommandsHandler?

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

I need this because some parts of my code get List[UserCommand]that either come from the database or are generated after checking the input command.

My code

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
source share
1 answer

CommandHandler UserCommand , UserCommand a CreateUser, UpdatePassword ( , UserCommand sealed).

  • UserCommand CreateUser :+: UpdatePassword :+: CNil.
  • cnilCommandHandler 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, CommandHandler[DBCommand].

+4

All Articles