Scala: a strange bahaviour of seqAsJavaList - scala

Given the following code:
import collection.JavaConversions._
def j(x: java.util.List[java.lang.Integer]): Unit =
// the function body here can be any valid code
println (x)
val a = List(1)
I get a type mismatch error when I call this:
j (seqAsJavaList(a))
Here is the error from REPL
<console>:13: error: type mismatch;
found : List[Int]
required: Seq[Integer]
f (seqAsJavaList(a))
^
However, I can call this with no error:
j(seqAsJavaList(List(1)))
I'm using 2.11.2.
Can someone explain to me why seqAsJavaList behaves differently? Thanks.
Adding more context/clarification to my original question:
What I meant to ask was "why does seqAsJavaList behave differently when operating on a predefined variable a than on an in-place value List(1) when they are of the same type?" Furthermore seqAsJavaList(a) and seqAsJavaList (List(1)) return exactly the same signature java.util.List[Int]. Using the substitution model, one would expect both j (seqAsJavaList(a)) and j (seqAsJavaList (List(1)) ) to succeed. And yet, only the latter works. When both seqAsJavaList(a) and seqAsJavaList (List(1)) are java.util.List[Int], why does one usage handle Int well and the other doesn't?
Another note:
I just tried collection.JavaConverters and the result is not ideal but at least consistent:
// The definitions of j & a are the same as above. I repeat them here to save some page scrolling.
// BTW, instead of f, I use j to indicate it is supposed to be a Java static method.
// I mock it in Scala so that this can be easily run in REPL.
def j ( ls: java.util.List [java.lang.Integer] ): Unit = println (ls)
val a = List( 1 )
// new code here
import collection.JavaConverters._
// Both require the explicit casting to work
j ( a.map (i => i: java.lang.Integer).asJava )
j ( List(1).map (i => i: java.lang.Integer).asJava )
// These fail with the same error.
j( a.asJava )
j( List(1).asJava )
// <console>:12: error: type mismatch;
// found : java.util.List[Int]
// required: java.util.List[Integer]
// j ( List(1).asJava )
// ^

The problem here is not with List but with its type Int. For example this works:
scala> j ( seqAsJavaList (a.map(x => x:Integer)) )
[1]
j expects argument type as java.util.List [java.lang.Integer]. But the return type of seqAsJavaList in your case is java.util.List [Int].
The above example works because now seqAsJavaList will take List [java.lang.Integer] and return java.util.List[java.lang.Integer]. Hence it works.
Or you could:
scala> implicit def toJavaIntegerList(ls:List[Int]):java.util.List[Integer] = seqAsJavaList(ls.map(x => x:Integer))
scala> j(List(1,2,3))
[1, 2, 3]
To explain why this works:
j (seqAsJavaList(List(1)))
This is equivalent to:
scala> val temp:List[Integer] = List(1)
temp: List[Integer] = List(1)
scala> j (seqAsJavaList(temp))
[1]
Or better: j (seqAsJavaList(List(1:Integer)))
Type-inference is in work here. There is an implicit function
implicit def int2Integer(x:Int):java.lang.Integer
defined in Predef. When you do j(seqAsJavaList(List(1))), type inference predicts that this can succeed legitimately by using implicit function which converts Int => java.lang.Integer. It sees that if this implicit is used with all elements of List, then call will succeed legitimately. So List(1) is actually constructed as List[Integer] rather than List[Int]
This is confirmed by checking
object Temp extends App{
import collection.JavaConversions._
def j(x: java.util.List[java.lang.Integer]): Unit = println (x)
j(seqAsJavaList(List(1)))
}
jatinpuri#jatin:~/Desktop$ scalac -Xprint:typer Temp.scala
[[syntax trees at end of typer]] // Temp.scala
package <empty> {
object Temp extends AnyRef with App {
def <init>(): Temp.type = {
Temp.super.<init>();
()
};
import scala.collection.JavaConversions._;
def j(x: java.util.List[Integer]): Unit = scala.this.Predef.println(x);
Temp.this.j(scala.collection.JavaConversions.seqAsJavaList[Integer](immutable.this.List.apply[Integer](scala.this.Predef.int2Integer(1))))
}
}
Notice (immutable.this.List.apply[Integer](scala.this.Predef.int2Integer(1))). So List(1) is actually constructed as List[Integer] and not List[Int]
This doesn't work in original case because in doing val a = List(1), a is set as List[Int]. And the only way to change it to List[Integer] is map all contents to Integer (as there is no implicit available in scala lib that converts List[Int] => List[Integer])

