Scala implicit converters - scala

I'm trying to figure out how to create an implicit converter for a particular type. In my case, that would be a Set[IdParam[T]] to a Set[IdTag[T]], where
import shapeless.tag.##
case class IdParam[T](value: IdTag[T])
type IdTag[T] = Id ## T
class Id(val value: Long)
object Id {
def tag[T](l: Long) = shapeless.tag[T][Id](new Id(l))
def value[T](l: Long) = Id.tag[T](l)
}
I wrote a simple
implicit def toIds[T](idParams: Set[IdParam[T]]): Set[IdTag[T]] = idParams.map(_.value)
which works OK. But my main question now is if there is a way to create an implicit converter which would cover both Seq and Set. Going up the hierarchy, that would mean an Iterable.
Tried the following
implicit def toIds[I <: Iterable[IdParam[T]], T](idParams: I): Iterable[IdTag[T]] = idParams.map(_.value)
The compiler refuses to accept this converter where it's needed.
[error] found : Set[tags.IdParam[tags.ProfileIdTag]]
[error] required: Set[tags.ProfileId]
[error] (which expands to) Set[model.Id with shapeless.tag.Tagged[tags.ProfileIdTag]]
EDIT
That implicit converter should be used in the Controller endpoint.
GET /campaigns #dummy.Campaigns.all(profileId: Set[IdParam[ProfileIdTag]] ?= Set.empty)
I have a QueryStringBindable which converts the query param retrieved as a string into a Set[IdParam[A]]
object IdQueryStringBinder {
implicit def idParamQueryStringBinder[A](implicit stringBinder: QueryStringBindable[String]) =
new QueryStringBindable[IdParam[A]] {
def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, IdParam[A]]] =
for {
idEither <- stringBinder.bind(key, params)
} yield {
idEither match {
case Right(idString) =>
IdBinder.parseIdString(idString)
case Left(_) => Left("Unable to bind an Id")
}
}
override def unbind(key: String, id: IdParam[A]): String =
stringBinder.unbind(key, id.value.toString)
}
implicit def idsParamQueryStringBinder[A](implicit stringBinder: QueryStringBindable[String]) =
new QueryStringBindable[Set[IdParam[A]]] {
override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Set[IdParam[A]]]] = {
stringBinder.bind(key, params).map(_.right.flatMap(parse))
}
private def parse(idsString: String): Either[String, Set[IdParam[A]]] = {
val (wrongIds, correctIds) = idsString.split(",")
.map(id => IdBinder.parseIdString[A](id))
.partition(_.isLeft)
if (wrongIds.nonEmpty) {
Left(s"Could not bind the following Ids: ${wrongIds.map(_.left.get).mkString(",")}")
} else {
Right(correctIds.map(_.right.get).toSet)
}
}
override def unbind(key: String, ids: Set[IdParam[A]]): String =
stringBinder.unbind(key, ids.map(_.value.toString).mkString(","))
}
}
My Controller is
class Campaigns extends play.api.mvc.Controller {
def allByProfileId(profile: ProfileId) = ...
def all(profileIds: Set[ProfileId]) = ...
}
The first endpoint works, which uses the defined idParamQueryStringBinder with the following implicit converter
implicit def toId[T](idParam: IdParam[T]): IdTag[T] = idParam.value

You must have done some coding mistake because it works perfectly fine for me:
https://gist.github.com/kpbochenek/ce2a783d59a7bd28585e4cbf7a64cad4
next time paste full code that causes the issue not only small parts.

Related

Generic method overloading via implicit class

