How to create my own custom converts class - scala

I have a very generic message object that I get back from a queue like:
case class Message(key: String, properties: Map[String, String])
I then have a bunch of very specific classes that represent a message, and I use properties.get("type") to determine which particular message it is:
sealed trait BaseMessage
case class LoginMessage(userId: Int, ....) extends BaseMessage
case class RegisterMessage(email: String, firstName: String, ....) extends BaseMessage
Now in my code I have to convert from a generic Message to a particular message in many places, and I want to create this in a single place like:
Currently I am doing something like:
val m = Message(....)
val myMessage = m.properties.get("type") match {
case Some("login") => LoginMessage(m.properties("userID"), ...)
case ...
}
What options do I have in making this less cumbersome in scala?

I don't know all your context here, but I can suggest using implicit conversions if you don't want to bring another library in your project. Anyway, implicit conversions can help you separate a lot the implementation or override it "on-the-fly" as needed.
We can start by defining a MessageConverter trait that is actually a function:
/**
* Try[T] here is useful to track deserialization errors. If you don't need it you can use Option[T] instead.
*/
trait MessageConverter[T <: BaseMessage] extends (Message => Try[T])
Now define an object that holds both the implementations and also enables a nice #as[T] method on Message instances:
object MessageConverters {
/**
* Useful to perform conversions such as:
* {{{
* import MessageConverters._
*
* message.as[LoginMessage]
* message.as[RegisterMessage]
* }}}
*/
implicit class MessageConv(val message: Message) extends AnyVal {
def as[T <: BaseMessage : MessageConverter]: Try[T] =
implicitly[MessageConverter[T]].apply(message)
}
// Define below message converters for each particular type
implicit val loginMessageConverter = new MessageConverter[LoginMessage] {
override def apply(message: Message): Try[LoginMessage] = {
// Parse the properties and build the instance here or fail if you can't.
}
}
}
That's it! It may not be the best solution as implicits bring complexity and they make code harder to follow. However, if you follow a well-defined structure for storing these implicit values and be careful how you pass them around, then you shouldn't have any issues.

You can convert the properties map to Json and read it as a case class. Assuming that the keys to the map have the same name as your case class fields you can write a formatter using playjson:
object LoginMessage {
implicit val fmtLoginMessage = Json.format[LoginMessage]
}
If the fields don't have the same name you will have to specify the reads object manually. Your code to convert it into a case class would be something like:
object BaseMessageFactory {
def getMessage(msg: Message): Option[BaseMessage] = {
val propertiesJson = Json.toJson(msg.properties)
msg.properties.get("type").mapĀ {
case "login" => propertiesJson.as[LoginMessage]
...
case _ => //Some error
}
}
}
The signature may differ depending on how you want to deal with error handling.

Related

What would be the best way in scala to convert between case classes?

I have a model class im getting back from an api inside my servcie, and when I return it to some client I want to use my own model to keep it simpler and cleaner for the client.
example :
case class ReturnedModel(succeed: Option[String], reason: Reason, transactionId: List[Int], opId: Option[Int])
case class MyReturnedModel(reason: String)
I might need to do more of those in the future so I thought maybe there is a best practice to do it that I dont know of, thanks!
You can use a companion object with a custom "apply" method:
case class MyReturnedModel(reason: String)
object MyReturnedModel {
def apply(mod: ReturnedModel) = MyReturnedModel(mod.reason.toString)
}
val data: ReturnedModel = ... // Some instance of ReturnedModel
val mr = MyReturnModel(data)
Just note that the case class and its companion object need to be in the same file for this to work.
Depending on your use case:
sealed trait IKnowAReason { def reason:String }
case class ReturnedModel(succeed: Option[String], reason: Reason,
transactionId: List[Int], opId: Option[Int]) extends IKnowAReason
Now replace uses of MyReturnedModel with IKnowAReason. Notice the sealed, it will ensure that there are no other implementations of IKnowAReason outside the same source file.
If you have access to change the ReturnedModel, you could use traits like #pedrofurla has demonstrated.
If you are unable to modify the ReturnedModel, you could declare an implicit function to convert all instance of ReturnedModel to MyReturnedModel like this:
implicit def returnedModelToMyModel(returnedModel: ReturnedModel): MyReturnedModel = {
// Have some logic to convert their model to your model
MyReturnedModel(returnedModel.reason.toString)
}
Then whenever you get a ReturnedModel from the API, you can use it anywhere you are expecting an instance of MyReturnedModel:
def doWork(myReturnedModel: MyReturnedModel) = { /* Logic that needs and instance of MyReturnedModel */ }
// Grab an instance of ReturnModel from the API
val returned: ReturnedModel = ???
// Will get converted when you need it to be an instance of MyReturnedModel
doWork(returned)
The compiler will try to preform implicit conversions when it finds that the type you have passed is not correct, at which point it will look for an implicit conversion to satisfy the type conversion.

How to define implicit Writes in trait

I have multiple case classes representing values in DB for ex User which saves user based properties like name / age / address and CallLog which saves timestamp / status_of_call
What i want to achieve
I want to have a helper function which accepts list of models and checks if the list is empty then returns "error" otherwise should return json array of the list.
My Approach
I want to have a trait which groups certain models in it and the helper method will accept either the trait or List of it in order to check or may be have a generic which implements the trait.
Problem
Since implicit writes are tightly coupled with the model class, compiler throws the error on the line Json.toJson(list)
Things i have tried
Kept implicit in trait and got recursive type error
I am scala noob pardon me if this sounds silly
Thanks in advance
Since User, CallLog, etc. will be serialized differently, Each Writes[T] will be different for each implementation of your Model trait, so a Writes[Model] has to know about the implementation it is trying to serialize.
It is therefore not possible to have it part of the Model trait, because this information isn't known yet when you define it.
A workaround in your case would be to define your Writes[Model] in the scope of your helper function instead.
An implementation of your helper function could be like this :
import play.api.libs.json.{JsValue, Json, Writes}
sealed trait Model
case class User(name: String, age: String, address: String) extends Model
object User {
implicit val userWrites = Json.writes[User]
}
case class CallLog(timestamp: String, status_of_call: String) extends Model
object CallLog {
implicit val callLogWrites = Json.writes[CallLog]
}
implicit val modelWrites = new Writes[Model] {
override def writes(o: Model): JsValue = o match {
case u: User => Json.toJson(u)
case cl: CallLog => Json.toJson(cl)
}
}
def helper(models: Model*): Either[JsValue, String] = models match {
case Nil => Right("Error")
case _ => Left(Json.toJson(models))
}
helper(User("John", "32", "..."))
helper(User("John", "32", "..."), CallLog("now", "In progress"))

Generic function with implicit parameter

