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
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 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").
I'm running into issues with the following code (heavily simplified). Looks like the problem may be with the way I use abstract type members. I would appreciate someone pointing out and explaining what I'm doing wrong here. Compiler error at the bottom.
I'm using Scala version 2.12.
trait DataType {
type A
def convert(value: String): A
def convertToString(value: A): String
}
case object IntType extends DataType {
type A = Int
def convert(value: String): A = value.toInt
def convertToString(value: A): String = value.toString
}
trait Codec[T <: DataType] {
val dtype: T
def encode(data: Array[String]): Array[T#A]
def decode(data: Array[T#A]): Array[String]
}
class CodecImp[T <: DataType](val dtype: T)(implicit tag: ClassTag[T#A]) extends Codec[T] {
def encode(data: Array[String]): Array[T#A] = {
Array[T#A](dtype.convert(data(0)))
}
def decode(data: Array[T#A]): Array[String] = {
Array[String](dtype.convertToString(data(0)))
}
}
val cod = new CodecImp(IntType)
val encoded = cod.encode(Array("1", "2", "3")) // expecting: Array[IntType.A]
val decoded = cod.decode(encoded) // expecting: Array[String]
Compiler error.
Error:(30, 50) type mismatch;
found : T#A
required: CodecImp.this.dtype.A
Array[String](dtype.convertToString(data(0)))
^
I found What does the # operator mean in Scala? explained the '#' operator pretty well.
Each instance of DataType has it's own path dependent type A.
The difference between: T#A meaning A is a nested class of any T and dtype.A meaning the A class of dtype
You could change the Codec trait method signatures to something like:
def encode(data: Array[String]): Array[dtype.A]
def decode(data: Array[dtype.A]): Array[String]
But type parameters might be a better way to express the relation.
This is caused by dtype.convertToString(data(0)) this method is wanting a variable with type: dtype#A, this type is decided by the variable dtype, but data type is Array[T#A], so this caused type mismatch, it's can't resolve in Scala, Since we can't state our method, like: def decode(data: Array[dType.A])... that's compiler expected.
And You can solve this by generics type agains type alias, like:
trait DataType[T] {
def convert(value: String): T
def convertToString(value: T): String
}
case object IntType extends DataType[Int] {
def convert(value: String): Int = value.toInt
def convertToString(value: Int): String = value.toString
}
class CodecImp[B](val dtype: DataType[B])(implicit tag: ClassTag[B]) {
def encode(data: Array[String]): Array[B] = {
Array[B](dtype.convert(data(0)))
}
def decode(data: Array[B]): Array[String] = {
Array[String](dtype.convertToString(data(0)))
}
}
The thing is that each instance of DataType can have A defined as anything whatsoever. Consider this:
class Foo extends DataType {
type A = Int
def convertToString(i: Int) = i.toString
def convert(s: String) = s.toInt
}
class Bar extends DataType {
type A = String
def convertToString(s: String) = s
def convert(s: String) = s
}
Now, if I do val codecs = Seq(CodecImpl(new Foo, new Bar)
and if that compiles, then what type should the argument be to codecs.map(_.decode(whateverMakesSenseHere))?
This doesn't work ... You can't use T#A like this, because it is abstract.
I get a feeling, you'd be better off modeling what you need to model with type parameters rather than path dependent types. This just doesn't really seem like a use case for the latter.
Suppose I have a set of converters to String, as a Type class:
import scala.reflect.runtime.universe._
abstract class ToStringConverter[T] {
def convert(value: T): String
}
implicit object IntToStringConverter extends ToStringConverter[Int] {
def convert(value: Int) = value.toString
}
implicit object DoubleStringConverter extends ToStringConverter[Double] {
def convert(value: Double) = value.toString
}
and a convert method that uses the type information to pick right converter:
def convert[T](v: T)(implicit ev: ToStringConverter[T]): String = ev.convert(v)
This works fine If I have the concrete type in advance, for example:
scala> convert[Double](12.2)
res0: String = 12.2
scala> convert[Int](12)
res1: String = 12
Is it possible to use the convert method above with a runtime type, for example, with a type 't' below?
scala> val t = typeOf[Double]
t: reflect.runtime.universe.Type = Double
If you want to do the resolution runtime, reflection is needed, as implicits are resolved compile time. A code like this should do the job:
import scala.reflect.runtime.universe._
abstract class ToStringConverterAny {
def convertAny(value: Any): String
}
abstract class ToStringConverter[T] extends ToStringConverterAny {
def convertAny(value: Any): String = convert(value.asInstanceOf[T])
def convert(value: T): String
}
implicit object IntToStringConverter extends ToStringConverter[Int] {
def convert(value: Int) = value.toString
}
implicit object DoubleStringConverter extends ToStringConverter[Double] {
def convert(value: Double) = value.toString
}
val converters: Map[Type, ToStringConverterAny] = Map(
typeOf[Int] -> IntToStringConverter,
typeOf[Double] -> DoubleStringConverter
)
def convert(t: Type, v: Any) = {
converters(t).convertAny(v)
}
def convert[T](v: T)(implicit ev: ToStringConverter[T]): String = ev.convert(v)
convert[Double](12.2)
convert[Int](12)
val t = typeOf[Double]
val v: Any = 1.23
convert(t, v)
If you want to building converters map automatically, you could also use reflection for this, but enumerating derived classes requires surprisingly non-trivial code (including class loaders - which is understandable when you think about it).
If you can make the ToStringConverterAny sealed, enumerating over its subclasses in a macro should be a bit easier.
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?