I have the following code and I wonder if the nasty thing could be removed. Override doesn't work if I add implicit parameter.
class FromEnumFormat[T <: Enum[T]] extends JsonFormat[T] {
override def read(json: JsValue): T = readit(json)
def readit(json: JsValue)(implicit m: Manifest[T]): T = {
val EnumerationClass = m.runtimeClass.asInstanceOf[Class[T]]
json match {
case s: JsString if EnumerationClass.getEnumConstants.map(_.toString.toLowerCase).contains(s) => Enum.valueOf(EnumerationClass, s.value.toUpperCase()).asInstanceOf[T]
case unknown => deserializationError(s"unknown Status: $unknown")
}
}
override def write(obj: T): JsValue = JsString(obj.name().toLowerCase)
}
Is there a way how to get rid of this line
override def read(json: JsValue): T = readit(json)
The question is essentially: Are the implicit parameters part of method signature?
UPDATE:
Code doesn't compile. So correct solution should be:
class FromEnumFormat[T<: Enum[T]] extends JsonFormat[T] {
implicit val m: Manifest[T] = ???
override def read(json: JsValue): T = {
val EnumerationClass = m.runtimeClass.asInstanceOf[Class[T]]
json match {
case s :JsString if EnumerationClass.getEnumConstants.map(_.toString.toLowerCase).contains(s) => Enum.valueOf(EnumerationClass ,s.value.toUpperCase()).asInstanceOf[T]
case unknown => deserializationError(s"unknown Status: ${unknown}")
}
}
override def write(obj: T): JsValue = {JsString(obj.name().toLowerCase)}
}
The question is how to access Manifest? I am using Scala 2.10
UPDATE:
Find that the issue is with scala reflection not with implicits and overrides, hence closing this question.
To answer your question, yest, implicit parameters are part of method signature. However, as it is implicit, this means you can call the def readit(json: JsValue)(implicit m: Manifest[T]): T function without mentioning it, as long as it has been declared somewhere in the code :
implicit val manifest = /* whatever a Manifest is*/
readit(jsonValue)
Could you please provide an example of what you'd like to do ?
Your final solution is nearly correct, but it has m in the wrong place, it should be a class parameter instead:
class FromEnumFormat[T<: Enum[T]](implicit m: Manifest[T]) extends JsonFormat[T] {
override def read(json: JsValue): T = {
val EnumerationClass = m.runtimeClass.asInstanceOf[Class[T]]
json match {
case s :JsString if EnumerationClass.getEnumConstants.map(_.toString.toLowerCase).contains(s) => Enum.valueOf(EnumerationClass ,s.value.toUpperCase()).asInstanceOf[T]
case unknown => deserializationError(s"unknown Status: ${unknown}")
}
}
override def write(obj: T): JsValue = {JsString(obj.name().toLowerCase)}
}
This way the compiler can insert it when creating a FromEnumFormat with a specific enum:
val format = new FromEnumFormat[TimeUnit]
If you want to create a generic FromEnumFormat[T], though, you'll have to pass implicit m: Manifest[T] as an argument:
def doSomethingWithFormat[T <: Enum[T]](implicit m: Manifest[T]) = {
val format = new FromEnumFormat[T]
...
}
Implicit parameters are not exactly part of the signature. But So you cannot add an implicit parameter by means of overloading.
Here's why. Multiple parameter list syntax is just a syntactic sugar. The code
def readit(json: JsValue)(implicit m: Manifest[T]): T = ???
gets desugared to
def readit(json: JsValue): Function1[Manifest[T], T] = {
def inner(implicit m: Manifest[T]): T = ???
inner
}
So as you see the real type of readit just doesn't match the type of read, thus it is not a proper override.
UPD.
As Alexey pointed out formally multiple parameter list contributes to type signature (see section 4.6 Function Declarations and Definitions of scala spec).
A function declaration has the form def fpsig: T, where f is the function's name, psig is its parameter signature and T is its result type.
A parameter signature consists of an optional type parameter clause
[tps], followed by zero or more value parameter clauses
(ps1)…(psn).
Related
I have written this code
trait Input[F[_]] {
def read: F[String]
def write(str: String) : F[Unit]
def getName : F[String] = for {
_ <- write("What is your name")
name <- read
} yield name
}
This code doesn't compile obviously because the compiler has no way of knowing that the type F supports flatmap. So I change my code to
import scalaz._
import Scalaz._
trait Input[F[_] : Monad] {
def read: F[String]
def write(str: String) : F[Unit]
def getName : F[String] = for {
_ <- write("What is your name")
name <- read
} yield name
}
but now I get compile time error traits cannot have type parameters with context bounds.
So how can I specify constraints on my type parameter so that it always supports flatmap?
trait Input[F[_]: Monad] would create an implicit constructor parameter and traits can't have constructor parameters (until Scala 3). def testFunc[F[_]: Monad] would create an implicit parameter. For instance:
def testFunc[F[_]: Monad](arg: Int) = ???
class TestClass[F[_]: Monad] {}
Will work because it is translated to:
def testFunc[F[_]](arg: Int)(implicit ev: Monad[F]) = ???
class TestClass[F[_]](implicit val ev: Monad[F]) {}
I.e. [F[_]: Monad] is syntactic sugar for [F[_]] with implicit val ev: Monad[F]. And traits has no constructor to pass parameters until Scala 3.
For your case, if you really need to inside a trait to constraint F to a Monad for example, then:
trait Input[F[_]] {
val M: Monad[F]
def read: F[String]
def write(str: String) : F[Unit]
def getName : F[String] = M.flatMap(write("What is your name"))(_ => read)
}
I.e. you are saying to one who implements, that "you can implement Input, as soon as you have Monad[F]". Then you can use it like:
object Main extends App{
class IOInput extends Input[IO] {
override val M: Monad[IO] = Monad[IO]
override def read: IO[String] = IO("red")
override def write(str: String): IO[Unit] = IO(println(s"write: $str"))
}
val impl = new IOInput
println(impl.getName.unsafeRunSync())
}
P.S. But for me, it seems like something is wrong. You are kinda defining effects in trait Input and using them right in the same trait. Reads weird for me at least. Probably getName should be somewhere else.
I am trying to write a JsonFormat for an abstract class with a generic parameter that looks like something like this:
abstract class Animal[A] {
def data: A
def otherStuff: String = "stuff"
}
case class CatData(catField: String)
case class Cat(data: CatData) extends Animal[CatData]
So far my attempt at this looks like:
object AnimalProtocol extends DefaultJsonProtocol {
implicit val catDataFormat = jsonFormat1(CatData)
implicit val catFormat = jsonFormat1(Cat)
implicit def animalFormat[T <: Animal[T]](t: T)(implicit fmt: JsonWriter[T]) = new RootJsonFormat[Animal[T]] {
def write(obj: Animal[T]) = obj match {
case x: Cat => catFormat.write(x)
}
def read(json: JsValue) = ???
}
Now, if I try to do this:
import AnimalProtocol._
val cat: Animal[CatData] = Cat(CatData("this is cat data"))
I get the compiler error:
Cannot find JsonWriter or JsonFormat type class for Animal[CatData]
How can I make it work? In the end I want to write json with the fields in Animal and with data set to whatever case class applies.
You need to provide a type parameter for both the generic field and the subclass of Animal in your implicit def:
object AnimalProtocol2 extends DefaultJsonProtocol {
implicit val catDataFormat = jsonFormat1(CatData)
implicit def animalFormat[A, T <: Animal[A]](implicit fmt: JsonWriter[A]): RootJsonFormat[T] = new RootJsonFormat[T] {
def write(obj: T) = {
JsObject(
"data" -> obj.data.toJson,
"otherStuff" -> obj.otherStuff.toJson
)
}
def read(json: JsValue) = ???
}
}
That also allows you to get rid of pattern matching on subclasses inside animalFormat.
I don't use spray-json (I've got much more experience with play-json), but I'll try to help by pointing at a few strange things in your code.
I'm not sure you need implicit val catFormat = jsonFormat1(Cat), unless you want it to be applied instead of animalFormat when type is known to be Cat.
You definition of animalFormat looks wrong/strange for the following reasons:
type is strange, T <: Animal[T] doesn't correspond to your types i.e., you don't have CatData <: Animal[CatData]
you don't use t
you don't use fmt (but instead you pattern match on obj)
I would suggest to either define a static animalFormat, something like (not sure about the wildcard type _):
val animalFormat: RootJsonFormat[Animal[_]] = new RootJsonFormat[Animal[_]] {
def write(obj: Animal[_]) = {
JsObject(
"otherStuff" -> JsString(obj.otherStuff),
"data" -> obj match {
case x: Cat => catDataFormat.write(x.data)
}
)
def read(json: JsValue) = ???
}
or, without using pattern matching:
implicit def animalFormat[T](implicit fmt: JsonWriter[T]) = new RootJsonFormat[Animal[T]] {
def write(obj: Animal[T]) =
JsObject(
"otherStuff" -> JsString(obj.otherStuff),
"data" -> fmt.write(obj.data)
)
def read(json: JsValue) = ???
}
Note that with this approach, you won't be able to read a generic Animal as there's no type information in the json.
I'm creating custom json readers for case classes but it can't find implicit JsonReader type class for List[T] which is used in other case class.
When I checked DefaultJsonProtocol, it has implicit format for collections already;
implicit def listFormat[T :JsonFormat] = new RootJsonFormat[List[T]] {
def write(list: List[T]) = JsArray(list.map(_.toJson).toVector)
def read(value: JsValue): List[T] = value match {
case JsArray(elements) => elements.map(_.convertTo[T])(collection.breakOut)
case x => deserializationError("Expected List as JsArray, but got " + x)
}
}
Here is the simplified code;
case class Test(i: Int, d: Double)
case class ListOfTest(t: List[Test])
trait TestResultFormat extends DefaultJsonProtocol {
import CustomFormat._
implicit object TestJsonFormat extends RootJsonReader[Test] {
override def read(json: JsValue): Test = {
val jsObject = json.asJsObject
val jsFields = jsObject.fields
val i = jsFields.get("i").map(_.convertTo[Int]).getOrElse(0)
val d = jsFields.get("d").map(_.convertTo[Double]).getOrElse(0d)
Test(i, d)
}
}
implicit object ListOfTestJsonFormat extends RootJsonReader[ListOfTest] {
override def read(json: JsValue): ListOfTest = {
val jsObject = json.asJsObject
val jsFields = jsObject.fields
val tests = jsFields.get("hs").map(_.convertTo[List[Test]]).getOrElse(List.empty)
ListOfTest(tests)
}
}
}
Here is the errors;
Error:(230, 53) not enough arguments for method convertTo: (implicit evidence$1: spray.json.JsonReader[List[com.xx.Test]])List[com.xx.Test].
Unspecified value parameter evidence$1.
val tests = jsFields.get("hs").map(_.convertTo[List[Test]]).getOrElse(List.empty)
^
Error:(230, 53) Cannot find JsonReader or JsonFormat type class for List[com.xx.Test]
val tests = jsFields.get("hs").map(_.convertTo[List[Test]]).getOrElse(List.empty)
^
I think the problem is related to the fact that the JsonReader for List[T] in DefaultJsonProtocol is a RootJsonFormat (not a RootJsonReader), which basically means you can read it and also write it. So, when you try to read a List[Item], it's expected that you are also able to write Items. So, you could use RootJsonFormat instead and throw an exception in case you try to write it (since you don't support it). For example:
import spray.json._
implicit object TestJsonFormat extends RootJsonFormat[Test] {
override def read(json: JsValue): Test = {
val jsObject = json.asJsObject
val jsFields = jsObject.fields
val i = jsFields.get("i").map(_.convertTo[Int]).getOrElse(0)
val d = jsFields.get("d").map(_.convertTo[Double]).getOrElse(0d)
Test(i, d)
}
override def write(obj: Test): JsValue = serializationError("not supported")
}
If you are aware of a clean solution involving only the readers, please do let me know because I ran into this problem myself and couldn't find anything else.
I have learned that limitation comes from spray-json:
spray-json 's type class infrastructure is build around the (Root)JsonFormat type, not the (Root)JsonReader. So you'll indeed have to provide a "Format" even if you are just reading.
Check here.
To overcome issue; I created another trait extends RootJsonFormat instead of reader and overrides write method with basically not implemented method.
trait EmptyWriterFormat[T] extends RootJsonFormat[T] {
override def write(o: T): JsValue = ???
}
I am trying to implement a ReadJsonCodec of sorts using the automatic type class derivation mechanism in Shapeless.
Here is my ReadCodecCompanionObject:
object ReadCodec extends LabelledProductTypeClassCompanion[ReadCodec] {
implicit object StringCodec extends SimpleCodec[String] {
def read(j: Json): String = j.stringOr(throw ...)
}
implicit object IntCodec ...
implicit object BooleanCodec ...
implicit object LongCodec ...
implicit object ShortCodec ...
implicit object DoubleCodec ...
implicit object BigDecimalCodec ...
implicit def readCodecInstance: LabelledProductTypeClass[ReadCodec] = new LabelledProductTypeClass[ReadCodec] {
def emptyProduct = new ReadCodec[HNil] {
// This will silently accept extra fields within a JsonObject
// To change this behavior make sure json is a JsonObject and that it is empty
def read(json: Json) = HNil
}
def product[F, T <: HList](name: String, FHead: ReadCodec[F], FTail: ReadCodec[T]) = new ReadCodec[F :: T] {
def read(json: Json): F :: T = {
val map = castOrThrow(json)
val fieldValue = map.getOrElse(name, throw new MappingException(s"Expected field $name on JsonObject $map"))
// Try reading the value of the field
// If we get a mapping exception, intercept it and add the name of this field to the path
// If we get another exception, don't touch!
// Pitfall: if handle did not accept a PartialFunction, we could transform an unknow exception into a match exception
val head: F = Try(FHead.read(fieldValue)).handle{ case MappingException(msg, path) => throw MappingException(msg, s"$name/$path")}.get
val tail = FTail.read(json)
head :: tail
}
}
def product[A, T <: HList](name: String, FHead: ReadCodec[Option[A]], FTail: ReadCodec[T]) = new ReadCodec[Option[A] :: T] {
def read(json: Json): Option[A] :: T = {
val map = castOrThrow(json)
val head: Option[A] = map.get(name).map { fieldValue =>
Try(FHead.read(fieldValue)).handle{ case MappingException(msg, path) => throw MappingException(msg, s"$name/$path")}.get.get
}
val tail = FTail.read(json)
head :: tail
}
}
def project[F, G](instance: => ReadCodec[G], to : F => G, from : G => F) = new ReadCodec[F] {
def read(json: Json): F = from(instance.read(json))
}
}
}
That's somewhat of a complicated piece of code to just grasp quickly but is really fairly simple once you understand it. The important part is the two def product methods. The problem I am having is that I want this codec to accept a Json AST that is missing a field if that field would be mapped to a value of type Option[A]. This means that I need the product function to know if the head of the HList is of type Option[A] or not.
Concretely:
case class Foo(a: String, b: Option[Boolean])
val test = read[Foo](json"""{"a" : "Beaver"}""") // should work
But currently this would fail because it makes no distinction for Option and expects a field b. I tried two ways of fixing this and neither of them worked.
The first one and the cleanest one is to overload the product method with a version where the F type parameter is replaced with Option[A]. This approach is fairly clean, although I am not sure whether or not it would play well with the shapeless derivation macro. However, it is not possible because Scala does not support the ability to overload functions when the type signature after erasure is the same. This is the version that is above. Unfortunately, this does not currently compile with the Scala compiler.
The second approach was to use a TypeTag to find out at runtime if F is of type Option[_] and behave appropriately. This version would almost certainly be less elegant and involve a cast but I could live with it. However, it seems impossible because the addition of a TypeTag changes the signature of the product method (adding an implicit parameter) and then the compiler complains that I am not defining the abstract method product.
Does anyone have any suggestions on the best way to proceed.
You need to get a typeclass with information about F passed through. But you're already passing a typeclass around, in the form of ReadCodec. So the solution is to replace that with one that contains all the information you need:
trait ReadCodecAndTypeTag[A] {
val rc: ReadCodec[A]
val tt: TypeTag[A]
}
But in that case you might as well delegate the decoding-from-optional-value-in-a-map to this typeclass as well:
trait OReadCodec[A] {
val rc: ReadCodec[A]
def missingField(name: String, map: Any): A =
throw new MappingException(s"Expected field $name on JsonObject $map")
}
implicit object StringCodec extends OReadCodec[String] {
val rc = new ReadCodec[String] {...}
}
implicit object IntCodec ...
...
implicit def OptionCodec[A](implicit orc: OReadCodec[A]) =
new OReadCodec[Option[A]] {
val rc = ...
override def missingField(name: String, map: Any) = None
}
...
def product[F, T <: HList](name: String, FHead: OReadCodec[F], FTail: OReadCodec[T]) =
new OReadCodec[F :: T] {
val rc = new ReadCodec[F :: T] {
def read(json: Json): F :: T = {
val map = castOrThrow(json)
val fieldValue = map.getOrElse(name, FHead.missingField(name, map))
val head: F = ...
...
}
}
}
implicit def ReadCodecFromOReadCodec[A](implicit orc: OReadCodec[A]) = orc.rc
Suppose that I have the following scala code:
trait ValueSource[A] {
def value(a: Int): A
}
trait ValueSourceOps[A] {
def self: Int
def F: ValueSource[A]
def value: A = F.value(self)
}
trait ToValueSourceOps {
implicit def toValueSourceOps[A](index: Int)(implicit F0: ValueSource[A]): ValueSourceOps[A] = new ValueSourceOps[A] {
def self = index
def F: ValueSource[A] = F0
}
}
object Test extends ToValueSourceOps {
def value[A: ValueSource](index: Int): A = toValueSourceOps(index).value
}
The code above compiles well, but when I change the last line (body of method "value" in object Test) to
def value[A: ValueSource](index: Int): A = index.value
the compiler complains that
could not find implicit value for parameter F0: ValueSource[A]
In my opinion, def value[A: ValueSource] means I have a implicit value "ValueSource[A]", then why does the compilation fail?
The A in toValueSourceOps has no relationship with the A in value, which makes inferring it problematic. To me the real question is why it works when you call the method explicitly.
I'm guessing that, when toValueSourceOp is called explicitly, it has to infer the same A because that's the only implicit available.