I have a situation where I am trying to create a generic function which should be able to take any instance of a class which specifies a certain implicit value in its companion object. I have replicated my problem below:
// Mocking up the library I am working with
trait Formatter[A] {
def output(o: A): String
def input(s: String): A
}
// Some models
trait Human
case class Child(name: String) extends Human
object Child {
implicit val f: Formatter[Child] = new Formatter[Child] {
override def output(c: Child): String = { ... }
override def input(s: String): Child = { ... }
}
}
case class Teen(name: String) extends Human
object Teen {
implicit val f: Formatter[Teen] = new Formatter[Teen] {
override def output(t: Teen): String = { ... }
override def input(s: String): Teen = { ... }
}
}
// The generic function
def gen[A <: Human](a: A)(implicit format: Formatter[A]) = {
// Do something with a formatter...
}
This all works fine, I can pass an instance of a Child or a Teen to my gen function:
gen(Child("Peter"))
gen(Teen("Emily"))
What I am having trouble with is that at run time I only know that the instance I am passing will be a subtype of a Human:
// Example of unknown subtype
val human: Human = Random.nextBoolean match {
case true => Child("Peter")
case false => Teen("Emily")
}
gen(human) // Error: Could not find implicit value for parameter format...
I understand that the error is because Human has no companion object and therefore it has no implementation of a Formatter.
How can I add a constraint to Human that says "anything extending Human will implement a new Formatter" ?
Your scenario fails because you should implement a Formatter[Human]. I think that what you want is that all the Human should be able to have this format "capability" instead.
At this point you have two options, one is to include in the Human trait a method for formatting (this implementation could be in the object if you want it static) or try a dsl approach where you will create a class with the responsibility to provide humans a new capability: "format".
The first approach could be something like this:
trait Human { def format:String }
case class Child(name: String) extends Human {
import Child._
override def format = Child.staticFormat(this)
}
object Child {
def staticFormat(c: Child): String = s"Child(${c.name})"
}
However I think "format" shouldn't be in the contract "Human" so I prefer the second approach:
trait Human
case class Child(name: String) extends Human
case class Teen(name: String) extends Human
import scala.language.implicitConversions
class HumanFormatter(human: Human) {
def format: String = human match {
case c: Child => s"Child(${c.name})"
case t: Teen => s"Teen(${t.name})"
}
}
object HumanDsl {
implicit def humanFormatter(human: Human): HumanFormatter = new HumanFormatter(human)
}
object Test extends App {
def human: Human = Random.nextBoolean match {
case true => Child("Peter")
case false => Teen("Emily")
}
import HumanDsl._
for(i <- 1 to 10) println(human.format)
}
What are the differences between both solutions?
In the first one you force all the new Human classes to implement a format method so you can assure that your code will work always. But at the same time... you are adding a Human a method that from my point of view is not necessary, I think a case class should have only the information needed and if you need to format/parse that class then is better to add this functionality just when needed (dsl approach).
In the other hand, with dsl you should update the formatter anytime a new Human class is created. So it means that the human.format method above will fail if a new Human class is created (you can always match _ to do a default behaviour or raise a custom error).
I think is a matter of design, I hope this would help you a little bit.
Edited:
just like a comment showed the Human trait could be sealed to ensure that the HumanFormatter pattern match doesn't compile if some class is not covered.
You don't need implicits for this.
Just make your subclasses point to the implementation directly:
trait Human[+A <: Human] {
def formatter: Formatter[A]
}
case class Child(name: String) extends Human[Child] {
def formatter = Child.f
}
// etc
def gen[A <: Human](a: A) {
// do something with a.formatter
}
Of course, Formatter needs to be covariant in A too. Otherwise, all bets are off: you simply cannot do what you want - there is nothing useful gen could do with it without knowing the specific type anyway.
If specifics of the concrete type are not needed in gen, you can still use implicits by enumerating them explicitly like this (but I don't really see why you would want that):
object Human {
implicit def formatter(h: Human): Formatter[_] = h match {
case Child(_) => Child.f
case Teen(_) => Teen.f
}
}
gen(h: Human)(implicit f: Formatter[_]) { ... }
Like I said, this does not seem very useful though, so not sure why you want want this over the above approach.

How to write class and tableclass mapping for slick2 instead of using case class?

I use case class to transform the class object to data for slick2 before, but current I use another play plugin, the plugin object use the case class, my class is inherent from this case class. So, I can not use case class as the scala language forbidden use case class to case class inherent.
before:
case class User()
class UserTable(tag: Tag) extends Table[User](tag, "User") {
...
def * = (...)<>(User.tupled,User.unapply)
}
it works.
But now I need to change above to below:
case class BasicProfile()
class User(...) extends BasicProfile(...){
...
def unapply(i:User):Tuple12[...]= Tuple12(...)
}
class UserTable(tag: Tag) extends Table[User](tag, "User") {
...
def * = (...)<>(User.tupled,User.unapply)
}
I do not know how to write the tupled and unapply(I am not my writing is correct or not) method like the case class template auto generated. Or you can should me other way to mapping the class to talbe by slick2.
Any one can give me an example of it?
First of all, this case class is a bad idea:
case class BasicProfile()
Case classes compare by their member values, this one doesn't have any. Also the name is not great, because we have the same name in Slick. May cause confusion.
Regarding your class
class User(...) extends BasicProfile(...){
...
def unapply(i:User):Tuple12[...]= Tuple12(...)
}
It is possible to emulate case classes yourself. Are you doing that because of the 22 field limit? FYI: Scala 2.11 supports larger case classes. We are doing what you are trying at Sport195, but there are several aspects to take care of.
apply and unapply need to be members of object User (the companion object of class User). .tupled is not a real method, but generated automatically by the Scala compiler. it turns a method like .apply that takes a list of arguments into a function that takes a single tuple of those arguments. As tuples are limited to 22 columns, so is .tupled. But you could of course auto-generated one yourself, may have to give it another name.
We are using the Slick code generator in combination with twirl template engine (uses # to insert expressions. The $ are inserted as if into the generated Scala code and evaluated, when the generated code is compiled/run.). Here are a few snippets that may help you:
Generate apply method
/** Factory for #{name} objects
#{indentN(2,entityColumns.map(c => "* #param "+c.name+" "+c.doc).mkString("\n"))}
*/
final def apply(
#{indentN(2,
entityColumns.map(c =>
colWithTypeAndDefault(c)
).mkString(",\n")
)}
) = new #{name}(#{columnsCSV})
Generate unapply method:
#{if(entityColumns.size <= 22)
s"""
/** Extractor for ${name} objects */
final def unapply(o: ${name}) = Some((${entityColumns.map(c => "o."+c.name).mkString(", ")}))
""".trim
else
""}
Trait that can be mixed into User to make it a Scala Product:
trait UserBase with Product{
// Product interface
def canEqual(that: Any): Boolean = that.isInstanceOf[#name]
def productArity: Int = #{entityColumns.size}
def productElement(n: Int): Any = Seq(#{columnsCSV})(n)
override def toString = #{name}+s"(${productIterator.toSeq.mkString(",")})"
...
case-class like .copy method
final def copy(
#{indentN(2,columnsCopy)}
): #{name} = #{name}(#{columnsCSV})
To use those classes with Slick you have several options. All are somewhat newer and not documented (well). The normal <> operator Slick goes via tuples, but that's not an option for > 22 columns. One option are the new fastpath converters. Another option is mapping via a Slick HList. No examples exist for either. Another option is going via a custom Shape, which is what we do. This will require you to define a custom shape for your User class and another class defined using Column types to mirror user within queries. Like this: http://slick.typesafe.com/doc/2.1.0/api/#scala.slick.lifted.ProductClassShape Too verbose to write by hand. We use the following template code for this:
/** class for holding the columns corresponding to #{name}
* used to identify this entity in a Slick query and map
*/
class #{name}Columns(
#{indent(
entityColumns
.map(c => s"val ${c.name}: Column[${c.exposedType}]")
.mkString(", ")
)}
) extends Product{
def canEqual(that: Any): Boolean = that.isInstanceOf[#name]
def productArity: Int = #{entityColumns.size}
def productElement(n: Int): Any = Seq(#{columnsCSV})(n)
}
/** shape for mapping #{name}Columns to #{name} */
object #{name}Implicits{
implicit object #{name}Shape extends ClassShape(
Seq(#{
entityColumns
.map(_.exposedType)
.map(t => s"implicitly[Shape[ShapeLevel.Flat, Column[$t], $t, Column[$t]]]")
.mkString(", ")
}),
vs => #{name}(#{
entityColumns
.map(_.exposedType)
.zipWithIndex
.map{ case (t,i) => s"vs($i).asInstanceOf[$t]" }
.mkString(", ")
}),
vs => new #{name}Columns(#{
entityColumns
.map(_.exposedType)
.zipWithIndex
.map{ case (t,i) => s"vs($i).asInstanceOf[Column[$t]]" }
.mkString(", ")
})
)
}
import #{name}Implicits.#{name}Shape
A few helpers we put into the Slick code generator:
val columnsCSV = entityColumns.map(_.name).mkString(", ")
val columnsCopy = entityColumns.map(c => colWithType(c)+" = "+c.name).mkString(", ")
val columnNames = entityColumns.map(_.name.toString)
def colWithType(c: Column) = s"${c.name}: ${c.exposedType}"
def colWithTypeAndDefault(c: Column) =
colWithType(c) + colDefault(c).map(" = "+_).getOrElse("")
def indentN(n:Int,code: String): String = code.split("\n").mkString("\n"+List.fill(n)(" ").mkString(""))
I know this may a bit troublesome to replicate, especially if you are new to Scala. I hope to to find the time get it into the official Slick code generator at some point.

