Why does importing Doobie before Circe raise a compilation error?

49 views Asked by At

Just made a discovery while working with Doobie and Circe in the same file. Not my first time working with both libraries but never really paid attention to the order in which they are imported.

Scala, Doobie, and Circe versions.

val scalaVersion = "2.13.12"
val DoobieVersion = "1.0.0-RC1"
val CirceVersion = "0.14.5"

The following imports will fail for circe.

import doobie.util._
import io.circe._ // cannot resolve symbol circe

The following imports are OK.

import io.circe._
import doobie.util._

Please what is the reason behind this? As for some reasons someone might decide to import Doobie first and later import Circe in the same file, and would be wondering why the compiler is yelling.

However, doing the following helps the compiler to do the right import.

import doobie.util._
import _root_.io.circe._

Does the compiler see an io package from util and try to fetch circe from it?

Sorry if my question is newb-like.

Thanks for your explanation

1

There are 1 answers

0
Christopher Brinkley On BEST ANSWER

Yes, as the commenter suggested, doobie.util does indeed contain an object named "io" that is specific to doobie.

Having imported it, you now have the name "io" in your namespace. Namespaces are ways of managing unique names of things so the compiler can find them by name, so by definition, first in wins.

However, "io" is a very common name. And in general, order of imports can make a difference, because of the first-in-wins behavior.

If you realize you are trying to import from more than one source that contains the same name, one simple solution is to first import from the source that has the instance of the name that you want to use. So you can just put the Circe import first, if you don't need doobie.util.io.

A more general solution, which would also work if you did want to use doobie.util.io as well as Circe, would be to use an import alias that removes the naming conflict and thus prevents the "shadowing" of the name "io", e.g., in Scala 2:

import doobie.util.{io => doobieIo} 

Yet another possibility would be to import only the specific parts of doobie.util that you actually want, assuming that does not include "io".