Given a trait Conjunction with AND and OR case object sub-types:
trait Conjunction
case object AND extends Conjunction
case object OR extends Conjunction
Using Play 2 JSON, I tried to write the following Writes[Conjunction]:
implicit object ConjunctionWrites extends Writes[Conjunction] {
implicit val orWrites: Writes[OR] = Json.writes[OR]
implicit val andWrites: Writes[AND] = Json.writes[AND]
def writes(c: Conjunction) = c match {
case a#AND => Json.toJson(a)(andWrites)
case o#OR => Json.toJson(o)(orWrites)
}
}
But I got a bunch of not found: type AND/OR errors.
How can I serialize these case objects?
When you create a case object, you create a value with that name, but not a type. So AND and OR don't exist as types. If you want to refer to the type of a case object, use .type, e.g. AND.type.
However, the Json.writes macro only works on case classes, not case objects. You'll have to write your own definition:
implicit object ConjunctionWrites extends Writes[Conjunction] {
def writes(c: Conjunction) = c match {
case AND => Json.toJson("AND")
case OR => Json.toJson("OR")
}
}
Related
I'm designing a typesafe api to work with a "types" -- an abstraction in the application I'm working on. Here is how it looks like:
sealed trait EnumType
case object A extends EnumType
case object B extends EnumType
case object C extends EnumType
sealed abstract class TypeInfo[T <: EnumType](val enumType: T)
case class Ainfo() extends TypeInfo(A)
case class Binfo() extends TypeInfo(B)
case class Cinfo() extends TypeInfo(C)
sealed trait TypeMeta[T <: EnumType]
case class Ameta() extends TypeMeta[A.type]
case class Bmeta() extends TypeMeta[B.type]
case class Cmeta() extends TypeMeta[C.type]
case class TypeDescription[T <: EnumType](info: TypeInfo[T], meta: TypeMeta[T])
I'm confused about defining a function which would accept a List of TypeInfo and return TypeDescription. I currently implemeted it as follows:
//Type parameter with omitted bound? Is that type safe?
def toDescription(lst: List[TypeInfo[_]]): List[TypeDescription[_]] = {
lst map {
case a: Ainfo => TypeDescription(a, Ameta())
case b: Binfo => TypeDescription(b, Bmeta())
case c: Cinfo => TypeDescription(c, Cmeta())
}
}
To workaround the issue I used [_] pattern which does not look typesafely. Is there a way to redeclare the function?
Its type safe , however the 2 type parameters are bound individually to their own constraints and not to one another.
If you are looking to do that , I think you would need to define a method type parameter like so
//Type parameter with omitted bound? Is that type safe?
def toDescription[T<:EnumType](lst: List[TypeInfo[T]]): List[TypeDescription[T]] = {
lst map {
case a: Ainfo => TypeDescription(a, Ameta())
case b: Binfo => TypeDescription(b, Bmeta())
case c: Cinfo => TypeDescription(c, Cmeta())
}
}
Now if you tried to write
case a: Ainfo => TypeDescription(a, Bmeta())
you will get a compilation error
First the code:
object MyEnums {
sealed abstract class MyEnum(val value: String)
case object First extends MyEnum("Some_ugly_looking_value1")
case object Second extends MyEnum("Some_ugly_looking_value2")
case object Third extends MyEnum("Some_ugly_looking_value3")
case object Fourth extends MyEnum("Some_ugly_looking_value4")
def fromString(value: String): Option[MyEnum] =
value match {
case First.value => Option(First)
case Second.value => Option(Second)
case Third.value => Option(Third)
case Fourth.value => Option(Fourth)
case _ => None
}
}
What I'm trying to achieve here is to be able to parse a string value coming from the outside into the form of the above enum. At the same time I would like to have the exhaustive pattern matching compiler warning if I don't cover all options in the match expression. What options do I have here? I don't like what I implemented above, since if this enum grows I may just forget to implement the new case clause...
Consider enumeratum like so
import enumeratum._
sealed abstract class MyEnum(override val entryName: String) extends EnumEntry
object MyEnum extends Enum[MyEnum] {
val values = findValues
case object First extends MyEnum("Some_ugly_looking_value1")
case object Second extends MyEnum("Some_ugly_looking_value2")
case object Third extends MyEnum("Some_ugly_looking_value3")
case object Fourth extends MyEnum("Some_ugly_looking_value4")
}
MyEnum.withName("Some_ugly_looking_value1") // res1: MyEnum = First
Now we do not have to fiddle with pattern match when adding a new case object.
I am using the scala Enumeration type in combination with Play Framework's Reads and Writes. I would like to have a trait like:
trait EnumerationWrites[T <: Enumeration] {
def reads(jsonValue: JsValue): JsResult[T] = jsonScope match {
case JsString(s) => JsSuccess(T.withName(s)) <-- ERROR!
case _ => JsError("String value expected")
}
}
and then I would do something like
object MyObject extends EnumerationTrait[T]
This doesn't work - the compiler is not able to resolve the T type within the case matching. What is the problem?
Bad news: you can not call method withName of type parameter. You need an object.
Good news: this object could be implicit. Which you already know if you are familiar with realization of typeclass concept in scala.
So for general use deserializer for Enumeration you could use function like this:
implicit def enumerationReads[T <: Enumeration](implicit enum: T): Reads[enum.Value] = {
val names: Set[String] = enum.values map (_.toString)
Reads {
case JsString(s) => if (names contains s) JsSuccess(enum withName s)
else JsError(s"could not find value '$s' for $enum")
case _ => JsError("String value expected")
}
}
As you can see it demands enumeration to be implicitly available, so you should declare enumeration for this like
implicit case object Colors extends Enumeration {
val Red, Blue = Value
}
Or provide some implicit value for your existing objects like
implicit val colorsEvidence = Colors
I have following class structure which involves inheritance
sealed trait GeometryObject
case class Point(coordinates: Array[Double],`type` :GeometryObjectsType) extends GeometryObject
case class Polygon(coordinates: Array[Array[Array[Double]]],`type` :GeometryObjectsType) extends GeometryObject
My protocol looks as follows:
object GeoJsonProtocol extends DefaultJsonProtocol {
implicit val geometryObjectsTypeFormat = GeometryObjectsTypeFormat
implicit val polygonFormat = jsonFormat2(Polygon)
implicit val pointFormat = jsonFormat2(Point)
}
Getting an error - could not find implicit value for evidence parameter of type GeometryObject.
Is there a way how to deal with that and keep inheritance flexibility?
There is a need for class hierarchy parent format as follows:
object GeometryObjectLeafFormats extends DefaultJsonProtocol{
implicit val geometryObjectsTypeFormat = GeometryObjectsTypeFormat
implicit val polygonFormat = jsonFormat2(Polygon)
implicit val pointFormat = jsonFormat2(Point)
}
object GeometryObjectFormat extends JsonFormat[GeometryObject] {
import GeometryObjectLeafFormats._
override def read(json: JsValue): GeometryObject = json match {
case known:JsObject if known.fields.contains("type") =>
known.fields.get("type").get match{
case JsString(PointType.value) => pointFormat.read(known)
case JsString(PolygonType.value) => polygonFormat.read(known)
case unknown => deserializationError(s"unknown GeometryObject: ${unknown}")
}
case unknown => deserializationError(s"unknown GeometryObject: ${unknown}")
}
override def write(obj: GeometryObject): JsValue = obj match {
case x:Point => pointFormat.write(x)
case x:Polygon=> polygonFormat.write(x)
case unrecognized => serializationError(s"Serialization problem ${unrecognized}")
}
}
Note: I have separate class hierarchy denoting the type which is serialized to type field which is mandatory in order to make class hierarchy serialization working.
Trait GenericLinkedList , case class Cons and case object Nil were created like below.
The question is I want to use this genericLinkedList however as you know when we write this code var list = new GenericLinkedList , it will not cause Traits cannot create any object , Right? I want to create a class which extends GenericLinkedList but I cannot. How can I fix it ?
trait GenericLinkedList [+T] {
def prepend[TT >: T](x: TT): GenericLinkedList[TT] = this match {
case _ => Cons(x,this)
}
}
case class Cons[+T](head: T,tail: GenericLinkedList[T]) extends GenericLinkedList[T]
case object Nil extends GenericLinkedList[Nothing]
Your issue seems to be unable of doing
val list = new GenericLinkedList
Is your goal creating an empty list?
You can do
val list = new GenericLinkedList[Int] { }
since the trait is not abstract, but it's not pretty. You can alternatively define a companion object for your trait
object GenericLinkedList {
def apply[T](): GenericLinkedList[T] = Nil
}
and use it to initialize an empty list this way
scala> val x = GenericLinkedList[Int]()
// x: GenericLinkedList[Int] = Nil
scala> x.prepend(42)
// res0: GenericLinkedList[Int] = Cons(42,Nil)
By the way, the universal match in the prepend implementation is useless. You can just do
def prepend[TT >: T](x: TT): GenericLinkedList[TT] = Cons(x, this)