Related

Getting an implicit parameterized type in a for comprehension, in Scala 3

A Scala 3 syntax question here.
I feel like there's not enough documentation around this yet.
How do you fetch an implicit value in a for comprehension, when its type is parameterized?
for
o <- Option("line just to make for comprehension start")
given Int = 2
a = implicitly[Int] // error: no implicit argument of type Int was found for parameter e of method implicitly in object Predef
given List[Int] = List(1, 2, 3)
b = implicitly[List[Int]] // error: no implicit argument of type List[Int] was found for parameter e of method implicitly in object Predef
yield ()
Edit:
But if I insert a dummy line in between the declaration and the usage, it works:
for
o <- Option("line just to make for comprehension start")
given List[Int] = List(1, 2, 3)
p <- Option("dummy")
b = implicitly[List[Int]] // works
yield ()

Why does scala allow concating Strings with Option[Strings](or any other type)?

I mistakenly concatted a string with an Option[String] while coding in scala.
I expected as a strongly typed language, scala would not allow me to do such operation.
This is what I tried.
This works
scala> val a:String = "aaa"
val a: String = aaa
scala> val b:Option[String] = Some("bbbb")
val b: Option[String] = Some(bbbb)
scala> a + b
val res0: String = aaaSome(bbbb)
scala> val c:Option[String] = None
val c: Option[String] = None
scala> val d = a + c
val d: String = aaaNone
scala> val e = 1
val e: Int = 1
scala> a + e
val res2: String = aaa1
while this does not work
scala> val f:Option[String] = Some("ffff")
val f: Option[String] = Some(ffff)
scala> val g:Option[String] = None
val g: Option[String] = None
scala> f + g
^
error: type mismatch;
found : Option[String]
required: String
Why does scala allow such behavior? Dynamically typed languages like python will stop me from adding strings to int types, None types or any type other than strings. Curious if this design is intentional? If so why?
Scala contains an implicit class any2stringadd in it's Predef package. This class is responsible for these concatenation operations.
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
What it means is that, by default, scope contains a method + which can concatenate value of any type A with string by converting value of this type to string via String.valueOf(...).
I can't speak of design choices, I agree that this might be an unexpected behavior. The same applies to Scala's native == method. For example, this code compiles just ok: Some("a") == "b". This can lead to nasty bugs in filtering and other methods.
If you want to eliminate this behavior I suggest you take a look at https://typelevel.org/cats/ library, which introduces different typeclasses that can solve this problem.
For example, for string concatenation you can use Semigroup typeclass (which has tons of other useful use-cases as well):
import cats.Semigroup
Semigroup[String].combine("a", "b") // works, returns "ab"
Semigroup[String].combine("a", Some("b")) // won't work, compilation error
This looks tedious, but there is a syntactic sugar:
import cats.implicits._
"a" |+| "b" // works, returns "ab"
"a" |+| Some("b") // won't work, compilation error
// |+| here is the same as Semigroup[String].combine
The same thing applies to == method. Instead you can use Eq typeclass:
import cats.implicits._
"a" == Some("b") // works, no error, but could be unexpected
"a" === Some("b") // compilation error (Cats Eq)
"a" === "b" // works, as expected
"a" =!= "b" // same as != but type safe

Type aliases that work in the REPL but not in a scala class/object

