Scala v2.13.11 issues a warning for the following code from the pureconfig orElse documentation (bottom of the page):
val csvIntListReader = ConfigReader[String].map(_.split(",").map(_.toInt).toList)
implicit val intListReader = ConfigReader[List[Int]].orElse(csvIntListReader)
case class IntListConf(list: List[Int])
The warning is:
[warn] Implicit definition should have explicit type (inferred pureconfig.ConfigReader[List[Int]])
[warn] implicit val intListReader = ConfigReader[List[Int]].orElse(csvIntListReader)
[warn] ^
When I add the inferred type, ConfigReader[List[Int]], it compiles without the warning but I get a NullPointerException at run-time.
This raises the following questions for me:
- Why does this work when we let the compiler infer the type but it does not when we explicitly supply the type the compiler says it inferred?
- Is there a type that can be explicitly given to
intListReaderthat will compile without a warning and run without error? - If 3 is not possible, is adding
@nowarn"safe" (eg still work with Scala3)?
Thanks for your insights.
PS: My run-time tests are also from the documentation:
ConfigSource.string("""{ list = [1,2,3] }""").load[IntListConf] ==> Right(IntListConf(List(1, 2, 3)))
and
ConfigSource.string("""{ list = "4,5,6" }""").load[IntListConf] ==> Right(IntListConf(List(4, 5, 6)))
It's an undocumented undefined behavior.
When you do:
compiler would generate
Which depending on context (where you have out it, is it
val,lazy valordef) will end up with:Usually, you would like to have it resolved to:
which would use some mechanics to avoid using
xin the process of computing the implicit. E.g. by using some wrapper or subtype to unwrap/upcast (e.g. in Circe you ask for Encoder/Decoder derived with semiauto and what is obtained by implicit is some DerivedEncoder/DerivedDecoder to unwrap/upcast).This is defined behavior coming from how implicits are resolved when all of implicit definitions in your scope are annotated.
What happens if some is not like this one?
ConfigReader[List[Int]]while computingintListReaderintListReader) is computed to beConfigReader[List[Int]]In general, library shouldn't rely on this behavior, there should be some semiautomatic derivation provided for you and in future versions on Scala (3.x) this code is illegal so I suggest rewriting it to semiauto.
In your particular case you can also use a trick, to summon different implicit of a different than the one being returned:
but if it wasn't a type with a build-in support (they don't work with
deriveReaderas it is only defined forsealedandcase class) you could use: