Something about implicit class, confuses reduce(). When inside implicit class, compiler complains on reduce() second parameter. but when same code is inside non-implicit method it compiles and works fine.
What am I missing about implicit classes?
object ImpliCurri {
implicit class MySeq[Int](val l: Seq[Int]) {
//not compiling
final def mapSum(f:Int=>Int):Int = {
l.map(x=>f(x)).reduce(_+_)
//compile error on reduce: Type mismatch. Expected String, fount Int
}
}
// works fine
def mySum(l:Seq[Int], f:Int=>Int):Int = {
l.map(x=>f(x)).reduce(_+_)
// compiles and works no issues
}
}
You need to get rid of the type parameter Int
. Int
in the case of the implicit class is not actually the type Int
, but instead it's a free type parameter that's shadowing the name of Int
.
The reason for the cryptic compiler error is that the compiler is inferring the type Any
from the lambda _ + _
(since the type parameter could be anything), and assuming the +
will come from a toString
on type Any
. If you replace Int
with T
in the class declaration, you'll see the error is the same.
This will work:
implicit class MySeq(val l: Seq[Int]) {
final def mapSum(f: Int => Int): Int = {
l.map(x => f(x)).reduce(_ + _)
}
}
It doesn't actually have anything to do with implicits. You get the same error if it's just a regular class
.
The reason is that you have declared a generic type: MySeq[Int]
that just happens to be called Int
. So when you say f: Int => Int
you think "Oh, that's an integer" and the compiler thinks, "Oh, that means you could fill in any type there!". (Replace all your Int
s with A
and it would work the same.)
Now the compiler is in a bind. What +
can you apply to any pair of types? Well, you can convert anything to a String
, and +
is defined on a String
. So you get a very misleading error message when the compiler realizes that this approach won't work.
Just drop the [Int]
and all your Int
s will actually mean what you think they mean, and the implicit class version will work fine.
Replace MySeq[Int](val l: Seq[Int])
with MySeq(val l: Seq[Int])
.
Explanation of the compiler message:
The MySeq[Int]
part defines an abstract type parameter for class MySeq
named Int
, which is (automatically) a subclass of Any
and shadows the actual scala.Int
. Then the compiler tries to call +
method of an instance of Any
. It sees a declaration of an implicit class scala.Predef.any2stringadd
, which has a method with signature def +(other: String): String
, so the compiler thinks the second parameter to +
should be a String
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.