When I try to create an extension for the class via implicit class and overload existing generic method, it fails with compilation error:
error: overloaded method value getAs with alternatives:
(fieldName: String)String <and>
(i: Int)String
cannot be applied to (FooField.type)
r.getAs[String](FooField)
While overloading normal (non-generic) method via implicit works fine. Tried on Scala 2.12.10. Link to scastie. What am I missing? The code:
trait Row {
// Two overloads for `getAs[T]`
def getAs[T](i: Int): T
def getAs[T](fieldName: String): T
// Two overloads for `get`
def get(i: Int): String
def get(fieldName: String): String
}
trait Field {
def columnName: String
def columnDescription: String
}
case object FooField extends Field {
def columnName: String = "Foo"
def columnDescription: String = "Foo desc"
}
object Implicits {
implicit class RowEx(val r: Row) extends AnyVal {
def getAs[T](field: Field): T = r.getAs[T](field.columnName)
def get(field: Field): String = {
println(s"RowEx.get: field")
r.get(field.columnName)
}
}
}
object Main {
import Implicits._
// Create some instance of `Row`
val r = new Row {
def getAs[T](i: Int): T = i.toString.asInstanceOf[T]
def getAs[T](fieldName: String): T = fieldName.toString.asInstanceOf[T]
def get(i: Int): String = i.toString
def get(fieldName: String): String = fieldName
}
def main(args: Array[String]): Unit = {
// Call extension method => `RowEx.get`
println(r.get(FooField))
// Call extension method => `RowEx.get`
// Won't compile with compilation error:
/*
overloaded method value getAs with alternatives:
(fieldName: String)String
(i: Int)String
cannot be applied to (FooField.type)
*/
println(r.getAs[String](FooField))
}
}
Opened a bug: https://github.com/scala/bug/issues/11810

Play how to implement an implicit Writes or Format for a case class including an Enumeration

