Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
865 views
in Technique[技术] by (71.8m points)

scala - Implicit view not working - is my implicit def to blame?

I'm having some trouble with an implicit view. I suspect this is quite trivial and may have some embarassingly easy answer. I have a situation like this, along with (obviously, unsuccessful) attempts to debug it:

abstract class StoresNumeric[A, T: Numeric] {
  def getNum(self: A): T
}
object StoresNumericSyntax {
  implicit class StoresNumericOps[A, T: Numeric](value: A) {
    def getNum(implicit sn: StoresNumeric[A, T]): T = sn.getNum(value)
  }
}
case class ANumber[T: Numeric](
  num: T
)
implicit def aNumberStoresNumeric[T: Numeric] = 
  new StoresNumeric[ANumber[T], T] {
    def getNum(self: ANumber[T]): T = self.num
  }
val a = ANumber[Int](3)
// 1. Works fine, so explicit conversion possible
aNumberStoresNumeric[Int].getNum(a) 
// 2. Works fine, so implicit conversion possible
implicitly[StoresNumeric[ANumber[Int], Int]].getNum(a) 
// 3. Doesn't work, so implicit conversion not working
println(implicitly[ANumber[Int] => StoresNumeric[ANumber[Int], Int]]) // no implicit view available...
// 4. The holy grail. Doesn't work, for the same reason as above, plus possibly other
a.getNum

I think the issue here is my implicit def, or more accurately my lack of understanding about implicit def is actually meant to work. I'm not sure what role the generics play - should the first generic argument be used to represent the type I would actually like converted, or is it OK as it is?

Any help gratefully received.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Firstly (#3),

implicitly[ANumber[Int] => StoresNumeric[ANumber[Int], Int]]

is wrong. You do not define an implicit conversion from a data type to a type class, you define an implicit conversion from a data type to an implicit class introducing extension method. So it should be

implicitly[ANumber[Int] => StoresNumericSyntax.StoresNumericOps[ANumber[Int], Int]]

and it compiles.

Be prepared that implicitly[A => B] not always checks that an implicit conversion from A to B exists. * (see below)

Secondly (#4), when you use extension method (a.getNum) you should import syntax object:

import StoresNumericSyntax._
(a: StoresNumericOps[ANumber[Int], Int]).getNum

compiles while

import StoresNumericSyntax._
a.getNum

produces (with scalacOptions += "-Xlog-implicits" switched on)

 Warning:
StoresNumericOps is not a valid implicit value for App.a.type => ?{def getNum: ?} because:
ambiguous implicit values:
 both object BigIntIsIntegral in object Numeric of type scala.math.Numeric.BigIntIsIntegral.type
 and object IntIsIntegral in object Numeric of type scala.math.Numeric.IntIsIntegral.type
 match expected type Numeric[T]

If you import IntIsIntegral you'll add this implicit to the local scope (before that it was in the implicit scope only), so you'll make its "priority" "higher" than the one of BigIntIsIntegral. Try

import StoresNumericSyntax._
import Numeric.IntIsIntegral
a.getNum

It compiles.

Scala 2.13.3.

See also how to debug implicits (at compile time): In scala 2 or 3, is it possible to debug implicit resolution process in runtime?


* For example if you modify the implicit class as @LuisMiguelMejíaSuárez advised in comments

object StoresNumericSyntax {
  implicit class StoresNumericOps[A](private val value: A) extends AnyVal {
    def getNum[T: Numeric](implicit sn: StoresNumeric[A, T]): T = sn.getNum(value)
  }
}

then

import StoresNumericSyntax._
implicitly[ANumber[Int] => StoresNumericOps[ANumber[Int]]]

doesn't compile

 Warning:
StoresNumericOps is not a valid implicit value for ANumber[Int] => StoresNumericSyntax.StoresNumericOps[ANumber[Int]] because:
hasMatchingSymbol reported error: type mismatch;
 found   : StoresNumericSyntax.StoresNumericOps.type
 required: ANumber[Int] => StoresNumericSyntax.StoresNumericOps[App393.ANumber[Int]]

while manually resolved

implicitly[ANumber[Int] => StoresNumericOps[ANumber[Int]]](new StoresNumericOps(_)) 

compiles and

import StoresNumericSyntax._
a: StoresNumericOps[ANumber[Int]]

compiles too.

But if I remove extends AnyVal

object StoresNumericSyntax {
  implicit class StoresNumericOps[A](private val value: A) /*extends AnyVal*/ {
    def getNum[T: Numeric](implicit sn: StoresNumeric[A, T]): T = sn.getNum(value)
  }
}

then

import StoresNumericSyntax._
implicitly[ANumber[Int] => StoresNumericOps[ANumber[Int]]]

compiles.

Also if I split the implicit class into a class + an implicit conversion

object StoresNumericSyntax {
  /*implicit*/ class StoresNumericOps[A](private val value: A) extends AnyVal {
    def getNum[T: Numeric](implicit sn: StoresNumeric[A, T]): T = sn.getNum(value)
  }

  implicit def toStoresNumericOps[A](value: A): StoresNumericOps[A] =
    new StoresNumericOps(value)
}

then

import StoresNumericSyntax._
implicitly[ANumber[Int] => StoresNumericOps[ANumber[Int]]]

compiles.

Why implicitly[A => B] is not the same as val x: B = ??? : A is expained here:

In scala, are there any condition where implicit view won't be able to propagate to other implicit function?

When calling a scala function with compile-time macro, how to failover smoothly when it causes compilation errors?

Scala Kleisli throws an error in IntelliJ

What are the hidden rules regarding the type inference in resolution of implicit conversions?

Scala: `ambigious implicit values` but the right value is not event found

The impact of presence/absence of extends AnyVal on implicit resolution can be a bug but difference between implicit instance (implicitly[A => B]) and implicit conversion (val x: B = ??? : A) is more or less intentional (different strategies of type inference, resolving type parameters are used).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...