I am struggling to obtained the types of the arguments of a defined function in Scala. For example Funcion1[T1, T2].
Since Java will eliminate the types checking (compiler warning: is unchecked since it is eliminated by erasure), I would like to find a way to match that function with their types.
The goal is to be able to have same functionality as:
val fnInput = {x: Map[String, Double] => x}
fnInput match {
case f: Function1[Map[String, Double], Map[String, Double]] => ???
case f: Function1[T1, T2] => ???
case f: Function2[T1, T2, T3] => ???
}
But, checking the arguments types.
Updated: so far my solution will go into using the following tools
import scala.reflect.runtime.universe._
def getType[T: TypeTag](obj: T) = typeOf[T]
val t = getType({x: Map[String, Any] => x})
// check first argument
typeOf[Map[String, Int]] <:< t.typeArgs(0)
// check return of Function1
typeOf[Map[String, Int]] <:< t.typeArgs(1)
// t.typeArgs.length will return the number of arguments +1
Do you believe that this is a good approach?
Related
I have the following Scala program:
object Test extends App {
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
zip.foreach(f)
}
Why I get the following compiler error:
Error:(6, 15) type mismatch;
found : ((Int, Int)) => Unit
required: (Int, Int) => ?
zip.foreach(f)
when there is an implicit conversion from Tuple2Zipped to Traversable where foreach[U](f: ((El1, El2)) => U): Unit is defined.
NOTE: I edited my question to clarify.
I know that the foreach defined in Tuple2Zipped has the following signature:
def foreach[U](f: (El1, El2) => U): Unit
and that my function f does not fit as the argument.
But in Tuple2Zipped.scala is defined the trait ZippedTraversable2 and his companion object this way:
trait ZippedTraversable2[+El1, +El2] extends Any {
def foreach[U](f: (El1, El2) => U): Unit
}
object ZippedTraversable2 {
implicit def zippedTraversable2ToTraversable[El1, El2](zz: ZippedTraversable2[El1, El2]): Traversable[(El1, El2)] = {
new scala.collection.AbstractTraversable[(El1, El2)] {
def foreach[U](f: ((El1, El2)) => U): Unit = zz foreach Function.untupled(f)
}
}
}
so as my function f does match the argument of the foreach defined in the Traversable returned by zippedTraversable2ToTraversable and the companion object defines an implicit conversion from ZippedTraversable2 to this Traversable and Tuple2Zipped is a ZippedTraversable2, i think that this conversion has to be tried and my function be accepted.
The Intellij Idea editor accepts my construct without reporting any error but the compiler fails with the shown error.
Here is yet another, and slightly more esoteric, way to fix your code.
zip.foreach(Function.untupled(f))
Function.untupled() will take a ((Int, Int)) => Unit, which is what you've got in f, and return a (Int, Int) => Unit, which is what you need to process the zip elements.
Your function f is wrong ( it's also explicitly stated in the error )
zip.foreach expectes Function2 that takes type T1, type T2 and returns R, but you are passing a Function1 that takes Tuple2[Int,Int] as type T1
Here's an explaination regarding your compilation error :
found : ((Int, Int)) => Unit EQUALS Function1[Tuple2[Int,Int]] => Unit
Whereas
required: (Int, Int) => Unit EQUALS Function2[Int,Int,Unit]
(Keep in mind that () is syntactic sugar for Tuple* , that's why your are seeing the double parenthesis)
Here's an example that compiles :
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f:Function2[Int,Int,Unit] = (x,y) => println(x + y)
zip.foreach[Unit](f)
Your object is a runtime.Tuple2Zipped, if you look at the signature
def foreach[U](f: (El1, El2) => U): Unit
It takes a function f: (El1, E1l2) => U, but your function is f: ((El1, El2)) => U, hence the error.
I found it confusing too, as Tuple2Zipped almost looks like a List[(A,B)], in fact the toList method would produce just that:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped.toList
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
zip.foreach(f)
Edit
I think you are looking for an implicit conversion from ((A, B)) => U to (A, B) => U which is not what zippedTraversable2ToTraversable does. You can define something like this that would work:
implicit def tupconv[A,B] (f: (((A,B)) => Unit)): (A, B) => Unit = Function.untupled(f)
Edit V2
For the code above you'd need an implicit variable in scope that would map from ((A,B)) => Unit to (A, B) => Unit as that's where the mismatch is. Whereas the one you quoted does ZippedTraversable2[El1, El2] to Traversable[(El1, El2)]. If you'd like to make use of that implicit conversion, you could do this:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
def thisUsesTheImplicitYouWant(t: Traversable[(Int,Int)]) = t.foreach(f)
thisUsesTheImplicitYouWant(zip)
Thanks to Jasper-M I have known that a Scala bug has been reported on this issue: https://github.com/scala/bug/issues/9523. This bug was reported by Michael Pollmeier that asked this question one and a half years ago: Why does Scala implicit resolution fail for overloaded method with type parameter?
jamborta posted a question with a more generic example on this same issue here.
Hi, I've been trying to unify collection of nested maps.
So I want to implement a method with signature:
def unifyMaps(seq: Seq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]]
(WordType is a Java Enum.) The first approach was to do manual map-merging.
def unifyMapsManually(seq: IndexedSeq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]] = {
seq reduce { (acc, newMap) =>
acc ++ newMap.map { case (k, v) =>
val nestedMap = acc.getOrElse(k, Map.empty)
k -> (nestedMap ++ v.map { case (k2, v2) => k2 -> (nestedMap.getOrElse(k2, 0) + v2) })
}
}
}
It works, but what I'm doing here is recursively applying the exact same pattern, so I thought I'd make a recursive-generic version.
Second approach:
def unifyTwoMapsRecursively(m1: Map[String, Map[WordType, Int]], m2: Map[String, Map[WordType, Int]]): Map[String, Map[WordType, Int]] = {
def unifyTwoMaps[K, V](nestedMapOps: (V, (V, V) => V))(m1: Map[K, V], m2: Map[K, V]): Map[K, V] = {
nestedMapOps match {
case (zero, add) =>
m1 ++ m2.map { case (k, v) => k -> add(m1.getOrElse(k, zero), v) }
}
}
val intOps = (0, (a: Int, b: Int) => a + b)
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps) _)
unifyTwoMaps(mapOps)(m1, m2)
}
But it fails with:
Error:(90, 18) type mismatch;
found : (scala.collection.immutable.Map[pjn.wierzba.DictionaryCLP.WordType,Int], (Map[Nothing,Int], Map[Nothing,Int]) => Map[Nothing,Int])
required: (scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int], (scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int], scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int]) => scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int])
unifyTwoMaps(mapOps)(m1, m2)
^
So ok, I have no idea about upper bound on map key, but the curried function clearly is not inferred correctly. I had similar error with intOps, so I tried to provide exact types:
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
But this time it fails with:
Error:(89, 67) type mismatch;
found : Map[String,Map[pjn.wierzba.DictionaryCLP.WordType,Int]]
required: Map[?,Int]
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
^
And this time I have absolutely no idea what to try next to get it working.
EDIT: I've found solution to my problem, but I'm still wondering why do I get type mismatch error in this code snippet:
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps) _)
According to this answer scala type inference works per parameter's list - this is exactly what I've been doing here for currying purposes. My unifyTwoMaps function takes two parameters' lists and I'm trying to infer just the second one.
Solution to generic-recursive solution
Ok, so after spending morning on it I've finally understood that I've been providing wrong exact types.
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
Should've been
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[WordType, Int], _: Map[WordType, Int]))
Because I needed to pass the type of Map's V, Map[WordType, Int], and not the type of whole outer map. And now it works!
Solution to underlying problem of nested map merging
Well, abstracting over maps' V zero and add should ring a bell, I've been reinventing Monoid. So I thought I'd try Scalaz |+| Semigroups operator solution from this answer.
import scalaz.Scalaz._
def unifyMapsWithScalaz(seq: Seq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]] = {
seq reduce (_ |+| _)
}
And it works!
What's interesting is that I already saw that post before trying my solution, but I thought that I'm not sure it'd work for nested data structure, especially with my map's keys being Java Enum. I thought I'd have to provide some custom implementation extending Semigroups's typeclass.
But as it turned out during my reinventing-the-wheel implementation, the enum is only needed as a passed type and map key and it works pretty well. Well done Scalaz!
Well, that would've made a good blog post actually..
EDIT: but I still don't understand why I had this type inference problem in the first place, I've updated the question.
I am sorry for such non-descriptive title, but I really don't know how to express this better.
class Foo[T]
Seq(new Foo[String], new Foo[Int]).groupBy(_ => 1).map { case (k, Seq(v)) => k -> v }.toMap
<console>:12: error: Cannot prove that (Int, Foo[_146]) forSome { type _146 >: Int with String } <:< (T, U).
WTF?
If I use .mapValues instead of .map, it works. Also, making Foo covariant fixes it too, but in that case I end up with Map[Int,Foo[Any]]
What's going on here? Any ideas?
Without variance, you create a somewhat "nonsensical" sequence:
class Foo[T]
val in = Seq(new Foo[String], new Foo[Int]) // Seq[_ >: Int with String]]
There is simply no common LUB between Foo[String] and Foo[Int]. You may assign it an existential type:
val in = Seq[Foo[_]](new Foo[String], new Foo[Int])
Then we can try to continue:
val in = Seq[Foo[_]](new Foo[String], new Foo[Int])
val g = in.groupBy(_ => 1) // Map[Int, Seq[Foo[_]]]
// the next line would produce a match error, thus make it a `def`
def m = g.map { case (k, Seq(v)) => k -> v } // Iterable[(Int, Foo[_])]
def p = m.toMap // cannot prove that (Int, Foo[_]) <:< (T, U)
Again the existential type bites you here in disallowing a useful inference for the value type. You can enforce it again:
def p = m.toMap[Int, Foo[_]] // Map[Int,Foo[_]]
AFAIK, Scalac will not infer existential types for you.
If you are thinking you have Foo[Any] here, you need to add a variance annotation:
class Foo[+T]
val in = Seq(new Foo[String], new Foo[Int]) // Seq[Foo[Any]]
def m = in.groupBy(_=>1).map {case (k,Seq(v)) => k->v}.toMap // Map[Int,Foo[Any]]
How can I get the actual type a generic function is called with?
The following example should print the type the given function f returns:
def find[A](f: Int => A): Unit = {
print("type returned by f:" + ???)
}
If find is called with find(x => "abc") I want to get ""type returned by f: String". How can ??? be implemented in Scala 2.11?
Use a TypeTag. When you require an implicit TypeTag for a type parameter (or try to find one for any type), the compiler will automatically generate one and fill in the value for you.
import scala.reflect.runtime.universe.{typeOf, TypeTag}
def find[A: TypeTag](f: Int => A): Unit = {
println("type returned by f: " + typeOf[A])
}
scala> find(x => "abc")
type returned by f: String
scala> find(x => List("abc"))
type returned by f: List[String]
scala> find(x => List())
type returned by f: List[Nothing]
scala> find(x => Map(1 -> "a"))
type returned by f: scala.collection.immutable.Map[Int,String]
The above definition is equivalent to:
def find[A](f: Int => A)(implicit tt: TypeTag[A]): Unit = {
println("type returned by f: " + typeOf[A])
}
Use TypeTag
import scala.reflect.runtime.universe._
def func[A: TypeTag](a: A): Unit = println(typeOf[A])
scala> func("asd")
String
See more: http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html
Forgive me if this question is a duplicate; I'm having trouble finding anything because I don't know the right words to search. So, with implicit def, I can do things like this:
type CharsetMap = Map[Charset, Byte]
implicit def seqtup2CharsetMap(input: Seq[(String, Int)]): CharsetMap = {
Map.empty // placeholder
}
def somef(a: Int, b:Int, p: CharsetMap) = p
somef(1, 3, Seq(("hey", 2), ("there", 9)))
which lets me call somef with a Seq[(String, Int)] object as a parameter. The problem is that I have something like this...
def somef2(p: (CharsetMap) => Int) = p
and this does not work:
val p = (a: Seq[(String, Int)]) => 19
somef2(p)
How can I do this without doing an implicit def specifically for (Seq[(String, Int)]) => Int?
It looks like you want to implicitly convert some function A => B to a function that goes from C => B. You can do that with this generic implicit:
implicit def f2InputConverter[A, B, C](f: A => B)(implicit i: C => A): C => B = (c: C) => f(i(c))
Once you have that in scope, in your particular case, you'll need an implicit function which is the inverse of the one that you've defined in the question:
implicit def charsetMap2Seqtup(input: CharsetMap): Seq[(String, Int)] = {
Nil // placeholder
}
and then you should be able to call somef2 with p