I have a testing program in which we have in memory static arrays. I am using type aliases for brevity.
The following works in the REPL
type >[T] = Array[T]
val dat = >(>(1,2,3),>(2,3,4))
dat: Array[Array[Int]] = Array(Array(1, 2, 3), Array(2, 3, 4))
However changing the identifier to "A" from ">" does not work: the type is created but the same syntax for creating the array as used above fails:
scala> type A[T] = Array[T]
defined type alias A
scala> val dat = A(A(1,2,3),A(2,3,4))
<console>:7: error: not found: value A
val dat = A(A(1,2,3),A(2,3,4))
Also, NEITHER of the two above work within a Scala program AFAICT:
test("VectorProjection") {
type A[T] = Array[T]
// Next line shows RED for all the A's and also has compiler error: "not found: value A"
val dat = A(A(1., 2., 3.), A(1.5,2.,2.5), A(2.,3.8,5.6), A(2.5,3.0,3.5), A(3.1,3.7,4.3) )
val firsteigen = subtractProject(dat(0), dat(4))
}
Looking for:
1) For the REPL: An explanation of why the symbol ">" works but not
the identifier would be helpful.
2) For a real scala program/class:
An explanation of if it were possible to use any syntax similar to
the above
UPDATE Per suggestion by James Iry the following approach does work :
def A[T : ClassTag](ts: T*) = Array(ts:_*)
Here it is in action:
test("VectorProjection") {
def A[T : ClassTag](ts: T*) = Array(ts:_*)
val dat = A(
A(1., 2., 3.),
A(1.5,2.,2.5),
A(3.,6.,9.) )
val firstEigen = subtractProject(dat(0), dat(5))
println(s"firstEigen: ${firstEigen.mkString(",")}")
}
Another UPDATE Another answer hits closer to this OP:
Use type and val together:
type A = Array[Double]
val A = Array
Here it is in action:
test("VectorProjection") {
type A = Array[Double]
val A = Array
val dat = A(
A(1., 2., 3.),
A(1.5,2.,2.5),
A(3.,6.,9.) )
val firstEigen = subtractProject(dat(0), dat(5))
println(s"firstEigen: ${firstEigen.mkString(",")}")
}
I'm not able to replicate your success with '>'
scala> type >[T]=Array[T]
defined type alias $greater
scala> >(1,2,3)
<console>:8: error: not found: value >
>(1,2,3)
^
At least, not until I define it
scala> import scala.reflect._
import scala.reflect._
scala> def >[T : ClassTag](ts: T*) = Array(ts:_*)
$greater: [T](ts: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]
scala> >(1,2,3)
res1: Array[Int] = Array(1, 2, 3)
Same thing works for A
scala> type A[T]=Array[T]
defined type alias A
scala> A(1,2,3)
<console>:11: error: not found: value A
A(1,2,3)
^
scala> def A[T : ClassTag](ts: T*) = Array(ts:_*)
A: [T](ts: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]
scala> A(1,2,3)
res2: Array[Int] = Array(1, 2, 3)
For an explanation: type X = Y just creates a synonym for the type X. It doesn't bring in synonyms for everything else that might be associated with the type like companion objects, constructor methods, etc.
If you create a value alias it will work:
type A[T] = Array[T]
val A = Array
val dat = A(A(1,2,3),A(2,3,4)) //dat: Array[Array[Int]] = Array(Array(1, 2, 3), Array(2, 3, 4))
Line 2 creates a value alias, so you can create values with A type alias. It will in turn be able to call A.apply(1,2,3).
Use this to show what the repl knows:
scala> $intp.definedTerms
res0: List[$intp.global.TermName] = List($intp)
scala> $intp.definedTypes
res1: List[$intp.global.TypeName] = List($greater)
E.g., you might have:
scala> object X
defined object X
scala> trait X
defined trait X
warning: previously defined object X is not a companion to trait X.
Companions must be defined together; you may wish to use :paste mode for this.
scala> type X = String
defined type alias X
But it doesn't warn on aliasing.

Invoking functions returned by methods that take implicits

Given the following function:
def foo()(implicit count: Int): (String => Seq[String]) = {
s => for (i <- 1 until count) yield s
}
Calling apply() on the result explicitly works:
implicit val count = 5
val x = foo().apply("x") // <- works fine
And setting the result to a val, which you then call as a function, works:
val f: String => Seq[String] = foo()
f("y") // <- works fine
But trying to do it all in one line, without apply, confuses the compiler into thinking you're passing the implicit explicitly:
val z = foo()("z") // type mismatch; found: String("z"), required: Int
Is there a way to do this without either the explicit apply or the intermediate val? For instance, is it possible somehow to move the implicit declaration into the returned anonymous function?
scala> (foo() _)("z")
res10: Seq[String] = Vector(z, z, z, z)

Scala convert List[Int] to a java.util.List[java.lang.Integer]

Is there a way in Scala to convert a List[Int] to java.util.List[java.lang.Integer]?
I'm interfacing with Java (Thrift).
JavaConversions supports List --> java.util.List, and implicits exist between Int --> java.lang.Integer, but from what I can tell I would still need an extra pass to manually do the conversion:
val y = List(1)
val z: java.util.List[Integer] = asList(y) map { (x: Int) => x : java.lang.Integer }
Apparently you need both conversions. However, you can group them in a single implicit conversion:
implicit def toIntegerList( lst: List[Int] ) =
seqAsJavaList( lst.map( i => i:java.lang.Integer ) )
Example:
scala> def sizeOf( lst: java.util.List[java.lang.Integer] ) = lst.size
scala> sizeOf( List(1,2,3) )
res5: Int = 3
Because the underlying representation of Int is Integer you can cast directly to java.util.List[java.lang.Integer]. It will save you an O(n) operation and some implicit stuff.
import collection.JavaConversions._
class A {
def l() = asList(List(1,2)).asInstanceOf[java.util.List[java.lang.Integer]]
}
Then you can use from Java like this:
A a = new A();
java.util.List<Integer> l = a.l();
Note that on 2.9.0 ,I get a deprecation warning on asList (use seqAsJavaList instead)
Did you try:
val javalist = collection.JavaConversions.asJavaList (y)
I'm not sure, whether you need a conversion Int=>Integer or Int=>int here. Can you try it out?
Update:
The times, they are a changing. Today you'll get a deprecated warning for that code. Use instead:
import scala.collection.JavaConverters._
val y = List (1)
> y: List[Int] = List(1)
val javalist = (y).asJava
> javalist: java.util.List[Int] = [1]
This doesn't have an implicit at the outmost layer, but I like this generic approach and have implemented it for a couple of types of collections (List, Map).
import java.util.{List => JList}
import scala.collection.JavaConverters._
def scalaList2JavaList[A, B](scalaList: List[A])
(implicit a2bConversion: A => B): JList[B] =
(scalaList map a2bConversion).asJava
Since an implicit conversion from Int to Integer is part of standard lib, usage in this case would just look like this:
scalaList2JavaList[Int, Integer](someScalaList)
In the other direction!
(since I have these available anyway as they were my original implementations...)
import java.util.{List => JList}
import scala.collection.JavaConversions._
def javaList2ScalaList[A, B](javaList: JList[A])
(implicit a2bConversion: A => B): List[B] =
javaList.toList map a2bConversion
Usage:
javaList2ScalaList[Integer, Int](someJavaList)
This can then be re-used for all lists so long as an implicit conversion of the contained type is in scope.
(And in case you're curious, here is my implementation for map...)
def javaMap2ScalaMap[A, B, C, D](javaMap: util.Map[A, B])(implicit a2cConversion: A => C, b2dConversion: B => D): Map[C, D] =
javaMap.toMap map { case (a, b) => (a2cConversion(a), b2dConversion(b)) }
Starting Scala 2.13, the standard library includes scala.jdk.CollectionConverters which provides Scala to Java implicit collection conversions.
Which we can combine with java.lang.Integer::valueOf to convert Scala's Int to Java's Integer:
import scala.jdk.CollectionConverters._
List(1, 2, 3).map(Integer.valueOf).asJava
// java.util.List[Integer] = [1, 2, 3]
I was trying to pass a Map[String, Double] to a Java method. But the problem was JavaConversions converts the Map to a java Map, but leaves the scala Double as is, instead of converting it to java.lang.Double. After a few hours of seaching I found [Alvaro Carrasco's answer])https://stackoverflow.com/a/40683561/1612432), it is as simple as doing:
val scalaMap = // Some Map[String, Double]
val javaMap = scalaMap.mapValues(Double.box)
After this, javaMap is a Map[String, java.lang.Double]. Then you can pass this to a java function that expects a Map<String, Double> and thanks to implicit conversions the Scala Map will be converted to java.util.Map
In your case would be the same, but with Int.box:
val y = List(1)
val javay = y.map(Int.box)