Following this answer shows how to bind an Enumeration to a form using a case class.
However in Play 2.7.3 this code fails with:
No Json serializer found for type jura.SearchRequest. Try to implement an implicit Writes or Format for this type.
When I implement the formatter:
object SearchRequest {
implicit val searchRequestFormat: OFormat[SearchRequest] = Json.format[SearchRequest]
}
I get
No instance of play.api.libs.json.Format is available for
scala.Enumeration.Value in the implicit scope
Should I be trying to write a formatter for the system scala.Enumeration type?
Or is there another way to implement a formatter when Enumerations are involved?
Test case here.
I use for any Enumeration this Library: enumeratum
With Dotty there will be great Enumerations, but until then I think moving to enumeratum is the best way handling Enumerations in Scala. See also Dotty - Enumerations.
As a bonus there is a play-json Extension, see Play JSON Extension.
With this your code would look like this:
import enumeratum.{ PlayJsonEnum, Enum, EnumEntry }
sealed trait SearchRequest extends EnumEntry
object SearchRequest extends Enum[SearchRequest] with PlayJsonEnum[SearchRequest] {
val values = findValues
case object SuperRequest extends SearchRequest
case object SimpleRequest extends SearchRequest
..
}
In essence PlayJsonEnum[SearchRequest] does all the work.
To write the enum as a string as cchantep says you can use Writes.enumNameWrites, we specifically use to read and write the ID. Therefore we have an EnumFormat in the package global for enums:
package object enums {
implicit def reads[E <: Enumeration](enum: E): Reads[E#Value] = new Reads[E#Value] {
def reads(json: JsValue): JsResult[E#Value] = json match {
case JsNumber(s) =>
try {
JsSuccess(enum.apply(s.toInt))
} catch {
case _: NoSuchElementException => JsError(s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$s'")
}
case _ => JsError("Number value expected")
}
}
implicit def writes[E <: Enumeration]: Writes[E#Value] = new Writes[E#Value] {
def writes(v: E#Value): JsValue = JsNumber(v.id)
}
implicit def formatID[E <: Enumeration](enum: E): Format[E#Value] =
Format(reads(enum), writes)
def readsString[E <: Enumeration](enum: E): Reads[E#Value] = new Reads[E#Value] {
def reads(json: JsValue): JsResult[E#Value] = json match {
case JsString(s) => {
try {
JsSuccess(enum.withName(s))
} catch {
case _: NoSuchElementException => JsError(s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$s'")
}
}
case _ => JsError("String value expected")
}
}
implicit def writesString[E <: Enumeration]: Writes[E#Value] = new Writes[E#Value] {
def writes(v: E#Value): JsValue = JsString(v.toString)
}
implicit def formatString[E <: Enumeration](enum: E): Format[E#Value] =
Format(readsString(enum), writesString)
}
And used:
object SearchRequest extends Enumeration(1) {
type SearchRequest = Value
val ONE /*1*/ , TWO /*2*/ , ETC /*n*/ = Value
implicit val searchRequestFormat: Format[SearchRequest] = formatID(SearchRequest)
}
Add the following to your Enumeration Object
implicit object MatchFilterTypeFormatter extends Formatter[MatchFilterType.Value] {
override val format = Some(("format.enum", Nil))
override def bind(key: String, data: Map[String, String]) = {
try {
Right(MatchFilterType.withName(data.get(key).head))
} catch {
case e:NoSuchElementException => Left(Seq(play.api.data.FormError(key, "Invalid MatchFilterType Enumeration")))
}
}
override def unbind(key: String, value: MatchFilterType.Value) = {
Map(key -> value.toString)
}
}
implicit val matchFilterTypeFormat = new Format[MatchFilterType.MatchFilterType] {
def reads(json: JsValue) = JsSuccess(MatchFilterType.withName(json.as[String]))
def writes(myEnum: MatchFilterType.MatchFilterType) = JsString(myEnum.toString)
}
And then the Formatter/Controller given in the question will work.
A working test case is here.

Default implicit object/definition for a typeclass

I have a requirement to intercept toString of DateTime, LocalDate and Option in the runTime.
#implicitNotFound("No member of type class ReportString in scope for ${T}")
trait ReportStringTransformer[T] {
def toReportString(e: T): String
}
object ReportStringTransformer {
implicit object ReportStringTransformerDateTime
extends ReportStringTransformer[DateTime] {
override def toReportString(e: DateTime): String =
ISODateTimeFormat.dateTime().print(e)
}
implicit object ReportStringTransformerDate
extends ReportStringTransformer[LocalDate] {
override def toReportString(e: LocalDate): String =
ISODateTimeFormat.date().print(e)
}
implicit def ReportStringTransformerOpt[T]: ReportStringTransformer[Option[T]] =
new ReportStringTransformer[Option[T]] {
override def toReportString(e: Option[T]): String = e match {
case Some(obj) => ReportStringTransform.transform(obj)
case None => ""
}
}
}
object ReportStringTransform {
def transform[T](obj: T)(implicit t: ReportStringTransformer[T]): String =
t.toReportString(obj)
}
I could add a default transformer for Any class at the end which could only
be picked up after these, but is there any other cleaner way to do?
Your implementation could be simplified as follows:
#implicitNotFound("No member of type class Show in scope for ${T}")
case class Show[T](f: T => String) extends AnyVal
object Show {
implicit val showDateTime = Show[DateTime](ISODateTimeFormat.dateTime() print _)
implicit val showDate = Show[LocalDate](ISODateTimeFormat.date() print _)
implicit def showOpt[T](implicit s: Show[T]) = Show[Option[T]](_.fold("")(s.f))
}
To have a fallback for anything that is not a DateTime, a LocalDate or an Option of either the these, you could the following trait as a paraent of object Show
trait LowPriorityShow {
implicit def showAnything[T] = Show[T](_.toString)
}
object Show extends LowPriorityShow { ... }
Probably late to the party, but your issue on your gist here, is that the implicit resolution picks up the type Some[String] for the second case you put. other than that, the solution given by OlivierBlanvillain is great and it should solve your needs.
You can verify that this is your case by casting to Option[String]
println(MyShow.show(Some("abc"): Option[String])) // this will work as you expected
playing with variance/covariance you can make it work as expected link.
import org.joda.time.{DateTime, DateTimeZone}
import org.joda.time.format.ISODateTimeFormat
trait Show[-T] {
def show: T => String
}
trait LowPriorityShow {
implicit def showAnything[T] = new Show[T] {
def show: T => String = _.toString
}
}
object Show extends LowPriorityShow {
implicit val showString: Show[String] = new Show[String] {
def show: String => String = identity
}
implicit val showDateTime: Show[DateTime] =
new Show[DateTime] {
def show: DateTime => String =
ISODateTimeFormat.dateTime().withZone(DateTimeZone.forID("America/New_York")).print
}
implicit def showOpt[T : Show]: Show[Option[T]] =
new Show[Option[T]] {
def show: Option[T] => String = _.fold("")(implicitly[Show[T]].show)
}
def show[T : Show](v: T): String = implicitly[Show[T]].show(v)
}
println(Show.show(DateTime.now()))
println(Show.show(Some("abc")))
println(Show.show(12))

How do I provide the proper type information to get my generic filter function working for slick

I'm trying to implement some generic filters in a base class for my slick tables.
What I'm trying to accomplish is the ability to translate url query strings into database filters. I've come up with a working solution, but the code isn't very DRY.
This is my modified code.
package db.utils.repos
import common.utils.request.filters.{StringMatchesFilter, RequestFilter}
import scala.slick.lifted.{Column => LiftedColumn}
import db.environments.slick.interfaces.SlickEnv
import org.joda.time.DateTime
import scala.slick.profile.RelationalProfile
trait SlickPageable {
val slickEnv: SlickEnv
import slickEnv.profile.simple._
trait FieldMapped {
def longField(f: String): Option[LiftedColumn[Long]]
def optLongField(f: String): Option[LiftedColumn[Option[Long]]]
def stringField(f: String): Option[LiftedColumn[String]]
def optStringField(f: String): Option[LiftedColumn[Option[String]]]
}
trait FieldSet{
def stringField(table: FieldMapped, f: String): Option[LiftedColumn[Any]]
}
object FSet extends FieldSet {
override def stringField(table: FieldMapped, f: String): Option[LiftedColumn[String]] = {
table.stringField(f)
}
}
object OptFSet extends FieldSet {
override def stringField(table: FieldMapped, f: String): Option[LiftedColumn[Option[String]]] = {
table.optStringField(f)
}
}
def filter(table: FieldMapped, f: RequestFilter) = {
queryFilter(table, f, QSet)
}
def optFilter(table: FieldMapped, f: RequestFilter) ={
queryFilter(table, f, OptQSet)
}
def queryFilter[T, R](table: FieldMapped, f: RequestFilter, querySet: QuerySet[T, R]) = {
def checkField[A](field: Option[A], op: (A, String) => R) = {
field match {
case Some(fi) =>
op(fi, f.value)
case None =>
throw new IllegalArgumentException(s"could not retrieve field ${f.field}")
}
}
f match {
case sf: StringMatchesFilter => {
checkField(querySet.fSet.stringField(table, f.field), querySet.sEq)
}
case _ => throw new IllegalArgumentException("No such filter")
}
}
trait QuerySet[T, R] {
val fSet: FieldSet
def sEq(col: T, value: String): R
}
object QSet extends QuerySet[LiftedColumn[String], LiftedColumn[Boolean]] {
override val fSet = FSet
override def sEq(col: LiftedColumn[String], value: String): LiftedColumn[Boolean] = {
col === value
}
}
object OptQSet extends QuerySet[LiftedColumn[Option[String]], LiftedColumn[Option[Boolean]]] {
override val fSet = OptFSet
override def sEq(col: LiftedColumn[Option[String]], value: String): LiftedColumn[Option[Boolean]] = {
col === value
}
}
}
And the error message
type mismatch;
[error] found : (T, String) => R
[error] required: (scala.slick.lifted.Column[_], String) => R
[error] checkField(fieldSet.stringField(table, f.field), querySet.sEq)
If I remove the FieldSet from the queryFilter parameter list I can get it working. But then I need to separate functions: one to call the field mapping methods, and the other to call the optional field mapping methods. It works though, with the type inferencing and everything working properly.
So how can I get this working with FieldSet? From what I can tell it seems there isn't enough type information at compile time. But I don't know how to provide it.

Implicit resolution and companion objects for case classes

I'm trying to add an implicit value to (what I believe is) the companion object of a case class, but this implicit value is not found.
I'm trying to achieve something like the following:
package mypackage
object Main {
def main(args: Array[String]): Unit = {
val caseClassInstance = MyCaseClass("string")
val out: DataOutput = ...
serialize(out, caseClassInstance)
// the above line makes the compiler complain that there is no
// Serializer[MyCaseClass] in scope
}
def serialize[T : Serializer](out: DataOutput, t: T): Unit = {
...
}
}
object MyCaseClass {
// implicits aren't found here
implicit val serializer: Serializer[MyCaseClase] = ...
}
case class MyCaseClass(s: String) {
// some other methods
}
I've explicitly added the package here to show that both the MyCaseClass case class and object should be in scope. I know that the object is actually being constructed because I can get this to compile if I add
implicit val serializer = MyCaseClass.serializer
to main (though notably not if I add import MyCaseClass.serializer).
I'm concerned that the MyCaseClass object is not actually a companion of the case class, because if I explicitly define apply and unapply on the object and then attempt to call MyCaseClass.apply("string") in main, the compiler gives the following error:
ambiguous reference to overloaded definition,
both method apply in object MyCaseClass of type (s: String)mypackage.MyCaseClass
and method apply in object MyCaseClass of type (s: String)mypackage.MyCaseClass
match argument types (String)
val a = InputRecord.apply("string")
^
If it's not possible to take this approach, is there a way to use type classes with case classes without creating an implicit value every time it must be brought into scope?
EDIT: I'm using scala 2.10.3.
EDIT 2: Here's the example fleshed out:
package mypackage
import java.io.{DataOutput, DataOutputStream}
object Main {
def main(args: Array[String]): Unit = {
val caseClassInstance = MyCaseClass("string")
val out: DataOutput = new DataOutputStream(System.out)
serialize(out, caseClassInstance)
// the above line makes the compiler complain that there is no
// Serializer[MyCaseClass] in scope
}
def serialize[T : Serializer](out: DataOutput, t: T): Unit = {
implicitly[Serializer[T]].write(out, t)
}
}
object MyCaseClass {
// implicits aren't found here
implicit val serializer: Serializer[MyCaseClass] = new Serializer[MyCaseClass] {
override def write(out: DataOutput, t: MyCaseClass): Unit = {
out.writeUTF(t.s)
}
}
}
case class MyCaseClass(s: String) {
// some other methods
}
trait Serializer[T] {
def write(out: DataOutput, t: T): Unit
}
This actually compiles, though. I am getting this issue when using Scoobi's WireFormat[T] instead of Serializer, but can't provide a concise, runnable example due to complexity and the Scoobi dependency. I will try to create a more relevant example, but it seems as though the issue is not as general as I thought.
It turns out that the type class instances actually need to be implicit values, rather than objects. The MyCaseClass object above works because its serializer is assigned to an implicit value. However, this implementation
object MyCaseClass {
implicit object MyCaseClassSerializer extends Serializer[MyCaseClass] {
override def write(out: DataOutput, t: MyCaseClass): Unit = {
out.writeUTF(t.s)
}
}
}
fails with the error
Main.scala:9: error: could not find implicit value for evidence parameter of type mypackage.Serializer[mypackage.MyCaseClass]
serialize(out, caseClassInstance)
^
In my real code, I was using an auxiliary function to generate the Serializer[T] (see https://github.com/NICTA/scoobi/blob/24f48008b193f4e87b9ec04d5c8736ce0725d006/src/main/scala/com/nicta/scoobi/core/WireFormat.scala#L137). Despite the function having its own explicit return type, the type of the assigned value was not being inferred correctly by the compiler.
Below is the full example from the question with such a Serializer-generator.
package mypackage
import java.io.{DataOutput, DataOutputStream}
object Main {
import Serializer._
def main(args: Array[String]): Unit = {
val caseClassInstance = MyCaseClass("string")
val out: DataOutput = new DataOutputStream(System.out)
serialize(out, caseClassInstance)
}
def serialize[T : Serializer](out: DataOutput, t: T): Unit = {
implicitly[Serializer[T]].write(out, t)
}
}
object MyCaseClass {
import Serializer._
// does not compile without Serializer[MyCaseClass] type annotation
implicit val serializer: Serializer[MyCaseClass] =
mkCaseSerializer(MyCaseClass.apply _, MyCaseClass.unapply _)
}
case class MyCaseClass(s: String)
trait Serializer[T] {
def write(out: DataOutput, t: T): Unit
}
object Serializer {
// does not compile without Serializer[String] type annotation
implicit val stringSerializer: Serializer[String] = new Serializer[String] {
override def write(out: DataOutput, s: String): Unit = {
out.writeUTF(s)
}
}
class CaseClassSerializer[T, A : Serializer](
apply: A => T, unapply: T => Option[A]) extends Serializer[T] {
override def write(out: DataOutput, t: T): Unit = {
implicitly[Serializer[A]].write(out, unapply(t).get)
}
}
def mkCaseSerializer[T, A : Serializer]
(apply: A => T, unapply: T => Option[A]): Serializer[T] =
new CaseClassSerializer(apply, unapply)
}
This related, simple code below prints 1.
object A{
implicit def A2Int(a:A)=a.i1
}
case class A(i1:Int,i2:Int)
object Run extends App{
val a=A(1,2)
val i:Int=a
println(i)
}