configs icon indicating copy to clipboard operation
configs copied to clipboard

Problem reading lists of non-trivial types

Open danielspicar opened this issue 5 years ago • 1 comments

Hi

I have come across an interesting problem when using configs version 0.4.4 and 0.5.0-SNAPSHOT (22ad412d17c104930d63be05bd67474b55745434) with scala 2.11.

Example:

object MyEnum extends Enumeration {
  type MyEnum = Value

  val Foo = Value("foo")
  val Bar = Value("bar")
}

case class Foo2(enum: Option[MyEnum])

Reading this class from a config works:

val config2_0 = ConfigFactory.parseString("foo = {enum = foo}")
val result2_0 = ConfigReader[Foo2].read(config2_0, "foo")
println(result2_0)

However, reading a List (or Seq) of this class does not compile!

val config2_1 = ConfigFactory.parseString("foo = [{enum = foo}, {enum = bar}]")
val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")
println(result2_1)
Error:(36, 33) diverging implicit expansion for type configs.ConfigReader[java.util.List[Foo2]]
starting with method fromStringConfigReader in class ConfigReaderInstances
    val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")

Error:(36, 33) diverging implicit expansion for type configs.ConfigReader[Option[MyEnum.MyEnum]]
starting with macro method autoDeriveConfigReader in class ConfigReaderInstances3
    val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")

Error:(36, 33) not enough arguments for method apply: (implicit A: configs.ConfigReader[List[Foo2]])configs.ConfigReader[List[Foo2]] in object ConfigReader.
Unspecified value parameter A.
    val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")

The same problem appears in other similar cases. For example:

case class EitherExample(num: Either[Int, String])

with the following reader:

implicit def eitherReader[A, B](implicit aReader: ConfigReader[A], bReader: ConfigReader[B]): ConfigReader[Either[A, B]] = {
  ConfigReader.fromTry { (c, p) =>
    aReader.read(c, p).map(Left(_)).orElse(bReader.read(c, p).map(Right(_))).valueOrThrow(_.configException)
  }
}

Reading this class from a config works:

val config3_0 = ConfigFactory.parseString("{num = two}")
val result3_0 = ConfigReader[EitherExample].extract(config3_0)
println(result3_0)

However, reading a List (or Seq) of this class does not compile!

val config3_1 = ConfigFactory.parseString("foo = [{num = 2}, {num = two}]")
val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")
print(result3_1)
Error:(44, 33) diverging implicit expansion for type configs.ConfigReader[Either[Int,String]]
starting with method eitherReader in object Bug
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Error:(44, 33) diverging implicit expansion for type configs.ConfigReader[java.util.List[EitherExample]]
starting with method fromStringConfigReader in class ConfigReaderInstances
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Error:(44, 33) diverging implicit expansion for type configs.ConfigReader[Either[Int,String]]
starting with macro method autoDeriveConfigReader in class ConfigReaderInstances3
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Error:(44, 33) not enough arguments for method apply: (implicit A: configs.ConfigReader[List[EitherExample]])configs.ConfigReader[List[EitherExample]] in object ConfigReader.
Unspecified value parameter A.
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Note: All examples compile and run with Scala 2.13. However, I do not have the option to upgrade beyond 2.11 in the project I am working on.

Any clues as to what is happening and whether it can be fixed or avoided?

Thank you!

danielspicar avatar Oct 29 '19 15:10 danielspicar

We found a workaround (hopefully) which may help to shed some light on what is happening.

The examples above work, when the "pimp my library" decorator for the typesafe Config is used instead of ConfigReader[A].

These work:

import configs.syntax._

val result2_1 = config2_1.get[List[Foo2]]("foo")

val result3_1 = config3_1.get[List[EitherExample]]("foo")

danielspicar avatar Nov 05 '19 07:11 danielspicar