Scala 2.9: Parsers subclass not recognizing "Elem" override?

I wrote a parser to act as a lexer. This lexer parses a file and returns a list of tokens, each of which is a case class or object that extends a common trait.
I am now trying to write a parser for the output of the lexer, but I have hit a very confusing snag. The parser is happy to implicitly cast my case objects, but throws a fit if I even try to call apply(classHere) manually.
The following is a simplified version of my code:
// CODE
trait Token
case class StringWrapperIgnoresCase(val string: String) {
private case class InnerWrapper(s: String)
lazy val lower = string.toLowerCase
override lazy val hashCode = InnerWrapper(lower).hashCode
override def equals(that: Any) =
that.isInstanceOf[StringWrapperIgnoresCase] &&
lower == that.asInstanceOf[StringWrapperIgnoresCase].lower
}
case class ID(val text: String)
extends StringWrapperIgnoresCase(text)
with Token {
override def toString = "ID(" + text + ")"
}
case object PERIOD extends Token
object Parser extends Parsers {
type Elem = Token
def doesWork: Parser[Token] = PERIOD
def doesNotWork: Parser[Token] = ID
}
The compiler reports the following message about doesNotWork:
// ERROR MESSAGE
type mismatch; found : alan.parser.ID.type (with underlying type object alan.parser.ID) required: alan.parser.Parser.Parser[alan.parser.Token]
How can I fix this?
Update: It wasn't clear to me from your question exactly what you were asking, but now that you've specified that you want a parser that matches any ID in your answer, here's a more idiomatic solution:
val id: Parser[ID] = accept("ID", { case i: ID => i })
Here you've provided a description of what the parser wants (for error messages) and a partial function with IDs as its domain. You could also use the acceptIf version that xiefei provides in a comment on your answer.
When you refer to a case class (as opposed to a case object) without a parameter list, you get the automatically generated companion object, which is not an instance of the class itself. Consider the following:
sealed trait Foo
case class Bar(i: Int) extends Foo
case object Baz extends Foo
Now Baz: Foo is just fine, but Bar: Foo will give an error very similar to what you're seeing.
Note also that what's happening here isn't strictly casting. The Parsers trait has a method with the following signature:
implicit def accept(e: Elem): Parser[Elem]
When you write this:
def doesWork: Parser[Token] = PERIOD
You're trying to type an Elem as a Parser[Elem], and the implicit conversion kicks in (see section 7.3 of the spec for more information about implicit conversions). When you write this, on the other hand:
def doesNotWork: Parser[Token] = ID
You're trying to type the ID companion object (which has type ID.type, not ID or Token, and therefore not Elem) as a Parser[Elem], and there's no implicit conversion that makes this possible.
You're probably better off writing out accept(PERIOD) and accept(ID("whatever")), for now, at least, and obeying the deprecation warning that says the following when you try to compile your code:
Case-to-case inheritance has potentially dangerous bugs which are
unlikely to be fixed.
Using what TravisBrown and drstevens have said, I have added a new production to the parser:
def id = {
acceptIf(
_ match {
case ID(_) => true
case _ => false
}
)("'ID(_)' expected but " + _ + " found")
}
def nowWorks = id
I won't accept this as the answer for the time being to allow someone to provide a more elegant solution than this. This looks a bit messy for my tastes, and I'm certain someone more accustomed to the functional programming approach will turn this into an elegant one-liner.