简体   繁体   中英

scala implicit class method type mismatch in reduce vs non-implicit method with function currying

Problem:

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?

Code:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM