Suppose that I have the following code snippet:
import scala.language.implicitConversions
sealed trait Command {
val typeName: String
}
object Command {
implicit def command2String(implicit c: Command): String =
c.typeName
}
case object SendMessageCommand extends Command {
override val typeName: String = "send_message"
}
I would like to compare String
with Command
descendants without explicit conversion. For example:
"sendMessage" == SendMessageCommand
Q1: Is it possible in Scala?
Q2: Can I define generic implicit conversion that will convert known type such as String to super class types (like Command
in my case)?
For example:
implicit def string2Command(implicit s: String): Command = {
case "send_message" => SendMessageCommand
case _ => // some error thrown
}
P.S. I know that it is not idiomatic Scala, but it would be awesome to find out the right way.
Yes, its possible, but not with ==
because Scala support implicit conversion for these cases only: conversions to an expected type, conversions of the receiver of a selection, and implicit parameters and hence implicit is not applicable for comparison case. Instead, you have to create a comparison method
in Command trait
as below.
sealed trait Command {
val typeName: String
def isEqual(c: Command) = c.typeName == this.typeName
}
object SendMessageCommand extends Command {
override val typeName: String = "send_message"
}
implicit def stringToCommand(s: String) = new Command {override val typeName: String = s}
implicit def commandToString(c: Command) = c.typeName
val strToCmdCompare = "send_message" isEqual SendMessageCommand
val cmdToStrCompare = SendMessageCommand isEqual "send_message"
println(strToCmdCompare) //print true
println(cmdToStrCompare) //print true
Now, coming to your second question, yes its possible to define a generic implicit conversion to convert any given type into another type instance. However, as I am understanding your situation, you have list of objects of Commands, and you want particular command within those objects for given string name. For this, you must have hold of those list of instance.
sealed trait Command {
val typeName: String
//This is required for implicit conversion.
override def toString: String = typeName
}
object SendMessageCommand extends Command {
override val typeName: String = "send_message"
}
object AddMessageCommand extends Command {
override val typeName: String = "add_message"
}
object UpdateMessageCommand extends Command {
override val typeName: String = "update_message"
}
object DeleteMessageCommand extends Command {
override val typeName: String = "delete_message"
}
//List of commands.
implicit val cmds: List[Command] = List(SendMessageCommand, AddMessageCommand, UpdateMessageCommand, DeleteMessageCommand)
//Convert given type T into type U.
implicit def convert[T, U](s: T)(implicit list: List[U]): Option[U] = {
list.find(_.toString == s.toString)
}
val res: Option[Command] = "add_message"
println((res.getOrElse(null) == AddMessageCommand)) //print true
Note: as I am comparing two type instance in conversion by converting them into string
, I have to override toString
method in Command for this use case. You may need to do so for type T
as well if it is not string. Furthermore, you can implement you own logic in convert method
as per required instead of comparing with toString
conversion.