I am going through some exercises I have invented on case classes and typeclasses. One of the problems I have faced is the following:
object Example extends App {
sealed trait Serializer[T] {
def serialize(seq: List[T]): String
}
implicit object StringSerializer extends Serializer[String] {
def serialize(seq: List[String]): String = seq.toString()
}
implicit object IntSerializer extends Serializer[Int] {
def serialize(seq: List[Int]): String = seq.toString()
}
case class Marker[T: Serializer](lst: Option[List[T]] = None)
Marker() // ambiguous implicit values: here...
}
Now this gives an error about ambiguous implicit values. I think this is related to a question I have asked before (albeit a different error message):
Type erasure in a nested list with a given context bound
Am I correct in that it is the same process at work here, even though the error message is different?
Compiler can't infer T. Try to specify T explicitly
Marker[String]() // compiles
Marker[Int]() // compiles
When you provide lst it can infer T itself
Marker(Some(List(1, 2)))
Marker(Some(List("a", "b")))
For the same reason
Marker(Option.empty[List[Int]])
Marker(Option.empty[List[String]])
Marker[Int](None)
Marker[String](None)
Marker(None: Option[List[Int]])
Marker(None: Option[List[String]])
compile while Marker(None) doesn't.
Alternatively you can prioritize your implicits
trait LowPrioritySerializer {
implicit object StringSerializer extends Serializer[String] {
def serialize(seq: List[String]): String = seq.toString()
}
}
object Serializer extends LowPrioritySerializer {
implicit object IntSerializer extends Serializer[Int] {
def serialize(seq: List[Int]): String = seq.toString()
}
}
Then IntSerializer will be tried firstly and StringSerializer will be tried secondly if IntSerializer didn't work (if type is different).
Related
I have this minimal example, I want to create encoders/decoders with circe semi-automatic derivation for the generic case class A[T]
import io.circe.{Decoder, Encoder}
import io.circe.generic.semiauto._
import io.circe.syntax._
sealed trait MyTrait
object MyTrait {
implicit val encoder: Encoder[MyTrait] = deriveEncoder
implicit val decoder: Decoder[MyTrait] = deriveDecoder
}
case class A[T](value: T) extends MyTrait
object A {
implicit def encoder[T: Encoder]: Encoder[A[T]] = deriveEncoder
implicit def decoder[T: Decoder]: Decoder[A[T]] = deriveDecoder
}
This codes does not compile and instead outputs this error
could not find Lazy implicit value of type io.circe.generic.encoding.DerivedAsObjectEncoder[A]
And the same for the decoder
What Am I doing wrong here and how can I get it working?
There are few issues. One is that MyTrait Encoder/Decoder will try to dispatch encodeing/decoding into codecs for the subtypes - since e thtrait is sealed all possible traits are known, so such list can be obtained by compiler.
BUT
While MyTrait trait does not take type parameters, its only implementation A takes. Which basically turns it into an existential type.
val myTrait: MyTrait = A(10)
myTrait match {
case A(x) =>
// x is of unknown type, at best you could use runtime reflection
// but codecs are generated with compile time reflection
}
Even if you wanted to make these codecs manually, you have no way of doing it
object Scope {
def aEncoder[T: Encoder]: Encoder[A[T]] = ...
val myTraitEncoder: Encoder[MyTrait] = {
case A(value) =>
// value is of unknown type, how to decide what Encoder[T]
// pass into aEncoder?
}
}
For similar reasons you couldn't manually implement Decoder.
To be able to implement codec manually (which is kind of prerequisite to being able to generate it), you can only remove type parameters as you go into subclasses, never add them.
sealed trait MyTrait[T]
case class A[T](value: T) extends MyTrait[T]
This would make it possible to know what kind of T is inside A since it would be passed from MyTrait, so you could write your codec manually and they would work.
Another problem is that reliable generation of ADT's usually require some configuration, e.g. whether or not to use discimination field. And that is provided by circe-generic-extras. Once you use it, (semi)automatic derivation is possible:
import io.circe.{Decoder, Encoder}
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto._
import io.circe.syntax._
// can be used to tweak e.g. discriminator field name
implicit val config: Configuration = Configuration.default
sealed trait MyTrait[T]
object MyTrait {
implicit def encoder[T: Encoder]: Encoder[MyTrait[A]] = deriveEncoder
implicit def decoder[T: Decoder]: Decoder[MyTrait[A]] = deriveDecoder
}
case class A[T](value: T) extends MyTrait[T]
object A {
implicit def encoder[T: Encoder]: Encoder[A[T]] = deriveEncoder
implicit def decoder[T: Decoder]: Decoder[A[T]] = deriveDecoder
}
Among other solutions to the problem, if you really didn't want to use type parameter in MyTrait but have polymorphism in A would be to replace umbound, generic A with another ADT (or sum type in Scala 3), so that list of all possible implementations would be known and enumerated.
// x is of unknown type, at best you could use runtime reflection
I'll just comment that in principle you can define Decoder with runtime reflection (runtime compilation) based on the class of value in case class A[T](value: T)
sealed trait MyTrait
case class A[T](value: T) extends MyTrait
object A {
// custom codecs
implicit val intEnc: Encoder[A[Int]] = new Encoder[A[Int]] {
override def apply(a: A[Int]): Json = Json.obj("A" -> Json.obj("value" -> Json.fromInt(a.value)))
}
implicit val strEnc: Encoder[A[String]] = new Encoder[A[String]] {
override def apply(a: A[String]): Json = Json.obj("value" -> Json.fromString(a.value))
}
implicit val boolEnc: Encoder[A[Boolean]] = new Encoder[A[Boolean]] {
override def apply(a: A[Boolean]): Json = Json.fromBoolean(a.value)
}
}
val Integer = classOf[Integer]
val Boolean = classOf[java.lang.Boolean]
def primitiveClass[T](runtimeClass: Class[_]): Class[_] = runtimeClass match {
case `Integer` => classOf[Int]
case `Boolean` => classOf[Boolean]
//...
case _ => runtimeClass
}
object MyTrait {
implicit val encoder: Encoder[MyTrait] = new Encoder[MyTrait] {
override def apply(a: MyTrait): Json = a match {
case a1#A(t) =>
tb.eval(q"implicitly[io.circe.Encoder[A[${rm.classSymbol(primitiveClass(t.getClass)).toType}]]]")
.asInstanceOf[Encoder[A[_]]]
.apply(a1)
}
}
}
(A(1): MyTrait).asJson.noSpaces // {"A":{"value":1}}
(A("a"): MyTrait).asJson.noSpaces //{"value":"a"}
(A(true): MyTrait).asJson.noSpaces //true
But for case class A[T](value: Int) such trick would be impossible.
I don't get the following code to compile and I am curious of what I did wrong.
I defined a Contravariant Jsonwriter Trait and a function accepting implicit writers:
trait JsonWriter[-A] {
def write(value: A): Json
}
object Json {
def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
writer.write(value)
}
Additionally I have defined some instances of these writers:
object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
(value: String) => JsString(value)
implicit val doubleWriter: JsonWriter[Double] =
(value: Double) => JsNumber(value)
class OptionWriter[-T](writer: JsonWriter[T]) extends JsonWriter[Option[T]] {
def write(value: Option[T]): Json = {
value match {
case None => JsNull
case Some(x) => writer.write(x)
}
}
}
implicit def optionWriter[T](implicit writer: JsonWriter[T]):
JsonWriter[Option[T]] = new OptionWriter[T](writer)
}
Now I have written a test:
"write double Option" in {
Some(1.0).toJson should be(JsNumber(1.0))
None.toJson should be(JsNull)
}
The first test for Some(1.0) works fine
The second one for None throws:
Error:(40, 12) could not find implicit value for parameter writer: JsonWriter[None.type]
None.toJson should be(JsNull)
If you want to try the code my JsonType definitions for this example are:
sealed trait Json
final case class JsObject(get: Map[String, Json]) extends Json
final case class JsString(get: String) extends Json
final case class JsNumber(get: Double) extends Json
case object JsNull extends Json
None, if you dont say anything else, is a Option[Nothing], so OptionWriter[Nothing] needs a JsonWriter[Nothing]. If you try Json.toJson(Some(1)) is the same, there is no JsonWriter[Int].
On the other hand, Json.toJson(None:Option[String]) works, because OptionWriter[String] can get a JsonWriter[String].
I think it has to do with the fact that
case object None extends Option[Nothing] { ... }
if you do one of the following, it will work
toJson(Option.empty[Double])
toJson(None : Option[Double])
Note that the second one uses type ascription to put a face, so to speak, on the Nothing (which is a subtype of everything)
I have a trait for which I wanted to write Typeclasses for. This trait actually is a contract to do JSON to Case class conversion and vice versa. The definition of the trait is as below:
trait Converter[T] {
def convertFromJson(msg: String): Either[ConverterError, T]
def convertToJson(msg: T): String
}
Now, for one of the case classes that I have, I have defined the implementation like this:
object Converter extends DefaultJsonProtocol {
implicit object DefaultMessageConversions extends Converter[DefaultMessage] {
implicit val json = jsonFormat(DefaultMessage, "timestamp")
def convertFromJson(msg: String): Either[ConverterError, DefaultMessage] = {
try {
Right(msg.parseJson.convertTo[DefaultMessage])
}
catch {
case _: Exception => Left(ConverterError("Shit happens"))
}
}
def convertToJson(msg: DefaultMessage) = {
implicit val writes = Json.writes[DefaultMessage]
Json.toJson(msg).toString
}
}
def apply[T: Converter] : Converter[T] = implicitly
}
But I run into some compiler errors when I tried to build my project. I'm not sure what I did wrong?
[error] /Users/joesan/ingestion/src/main/scala/com/my/project/common/JsonMessageConversion.scala:28: could not find implicit value for evidence parameter of type com.my.project.common.JsonMessageConversion.Converter.JF[org.joda.time.DateTime]
[error] implicit val json = jsonFormat(DefaultMessage, "timestamp")
Here is how my case class look like:
case class DefaultMessage(timestamp: DateTime) extends KafkaMessage {
def this() = this(DateTime.now(DateTimeZone.UTC))
}
Your DefaultMessage uses org.joda.time.DateTime and spray-json does not know how to serialize/deserialize it out of the box.
Therefore you need to define a RootJsonFormat[DateTime] and bring it in implicit scope.
Here is an example implementation.
Let's say we have the following traits:
trait MyValue
object MyValue {
case class MyBoolean(record: Boolean) extends MyValue
case class MyLong(record: Long) extends MyValue
}
trait MyValueExtractor[T] {
def apply(record: T): Option[MyValue]
}
trait MyThing[T] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
}
What I want is something like this but without the second type parameter.
Note: I can't actually update the MyThing trait; I'm just using this as an illustration of the intended functionality.
trait MyThing[T, U] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
def myRelatedValue(record: T): Option[U]
}
I'm wondering if I could use the type class pattern to help solve this (i.e., import some rich class that implicitly gives me a myRelatedValue method)?
Here's the rub. Every time T (above) is MyValue.MyBoolean, U must be a String. Every time T is MyValue.MyLong, U must be a Double. In other words, there's a sort of underlying mapping between T and U.
Is there a good way to do this using type class?
Sure. You just need to define some Mapping typeclass with implementations for your desired pairs of types. Then MyThing can have a method that takes an implicit typeclass instance and simply invokes its method.
Here's the code (I removed the unneeded details)
// types
case class MyBoolean(record: Boolean)
case class MyLong(record: Long)
// trait which uses the Mapping typeclass
trait MyThing[T] {
def myRelatedValue[U](record: T)(implicit ev: Mapping[T, U]): Option[U] = ev.relatedValue(record)
}
// typeclass itself
trait Mapping[T, U] {
def relatedValue(record: T): Option[U]
}
object Mapping {
implicit val boolStringMapping = new Mapping[MyBoolean, String] {
def relatedValue(record: MyBoolean) = Some(record.record.toString)
}
implicit val longDoubleMapping = new Mapping[MyLong, Double] {
def relatedValue(record: MyLong) = Some(record.record)
}
}
// usage
val myBoolThing = new MyThing[MyBoolean] {}
val myLongThing = new MyThing[MyLong] {}
val myStringThing = new MyThing[String] {}
myBoolThing.myRelatedValue(MyBoolean(true)) // Some(true)
myLongThing.myRelatedValue(MyLong(42L)) // Some(42.0)
myStringThing.myRelatedValue("someString") // error: could not find implicit value
Note that e.g. myBoolThing.myRelatedValue(MyBoolean(true)) will yield a type Option[U]. However, since myRelatedValue is parameterized, you can help the compiler and invoke it as myBoolThing.myRelatedValue[String](MyBoolean(true)), in which case you will obtain an Option[String]. If you try something other than String for MyBoolean, you will get an error.
Just when I thought I understood the basics of Scala's type system... :/
I'm trying to implement a class that reads the contents of a file and outputs a set of records. A record might be a single line, but it could also be a block of bytes, or anything. So what I'm after is a structure that allows the type of Reader to imply the type of the Record, which in turn will imply the correct Parser to use.
This structure works as long as MainApp.records(f) only returns one type of Reader. As soon as it can return more, I get this error:
could not find implicit value for parameter parser
I think the problem lies with the typed trait definitions at the top, but I cannot figure out how to fix the issue...
// Core traits
trait Record[T]
trait Reader[T] extends Iterable[Record[T]]
trait Parser[T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
trait TypeA
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
trait TypeB
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
// The "app"
object MainApp {
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File) = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
First off you must import the implicit object in order to use them:
import TypeA._
import TypeB._
That's not enough though. It seems like you're trying to apply implicits dynamically. That's not possible; they have to be found compile time.
If you import the objects as above and change the records so that the compiler finds the correct generic it will run fine:
def records(f: File) = new FileReader[TypeA](f)
But then it may not be what you were looking for ;)
The problem is that the return type of your records method is basically FileReader[_] (since it can return either FileReader[TypeA] or FileReader[TypeB]), and you don't provide an implicit argument of type Parser[Any]. If you remove the if-expression the return type is inferred to FileReader[TypeA], which works fine. I'm not sure what you're trying to do, but obviously the compiler can't select implicit argument based upon a type that is only known at runtime.
1) Using type with implicit inside as type parameter - doesn't bind this implicit to the host type, to do this change objects to the traits and mix them instead of generalizing (type-parametrizing):
def records(f: File) = {
if(true)
new FileReader(f) with TypeA
else
new FileReader(f) with TypeB
}
2) The parser should be in scope of function that calls parse. So you may try smthg like that:
def process(f: File) = {
val reader = records(f);
import reader._
reader foreach { r => parse(r) }
}
PlanB) Simpler alternative is to define type-parameter specific implicit methods inside the AppMain (or some trait mixed in), but it will work only if TypeA/TypeB is known on compile time, so records method can return concrete type:
implicit class TypeAParser(r: Record[TypeA]) {
def parse: Option[Int] = ???
}
implicit class TypeBParser(r: Record[TypeB]) {
def parse: Option[Int] = ???
}
def process[T <: TypeAorB](f: File) =
records[T](f).foreach(_.parse)
def recordsA[T <: TypeAorB](f: File) = new FileReader[T](f)
Here is, I think, the full set of modifications you need to do to get where I think you want to go.
import scala.io.Source
import java.io.File
import reflect.runtime.universe._
// Core traits
trait Record[+T]
trait Reader[+T] extends Iterable[Record[T]]
trait Parser[-T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations [unmodified]
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
sealed trait Alternatives
case class TypeA() extends Alternatives
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
case class TypeB() extends Alternatives
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
class ParseAlternator(parserA: Parser[TypeA], parserB: Parser[TypeB]) extends Parser[Alternatives] {
def parse(r: Record[Alternatives]): Option[Int] = r match {
case x: Record[TypeA #unchecked] if typeOf[Alternatives] =:= typeOf[TypeA] => parserA.parse(x)
case x: Record[TypeB #unchecked] if typeOf[Alternatives] =:= typeOf[TypeB] => parserB.parse(x)
}
}
object ParseAlternator {
implicit def parseAlternator(implicit parserA: Parser[TypeA], parserB: Parser[TypeB]): Parser[Alternatives] = new ParseAlternator(parserA, parserB)
}
// The "app"
object MainApp {
import ParseAlternator._
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File): Reader[Alternatives] = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
The gist of it is: all of this would be completely classsical if only your parse instance did not have to pattern-match on a generic type but dealt directly with an Alternative instead.
It's this limitation (inherited from the JVM) that scala can't properly pattern-match on an object of a parametric type that requires the reflection & typeOf usage. Without it, you would just have type alternatives for your content (TypeA, TypeB), which you would add to a sealed trait, and which you would dispatch on, in an implicit that produces a Parser for their supertype.
Of course this isn't the only solution, it's just what I think is the meeting point of what's closest to what you're trying to do, with what's most idiomatic.