I am facing this weird problem related to scala implicit resolution
Here is the code snippet
import scala.collection.Factory
import scala.collection.immutable.Seq
sealed trait A
sealed trait B
case class BImpl() extends B
case class AImpl() extends A
object implicitsContainer {
type AB = (A, B)
implicit def toStringAnyTuples[C[X] <: Iterable[X], A <: AB]
(col: C[A])
(implicit factory: Factory[(String, Any), C[(String, Any)]])
: C[(String, Any)] = {
factory.fromSpecific(col.iterator.map(f => f._1.toString -> f._2))
}
}
object Main extends App {
import implicitsContainer._
def a(f: Seq[(String, Any)]): Seq[(String, Any)] = f
val w: Seq[(AImpl, BImpl)] = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
// Won't compile
// val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
}
Scala automatically picking up the implicit method
implicit def toStringAnyTuples[C[X] <: Iterable[X], A <: AB](col: C[A])
(implicit factory: Factory[(String, Any), C[(String, Any)]])
: C[(String, Any)] = {
factory.fromSpecific(col.iterator.map(f => f._1.toString -> f._2))
}
for this: -
val w: Seq[(AImpl, BImpl)] = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
but throws an error for this
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
and the error is:-
Error:(44, 47) type mismatch;
found : (AImpl, BImpl)
required: (String, Any)
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
and one more point, if I remove the type from w
val w = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
then also this will work fine.
The only error is with
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
I am using: -
SCALA -> 2.13.3
SBT -> 1.3.13
JAVA -> 14
It's just type inference issue. Type parameter of Seq.apply was not inferred. Try
val y: Seq[(String, Any)] = a(Seq[(AImpl, BImpl)](AImpl() -> BImpl()))
or
val y: Seq[(String, Any)] = a(Seq[(A, B)](AImpl() -> BImpl()))
What you are experiencing is a symptom of the way inference works in the Scala compiler.
Here is a smaller example that shows the same issue:
object Test {
class C[T](x: T)
implicit def conv(c: C[Int]): C[String] = ???
def m(cs: C[String]) = 1
val ci = new C(1)
def t1: Int = m(ci) // OK
def t2: Int = m(new C(1)) // found: Int, expected: String
}
When type-checking new C(1), the compiler pushes down the expected type String to type
checking the expression 1, which fails. In the line above, type checking ci with expected type
C[String] succeeds thanks to the implicit conversion.
My suggestion here would be to define an extension method that performs the conversion, instead
of making the conversion implicit. This is also recommended for clarity - implicit conversions like
the one defined in your example can lead to surprising, hard to diagnose issues.
In my example, it would look like this:
object Test {
class C[T](x: T)
implicit class CExt(private val c: C[Int]) extends AnyVal {
def toCString: C[String] = ???
}
def m(cs: C[String]) = 1
val ci = new C(1)
def t1: Int = m(ci.toCString)
def t2: Int = m(new C(1).toCString)
}
Related
When I try to call the following function:
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = {
...
}
with this:
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
sortBy(Seq(o1, o2), x => x.f1)
I get compilation error "cannot resolve symbol f1"
However when I call it with explicit types it works:
sortBy[MyClass,Int](...)
My question is why scala cannot infer these types automatically?
As one of the ways to solve
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = ???
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
def orderFunc(a: MyClass): Int = a.f1
sortBy(Seq(o1, o2), orderFunc)
I thought we can rely on implicit conversion which converts scala.Double to java.lang.Double. So I tried the following:
import scala.collection.JavaConverters._
object Main extends App {
def main(args: Array[String]) = {
val m = Map("10" -> 20.0)
doSome(m.asJava) //error. Type mismatch found: java.util.Map[String,scala.Double]
// required: java.util.Map[String,java.lang.Double]
doSome2(m.asJava)
}
def doSome(m: java.util.Map[java.lang.String, java.lang.Double]) = println(m)
def doSome2(m: java.util.Map[java.lang.String, Double]) = println(m)
}
Why doesn't it work? What would be the idiomatic way to perform such a conversion?
You need the boxed version of double:
import scala.collection.JavaConverters._
m.mapValues(Double.box).asJava
The implicits are able to convert a value of Double to java.lang.Double, but not a Map[String,Double] to java.util.Map[String,java.lang.Double].
String requires no conversion because String is a java.lang.String while Double is a double (primitive).
It seems that for String, you don't need to do any conversion, but is not the case for Double.
You can use the method double2Double which is defined in Predef to convert to java.double.
import scala.collection.JavaConverters._
m.map { case (k, v) => k -> double2Double(v) }.asJava
or another way is to do asInstanceOf to convert it to Java map directly.
The issue here is that scala.Double is a primitive (equivalent to Java double) while java.lang.Double is a class. They are not at all the same thing. The JavaConverters converts between Java Map and Scala Map, not their contents. This works:
import scala.collection.JavaConverters._
object Main {
def main(args: Array[String]): Unit = {
val m = Map("10" -> 20.0)
doSome(mapConv(m))
doSome2(m.asJava)
}
def doSome(m: java.util.Map[java.lang.String, java.lang.Double]) = println(m)
def doSome2(m: java.util.Map[java.lang.String, Double]) = println(m)
def mapConv(msd: Map[String, Double]): java.util.Map[java.lang.String, java.lang.Double] = {
msd.map { case (k, v) => (k -> new java.lang.Double(v)) }.asJava
}
}
I tried implicit conversions in the following example:
val m: Map[Int, Int] = Map(10 -> "asd") //fine
val mm: Map[Int, Int] = Map("asd" -> 20) //type mismatch; found: (String, Int)
//required: (Int, Int)
implicit def stringToInt(str: String): Int = 10
Why can't we apply implicit conversions to map keys? Is there a way to work around this?
It doesn't work because you're using -> which is an (inline) operator:
implicit final class ArrowAssoc[A](self : A) extends scala.AnyVal {
#scala.inline
def ->[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
def →[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
}
You can see that by the time B is evaluated, A is already "fixed". Let's just say that you can only (implicitly) convert the right hand side of a tuple when using -> operator:
implicit def stringToInt(str: String): Int = 10
implicit def intToStr(str: Int): String = "a"
val a: Map[Int, Int] = Map(10 -> "asd") //fine
val b: Map[Int, Int] = Map("asd" -> 20) // error! cant change left side
val c: Map[String, String] = Map("asd" -> 20) // fine
val d: Map[String, String] = Map(10 -> "asd") // error! cant change left side
Because of similar compiler quirks related to using operator ->, #Jorg's solution works in one direction, but not the other:
implicit def tupleIntifier(t: (String, Int)) = (10, 10)
implicit def tupleIntifier2(t: (Int, String)) = (10, 10)
val a: Map[Int, Int] = Map("asd" -> 20) // uses tupleIntifier
val b: Map[Int, Int] = Map(10 -> "asd") // fails!!
However, if you avoid using -> operator altogether and simply use (key, value) syntax, it will work:
val a: Map[Int, Int] = Map((10, "asd"))
val b: Map[Int, Int] = Map(("asd", 20))
implicit def stringToInt(str: String): Int = 15
println(a) // prints Map(10 -> 15)
println(b) // prints Map(15 -> 20)
Please, look at the error message you are getting:
error: type mismatch;
found : (String, Int)
required: (Int, Int)
val mm: Map[Int, Int] = Map("asd" -> 20)
^
The error message is not about String instead of Int, it is about (String, Int) instead of (Int, Int). So, you are simply converting the wrong thing:
implicit def tupleIntifier[T](t: (String, T)) = (10, t._2)
val mm: Map[Int, Int] = Map("asd" -> 20)
//=> mm: Map[Int,Int] = Map(10 -> 20)
Voila! It works.
If you were to add such a general implicit conversion, you would lose the type-safety that Scala is enforcing, because any String would become an Int as needed, anywhere, without intervention from the programmer.
In reality, when you want to create that map from other data, you probably already know the data types of that other data. So if the keys are known to be integers, convert them to Int and use them like that. Otherwise, use strings.
Your example is highly artificial. Which concrete problem are you trying to solve?
I'm using a ConcurrentHashMap in Scala and I would like to use the computeIfAbsent() method but can't figure out the syntax for the second argument. Can someone show me what would be the proper syntax?
When running the following code
val data = new ConcurrentHashMap[String, LongAdder]
data.computeIfAbsent("bob", k: String => new LongAdder()).increment()
I'm getting the following error
Type mismatch, expected: Function[_ >: String, _ <: LongAdder], actual: (String) => Any
Thanking you in advance
Francis
The problem is that you're using java.util.concurrent.ConcurrentHashMap, which accepts java.util.function.Function as a parameter for computeIfAbsent() instead of scala.Function1 which you pass to it.
Since scala doesn't support lambda conversion for functional interfaces as Java does (at least not without the -Xexperimental flag), you can solve this by implementing a java.util.function.Function explicitly:
val data = new ConcurrentHashMap[String, LongAdder]
val adderSupplier = new java.util.function.Function[String, LongAdder]() {
override def apply(t: String): LongAdder = new LongAdder()
}
data.computeIfAbsent("bob", adderSupplier).increment()
Alternatively, if you need this more often, you may write a utility conversion function or even an implicit conversion:
object FunctionConverter {
implicit def scalaFunctionToJava[From, To](function: (From) => To): java.util.function.Function[From, To] = {
new java.util.function.Function[From, To] {
override def apply(input: From): To = function(input)
}
}
}
import FunctionConverter._
val data = new ConcurrentHashMap[String, LongAdder]()
data.computeIfAbsent("bob", (k: String) => new LongAdder()) // <- implicit conversion applied here
If you enable -Xexperimental flag you can use scala anonymous function notation for this:
scala> val data = new java.util.concurrent.ConcurrentHashMap[String, Int]
data: java.util.concurrent.ConcurrentHashMap[String,Int] = {}
scala> data.computeIfAbsent("bob", _.size)
res0: Int = 3
Note that you still can't pass regular scala Function
scala> val f: String => Int = _.size
f: String => Int = <function1>
scala> data.computeIfAbsent("bob", f)
<console>:13: error: type mismatch;
found : String => Int
required: java.util.function.Function[_ >: String, _ <: Int]
data.computeIfAbsent("bob", f)
^
But eta-expansion will work
scala> def a(s: String): Int = s.size
a: (s: String)Int
scala> data.computeIfAbsent("bob", a)
res3: Int = 3
My understanding is that TypeTags can allow us to recover types at runtime. However, I am unable to do so when using a collection parameterized with TypeTag[_]:
Here is my first attempt:
import scala.reflect.runtime.universe._
import scala.collection.mutable
type TaggedVal = Tuple2[TypeTag[_], Any]
val tagMap: Map[Int, TaggedVal] = Map(
0 -> (typeTag[Int], 12345),
1 -> (typeTag[String], "abcde")
)
def get[T](key: Int)(implicit tag: TypeTag[T]) = tagMap.get(key) match {
case Some((t, v)) => Some(v.asInstanceOf[T])
case _ => None
}
implicit val zeroTag = tagMap(0)._1
val zero = get(0).get
val testing: Int = zero
This results in failure to get back the stored type:
Error:(18, 26) type mismatch;
found : Any
required: Int
lazy val testing: Int = zero
^
A second similar attempt is the following, but it results in a match error:
type TypedVal = Tuple2[Type, Any]
val tagMap: mutable.Map[Int, TypedVal] = mutable.Map()
def put[T: TypeTag](key: Int, value: T) = tagMap.put(key, (typeOf[T], value))
def get[T: TypeTag](key: Int) = tagMap.get(key).get match {
case tv: TypedVal if tv._1 =:= typeOf[T] => Some(tv._2.asInstanceOf[T])
}
put(0, (typeOf[Int], 12345))
put(1, (typeOf[String], "abcde"))
val zero = get(0).get
Is there any reasonable way to do this?
Reference: How save a TypeTag and then use it later to reattach the type to an Any (Scala 2.10)
You need to supply the type parameter to get yourself, or else it can't work. You're essentially asking for the type at compile-time, when it is only known at run-time.
type TaggedVal = (TypeTag[_], Any)
val tagMap: Map[Int, TaggedVal] = Map(
0 -> (typeTag[Int], 12345),
1 -> (typeTag[String], "abcde")
)
def get[T](key: Int)(implicit tag: TypeTag[T]): Option[T] = tagMap.get(key) match {
case Some((t, v)) if t.tpe =:= typeOf[T] => Some(v.asInstanceOf[T])
case _ => None
}
scala> get[String](1)
res31: Option[String] = Some(abcde)
scala> get[Int](1)
res32: Option[Int] = None
Something like the following (without forcing the type parameter), however, cannot work, because the type that is returned (via a cast) is only known at run-time. i can't be inferred as an Int.
val i = get(0).get
val j: Int = i