question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ConfigReader.map is broken for readers with ReadsMissingKeys

See original GitHub issue

Consider:

val Right(cfg) = ConfigFactoryWrapper.parseString("")

val reader1 = ConfigReader.forProduct1("foo")(identity[Option[Int]])(
  ConfigReader[Option[Int]]
)
reader1.from(cfg.root) // Right

val reader2 = ConfigReader.forProduct1("foo")(identity[Option[Int]])(
  ConfigReader[Option[Int]].map(identity)
)
reader2.from(cfg.root) // Left

That is, .map(identity), surprisingly, results in a ConfigReader with a different behavior, breaking the functor law!

There are two ways to fix this. First, we can crawl through our codebase, and make sure all the ConfigReader combinators preserve ReadsMissingKeys. Alternatively, we can somehow change the ConfigReader abstraction to eliminate ReadsMissingKeys altogether. We can look at what Circe does here.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:2
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
mdedetrichcommented, Jul 16, 2021

Your right, you can just use the forProduct and help the type inference, I just implemented like this

  implicit val s3HeadersConfigReader =
    ConfigReader.forProduct5("canned-acl",
                             "meta-headers",
                             "storage-class",
                             "custom-headers",
                             "server-side-encryption"
    ) {
      (cannedAcl: Option[CannedAcl],
       metaHeaders: Option[MetaHeaders],
       storageClass: Option[StorageClass],
       customHeaders: Option[Map[String, String]],
       serverSideEncryption: Option[ServerSideEncryption]
      ) =>
        val base  = S3Headers()
        val base2 = cannedAcl.fold(base)(base.withCannedAcl)
        val base3 = metaHeaders.fold(base2)(base2.withMetaHeaders)
        val base4 = storageClass.fold(base3)(base3.withStorageClass)
        val base5 = customHeaders.fold(base4)(base4.withCustomHeaders)
        serverSideEncryption.fold(base5)(base5.withServerSideEncryption)
    }

The .copy is unfortunately private

1reaction
jcazevedocommented, Apr 15, 2021

I just came across this issue in a more disguised form. I’m posting it here for posteriority since it also took me a while to figure it out.

Consider the following:

import pureconfig.ConfigSource
import pureconfig.generic.semiauto.deriveReader

case class Foo(a: Option[Int])

implicit val reader = deriveReader[Foo]
ConfigSource.string("{}").load[Foo]
// Right(Foo(None)), as expected.

However, if we happen to introduce an extra import:

import pureconfig.ConfigConvert._
import pureconfig.ConfigSource
import pureconfig.generic.semiauto.deriveReader

case class Foo(a: Option[Int])

implicit val reader = deriveReader[Foo]
ConfigSource.string("{}").load[Foo]
// Left(ConfigReaderFailures(ConvertFailure(KeyNotFound(a,Set()),Some(ConfigOrigin(String)),)))

The reason for this is that we have an implicit fromReaderAndWriter in ConfigConvert that produces a ConfigConvert from a ConfigReader and a ConfigWriter. When this implicit gets a higher priority, it will be used to produce a ConfigConvert[Option[Int]], which is also a ConfigReader[Option[Int]], and will therefore be used when deriving a ConfigReader[Foo]. However, the ConfigConvert[Option[Int]] won’t retain the ReadsMissingKeys type and will therefore be used to create a ConfigReader[Foo] with a different behavior from the default.

Read more comments on GitHub >

github_iconTop Results From Across the Web

pureconfig - Bountysource
ConfigReader.map is broken for readers with ReadsMissingKeys $ 0 ... Created 1 year ago in pureconfig/pureconfig with 4 comments. Consider: val Right(cfg) = ......
Read more >
Case Classes - PureConfig
How do keys in config objects map to field names of the case class? ... For ConfigReader s extending ReadsMissingKeys , a missing...
Read more >
Pureconfig read config as properties map - Stack Overflow
Pureconfig only takes these nested configs and maps them into case classes. ... implicit val strMapReader: ConfigReader[Map[String, ...
Read more >
PureConfig pureconfig Issues - Giters
Case class instance unexpectedly shared across map entries. Updated 5 months ago 3 ... ConfigReader.map is broken for readers with ReadsMissingKeys.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found