doobie icon indicating copy to clipboard operation
doobie copied to clipboard

doobie.implicits.javatime includes time/date formats that can conflict

Open drewboardman opened this issue 4 years ago • 4 comments

I have a class

// This exists in an internal library
case class CustomDateTime(v: ZonedDateTime) {
  def someConversion: NewType = ???
}

// This is in my application
object SomewhereInMyApplication {

  private def toOffsetDateTime(cdt: CustomDateTime): OffsetDateTime =
    OffsetDateTime.ofInstant(cdt.timestamp.toInstant, ZoneId.of("UTC"))

  private def fromOffsetDateTime(ofdt: OffsetDateTime): CustomDateTime = {
    CustomDateTime(ofdt.toZonedDateTime)
  }

  implicit val cDateTimePut: Put[CustomDateTime] = Put[OffsetDateTime].tcontramap(toOffsetDateTime)
  implicit val cDateTimeGet: Get[CustomDateTime] = Get[OffsetDateTime].map(fromOffsetDateTime)
}

When writing a larger type T to the DB that includes a parameter of type CustomDateTime, it's easy to get into tricky implicit scoping scenarios with doobie.implicits.javatime._, since that includes many different time formats.

It would be useful to ensure that the OffsetDateTime instance is available to be imported without importing the ZonedDateTime instance.

drewboardman avatar Dec 14 '20 18:12 drewboardman

Hi do you still get the issue if you put the Put/Get instances into the CustomDateTime companion object?

(Even if you import it explicitly it should still work fine. A simple repo to demonstrate the issue would help)

jatcwang avatar Dec 15 '20 11:12 jatcwang

Hi do you still get the issue if you put the Put/Get instances into the CustomDateTime companion object?

(Even if you import it explicitly it should still work fine. A simple repo to demonstrate the issue would help)

This was what my solution was.

drewboardman avatar Dec 15 '20 16:12 drewboardman

So I think this is resolved? I couldn't quite figure out whether there's an actual issue because what you were describing in the opening post doesn't really fit my understanding of how implicit resolution work (which is why I asked for a small reproduction).

If an implicit for a type exists in scope (either through implicit scope from companion object or explicitly imported) it should always be used by the compiler.

jatcwang avatar Dec 16 '20 10:12 jatcwang

Context: https://gitter.im/tpolecat/doobie?at=5fd78c783646a85814ebb046

I think the issue is that if the custom implicit isn't defined on the companion object, but is imported, that it seems to prefer picking the derived instance instead of the custom one. I'm not fully sure.

The wider point is that right now, postgres users can't import the OffsetDateTime instance without importing the invalid-for-postgres ZonedDateTime instance, unless they import a single named field. That's pretty awful for UX, especially since the docs and all material you can find will point you to use the wildcard imports

Daenyth avatar Dec 16 '20 13:12 Daenyth