I trying to understand imap functor and have the following code:
trait Codec[A] {
def encode(value: A): String
def decode(value: String): A
def imap[B](dec: A => B, enc: B => A): Codec[B] = {
val self = this
new Codec[B] {
override def encode(value: B): String =
self.encode(enc(value))
override def decode(value: String): B =
dec(self.decode(value))
}
}
}
object Codec {
implicit val stringCodec: Codec[String] =
new Codec[String] {
override def encode(value: String): String = value
override def decode(value: String): String = value
}
implicit val intCodec: Codec[Int] =
stringCodec.imap(_.toInt, _.toString)
implicit val booleanCodec: Codec[Boolean] =
stringCodec.imap(_.toBoolean, _.toString)
implicit val doubleCodec: Codec[Double] =
stringCodec.imap(_.toDouble, _.toString)
def encode[A](value: A)(implicit c: Codec[A]): String =
c.encode(value)
def decode[A](value: String)(implicit c: Codec[A]): A =
c.decode(value)
}
Converting for example from double to string it works:
def main(args: Array[String]): Unit = {
println(Codec.encode(34.343))
}
but from String to double:
def main(args: Array[String]): Unit = {
println(Codec.decode("34.343"))
}
I've got the error message:
Error:(44, 24) ambiguous implicit values:
both value stringCodec in object Codec of type => io.khinkali.Codec[String]
and value intCodec in object Codec of type => io.khinkali.Codec[Int]
match expected type io.khinkali.Codec[A]
println(Codec.decode("34.343"))
Error:(44, 24) could not find implicit value for parameter c: io.khinkali.Codec[A]
println(Codec.decode("34.343"))
Error:(44, 24) not enough arguments for method decode: (implicit c: io.khinkali.Codec[A])A.
Unspecified value parameter c.
println(Codec.decode("34.343"))
What do I forget?
You forgot to specify type for decode operation:
Codec.decode[Double]("34.343")
You have two implicit codecs in the scope: Codec[Int] and Codec[Double]. How does compiler know which one you want to use?
You have multiple options (implicits) for the decode function. If you specify the type parameter, then the compiler will be able to choose the right one: Codec.decode[Double]("34.343").
Related
Suppose I have a case class below
case class SomeCaseClass[M] private (
value: String
)
and in another file, I have the following trait and object.
trait SomeTrait[A] {
def get(oldId: String): A
:
}
object SomeObject {
private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
def get(oldId: String): A = id(oldId)
:
}
val aaa: SomeTrait[String] = init[String]()
val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
}
How should I modify the code so that restrict the init method only to being used with SomeCaseClass[_] type and not with any types like String as above?
Ideally with some modification to the code, the line val aaa: SomeTrait[String] = init[String]() should cause compilation error.
This is what I came up with:
case class SomeCaseClass[M] private (
value: String
)
trait SomeTrait[A] {
def get(oldId: String): A
}
private[this] def init[A <: SomeCaseClass[_]](): SomeTrait[A] = new SomeTrait[A] {
def get(oldId: String): A = ???
}
val aaa: SomeTrait[String] = init[String]() // Will fail
val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
It fails with
ScalaFiddle.scala:16: error: type arguments [String] do not conform to method init's type parameter bounds [A <: ScalaFiddle.this.SomeCaseClass[_$1] forSome { type _$1 }]
You can check this scalafiddle.
I do not know if this is the best approach, but init[A <: SomeCaseClass[_]] is adding a type bound to A, and forcing A to be a Subclass of SomeCaseClass. I would love to know if there is a better way though.
You can force a type parameter to be equal to some type B by using an implicit parameter:
def foo[A](implicit e: A =:= B): …
Also see this question.
To add some more value to this answer.
Following code shows how to use the implicit parameter e: A =:= String to convert an A to a String.
def bar(b: String): Unit = println(b)
def foo[A](a: A)(implicit e: A =:= String): Unit = {
bar(e(a))
}
foo("hi") //compiles
foo(5) //error: Cannot prove that scala.this.Int =:= String.
Answer to problem the OP has
This problem is much simpler: Make the method parametric only in the parameter A of SomeCaseClass[A], instead of using the whole type SomeCaseClass[A] as a type parameter:
private[this] def init[A](): SomeTrait[SomeCaseClass[A]] = new
SomeTrait[SomeCaseClass[A]] {
def get(oldId: String): SomeCaseClass[A] = ???
}
This is based on the answer above:
case class SomeCaseClass[M] private (
value: String
)
trait SomeTrait[A] {
def get(oldId: String): SomeCaseClass[A]
}
private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
def get(oldId: String): SomeCaseClass[A] = ???
}
val aaa: SomeTrait[String] = init[String]()
(https://scalafiddle.io/sf/KuXZc0h/3)
This doesn't allow other types than SomeCaseClass to be used with SomeTrait.
I have this type class:
sealed trait DbValueOps[T <: DbValue] {
type R
def apply(newContent: R): Option[T]
def fromString(newContent: String): Option[T]
def isValidContent(newContent: R): Boolean
}
with this type class instance:
package object DbOps {
implicit val dbStringOps: DbValueOps[DbString] = new DbValueOps[DbString] {
type R = String
def apply(newContent: String): Option[DbString] =
isValidContent(newContent) match {
case true => Some(new DbString(newContent))
case false => None
}
def fromString(newContent: String): Option[DbString] = this(newContent)
def isValidContent(newContent: String): Boolean = !newContent.isEmpty
}
}
But when trying to use the type class instance with something like dbStringOps.isValidContent(newContent) where newcontent is a string, i get a type mismatch:
found : newContent.type (with underlying type String)
required: database.DbOps.dbStringOps.R
I can get it to work by converting R from an abstract type member to a type parameter, but that is ugly since R is already determined when i'm writing the implementation of the type class.
add type annotations on that implicit val.
implicit val dbStringOps: DbValueOps[DbString] { type R = String } = ...
or
receive implicit parameter using this signature.
def f()(implicit db: DbValueOps[DbString] { type R = String }) = ...
can also write like this.
type AUX[A, T] = DbValueOps[A] { type R = T }
def f()(implicit db: AUX[DbString, String]) = ...
恵砂川 answer solves your problem perfectly, but unless you really want to center your design on DbValues, I will suggest to center your implicit on the wrapped value (String on this case), as you don't need to provide the unification of R with String.
trait DbValue[T]
case class DbString(s:String) extends DbValue[String]
sealed trait DbOps[R]{
type T <: DbValue[R]
def apply(newContent: R): Option[T]
def fromString(newContent: String): Option[T]
def isValidContent(newContent: R): Boolean
}
object DbOps {
val dbStringOps: DbOps[String] = new DbOps[String] {
type T = DbString
def apply(newContent: String): Option[DbString] =
isValidContent(newContent) match {
case true => Some(new DbString(newContent))
case false => None
}
def fromString(newContent: String): Option[DbString] = this(newContent)
def isValidContent(newContent: String): Boolean = !newContent.isEmpty
}
val newContent = "hello"
dbStringOps.isValidContent(newContent)
}
Adding a type parameter to DbValue can be a little more verbose but it prevents you to define things like DbValueOps[DbString] { type R = Int } that probably is NOT what are you looking for.
I have the following example with aux type, that does not compile:
trait Foo[A] {
type B
def value: B
}
object Foo {
type Aux[A0, B0] = Foo[A0] { type B = B0}
implicit def footInt = new Foo[Int] {
override type B = String
override def value = "Hey!"
}
implicit def fooString = new Foo[String] {
override type B = Boolean
override def value = true
}
}
import cats._
import cats.implicits._
object App {
def main(args: Array[String]): Unit = {
val x: Id[String] = "Hello"
println(foo(1))
println(foo("Hello"))
println(fooAux(1)(x))
}
def foo[T](t: T)(implicit f: Foo[T]): f.B = f.value
def fooAux[T, R] (t: T) (implicit f: Foo.Aux[T, R], id: Id[R]) : R = f.value
}
the compile complains:
Error:(15, 22) not enough arguments for method fooAux: (implicit f: com.sweetsoft.Foo.Aux[Int,R], implicit id: cats.Id[R])R.
Unspecified value parameter id.
println(fooAux(1)(x))
Error:(21, 55) parameter value id in method fooAux is never used
def fooAux[T, R] (t: T) (implicit f: Foo.Aux[T, R], id: Id[R]) : R = f.value
Error:(4, 23) Unused import
import cats.implicits._
What am I doing wrong?
It compiles with minor modifications:
trait Foo[A] {
type B
def value: B
}
object Foo {
type Aux[A0, B0] = Foo[A0] { type B = B0 }
implicit val footInt = new Foo[Int] {
override type B = String
override def value = "Hey!"
}
implicit def fooString = new Foo[String] {
override type B = Boolean
override def value = true
}
}
object App {
type Id[X] = X
def main(args: Array[String]): Unit = {
implicit val x: Id[String] = "Hello"
println(foo(1))
println(foo("Hello"))
println(fooAux(1))
}
def foo[T](t: T)(implicit f: Foo[T]): f.B = f.value
def fooAux[T, R] (t: T) (implicit f: Foo.Aux[T, R], id: Id[R]): R = id
}
The first error is pretty clear: the second argument list of fooAux needs two arguments, but you pass just a single x.
The second error message is also quite mundane: you don't ever use id.
The rest works almost as-is as soon as you provide an implicit String (I assume that you wanted to make x implicit).
I don't know how to solve a problem in scala. Maybe someone can help me!
I have a case class (Operation) with some type parameter, this class can be returned by a method that know nothing about the parameter types (example a parser from string/json/xml).
So I need a way to transform from ShadowedOperation to Operation in some way, because the need is to parse from some data a ShadowedOperation and from this extract the typed version (an Operation).
I've write a code that should express the problem, it's simplified and try to do something different, but if this can be solved I can solve also the real need.
Probably with shapeless there is a solution, but I need to find a solution without it.
object box {
trait Transform[A, B] {
def apply(in: A): B
}
object Transform {
def instance[A, B](f: A => B): Transform[A, B] = new Transform[A, B] {
override def apply(in: A): B = f(in)
}
}
implicit class TransformOps[T](w: T) {
def transform(implicit t: Transform[T, String]) = t(w)
}
trait ShadowedOperation {
type I
type O
def param: String
def otherParam: Int
def in: I
def out: O
}
object ShadowedOperation {
// How can i do this in a generic, typed and wonderful way ???
implicit def operationToString: Transform[ShadowedOperation, String] = ???
}
case class Operation[I0, O0](
param: String,
otherParam: Int,
in: I0,
out: O0
) extends ShadowedOperation {type I = I0; type O = O0}
object Operation {
implicit def operationToString[I, O](
implicit
iToString: Transform[I, String],
oToString: Transform[O, String]
): Transform[Operation[I, O], String] =
Transform.instance(in => s"${in.otherParam} - ${in.param} - ${iToString(in.in)} - ${oToString(in.out)}")
}
def fakeParseFromString(in: String): List[ShadowedOperation] = {
// this simulate a parsing (or read from db) from string to extract the case class
List(Operation("param", 0, "in!", "out!"), Operation("param", 0, "in!", 100))
}
}
object Main extends App {
import box._
implicit val intToString: Transform[Int, String] = Transform.instance(_.toString)
implicit val stringToString: Transform[String, String] = Transform.instance(_.toString)
val op = Operation("param", 0, "in!", "out!")
val shadowedOperationList = fakeParseFromString("imagine that this string contain a json")
val opString = op.transform
val shadowedOpString = shadowedOperationList.map(_.transform)
println(opString)
println(shadowedOpString)
}
Thanks in advance to all who can help!
I made several changes:
added covariance/contravariance to Transform[-A, +B]
introduced type ShadowedOperation.Aux[I0, O0]
fixed returning type of fakeParseFromString using Aux-type
lifted operationToString from companion object of case class to companion object of trait with corresponding changes
imported instance: import op._
The whole code:
object box {
trait Transform[-A, +B] {
def apply(in: A): B
}
object Transform {
def instance[A, B](f: A => B): Transform[A, B] = new Transform[A, B] {
override def apply(in: A): B = f(in)
}
}
implicit class TransformOps[T](w: T) {
def transform(implicit t: Transform[T, String]) = t(w)
}
trait ShadowedOperation {
type I
type O
def param: String
def otherParam: Int
def in: I
def out: O
implicit def operationToString(
implicit
iToString: Transform[I, String],
oToString: Transform[O, String]
): Transform[ShadowedOperation.Aux[I, O], String] =
Transform.instance(in => s"${in.otherParam} - ${in.param} - ${iToString(in.in)} - ${oToString(in.out)}")
}
object ShadowedOperation {
type Aux[I0, O0] = ShadowedOperation { type I = I0; type O = O0 }
}
case class Operation[I0, O0](
param: String,
otherParam: Int,
in: I0,
out: O0
) extends ShadowedOperation {type I = I0; type O = O0}
def fakeParseFromString[I, O](in: Operation[I, O]): ShadowedOperation.Aux[I, O] = in
}
def main(args: Array[String]): Unit = {
import box._
implicit val intToString: Transform[Int, String] = Transform.instance(_.toString)
implicit val stringToString: Transform[String, String] = Transform.instance(_.toString)
val op = Operation("param", 0, "in!", "out!")
val shadowedOperation = fakeParseFromString(op)
import op._
val opString = op.transform
val shadowedOpString = shadowedOperation.transform
println(opString)//0 - param - in! - out!
println(shadowedOpString)//0 - param - in! - out!
}
So shapeless isn't necessary here.
When you write just ShadowedOperation instead of ShadowedOperation.Aux[???, ???] you loose some information about types. You have to find a way to restore this information about I, O (some casting, specifying types explicitly, defining more implicits etc.). Otherwise implicits won't work.
For instance in your updated example you can write
def fakeParseFromString(in: String): List[ShadowedOperation.Aux[String, Any]] =
List(Operation("param", 0, "in!","out!"), Operation("param", 0, "in!", 100))
implicit val anyToString: Transform[Any, String] = Transform.instance(_.toString)
val shadowedOpString = shadowedOperationList.map(_.transform)
println(shadowedOpString)
// List(Operation(param,0,in!,out!), Operation(param,0,in!,100))
There is a good way to make the line 1.print works without defining an implicit intPrintable?
I would like to say to the compiler to use the stringPrintable implementation also for Int type without provide a new implicit Printable[Int], the idea is to say to compiler that Int can be viewed as String
Here the example:
trait Printable[T]{
def print(in: T): String
}
object Printable{
def apply[T](f: T => String): Printable[T] = new Printable[T] {
def print(in: T): String = f(in)
}
}
implicit class PrintableOps[T](v: T)(implicit printable: Printable[T]) {
def print: String = printable.print(v)
}
implicit val stringPrintable: Printable[String] = Printable((in: String) => s"print $in")
implicit def intToString(i: Int): String = i.toString
// doesn't works
1.print
// works
stringPrintable.print(1)
// works
intToString(1).print
Your code is requiring the additional implicit, because it is required as a constructor for your (implicit class) PrintableOps
You can simplify this by simply declaring implicit classes more directly:
trait Printable[T] {
def print: String
}
implicit class GenericPrintable[T](in: T) extends Printable[T] {
def print:String = in.toString
}
implicit class StringPrintable(in:String) extends Printable[String]{
override def print:String = s"print ${in}"
}
println( 1.print )
println( "1".print )
Your stringPrintable would require to use intToString to convert Int to String.
implicit val stringPrintable: Printable[String] = Printable((in: String) => s"print $in")
implicit def intToString(i: Int): String = i.toString
Printable.apply() requires an anonymous function and an anonymous function can't take an implicit value where intToString is implicit.
A better workaround is to statically define an implicit Printable[Int] or reform the Printable to GenericPrintable[T] with reference to #Jon Anderson
We need to provide an implicit conversion from Printer[String] => Printer[Int] given an implicit conversion from Int => String. We can put this on Printer companion object:
implicit def apply[T0, T1](implicit pin: Printable[T0], c: T1 => T0): Printable[T1] = new Printable[T1] {
def print(in: T1): String = pin.print(c(in))
}
Here the solution:
trait Printable[T]{
def print(in: T): String
}
object Printable{
def apply[T](f: T => String): Printable[T] = new Printable[T] {
def print(in: T): String = f(in)
}
implicit def apply[T0, T1](implicit pin: Printable[T0], c: T1 => T0): Printable[T1] = new Printable[T1] {
def print(in: T1): String = pin.print(c(in))
}
}
implicit class PrintableOps[T](v: T)(implicit printable: Printable[T]) {
def print: String = printable.print(v)
}
implicit val stringPrintable: Printable[String] = Printable((in: String) => s"print $in")
implicit def intToString(i: Int): String = i.toString
// now working
1.print
// works
stringPrintable.print(1)
// works
intToString(1).print
// don't compile, and it's ok, because no implicit conversion (A => String) is provided
case class A(in: String)
A("A").print
What do you think?