Search code examples
scalascala-cats

ambiguous implicit values: match expected type cats.derived.MkShow[A]: show cats:kittens


I'm trying to create Show Instance for my custom Config class.

The build.sbt file is -

    name := "circe-demo"

    version := "0.1"

    scalaVersion := "2.11.12"

    resolvers += Resolver.bintrayRepo("ovotech", "maven")

    libraryDependencies += "io.circe" %% "circe-core" % "0.11.0"
    libraryDependencies += "io.circe" %% "circe-parser" % "0.11.0"
    libraryDependencies += "io.circe" %% "circe-generic" % "0.11.0"
    libraryDependencies += "org.typelevel" %% "kittens" % "1.2.0"


    libraryDependencies ++= Seq(
    "is.cir" %% "ciris-cats",
    "is.cir" %% "ciris-cats-effect",
    "is.cir" %% "ciris-core",
    "is.cir" %% "ciris-enumeratum",
    "is.cir" %% "ciris-refined"
    ).map(_ % "0.12.1")

Complete code is -

    import enumeratum.{Enum, EnumEntry}

    sealed abstract class AppEnvironment extends EnumEntry

    object AppEnvironment extends Enum[AppEnvironment] {
    case object Local extends AppEnvironment
    case object Testing extends AppEnvironment
    case object Production extends AppEnvironment

    override val values: Vector[AppEnvironment] =
        findValues.toVector
    }

    import java.net.InetAddress
    import scala.concurrent.duration.Duration

    final case class ApiConfig(host: InetAddress, port: Int, apiKey: String, timeout: Duration)

    import java.net.InetAddress
    import cats.Show
    import cats.derived.semi
    import ciris.config.loader.AppEnvironment.{Local, Production, Testing}
    import enumeratum.EnumEntry
    import eu.timepit.refined.auto._
    import eu.timepit.refined.types.string.NonEmptyString
    import scala.concurrent.duration._

    final case class Config(appName: NonEmptyString, environment: AppEnvironment, api: ApiConfig)

    object Config {
    implicit val showConfig: Show[Config] = {
        implicit val showDuration: Show[Duration] =
        Show.fromToString

        implicit val showInetAddress: Show[InetAddress] =
        Show.fromToString

        implicit def showEnumEntry[E <: EnumEntry]: Show[E] =
        Show.show(_.entryName)

        // Show.show[Config](x => s"api = ${x.api} appName = ${x.appName} environment ${x.environment}")
        semi.show
        }
    }

semi.show in the above code throws the below exception -

    [error] /Users/rajkumar.natarajan/Documents/Coding/kafka_demo/circe-demo/src/main/scala/ciris/config/loader/Config.scala:32:5: ambiguous implicit values:
    [error]  both value emptyProductDerivedShow in trait MkShowDerivation of type => cats.derived.MkShow[shapeless.HNil]
    [error]  and method emptyCoproductDerivedShow in trait MkShowDerivation of type => cats.derived.MkShow[shapeless.CNil]
    [error]  match expected type cats.derived.MkShow[A]
    [error]     show
    [error]     ^
    [error] one error found
    [error] (Compile / compileIncremental) Compilation failed
    [error] 

I'm new to functional programming using cats. How can I resolve this exception.


Solution

  • Unfortunately error reporting when such complicated implicits and macros are involved is far from perfect. The message you see actually means that some required implicits for the real generator (MkShow.genericDerivedShowProduct in this case) have not been found and the search went back to some where basic stuff where there is an ambiguity. And the stuff that is missing is mostly very basic such as an implicit for Show[Int] or Show[String]. The simplest way to get them all is to import cats.implicits._ but that will also bring catsStdShowForDuration which is a Show[Duration]. But since it's implementation is really the same as your custom one, it is easier to remove your custom one. One more thing that is missing is Show[NonEmptyString] and it is easy to create one

      implicit def showNonEmptyString: Show[NonEmptyString] = Show.show(nes => nes)
    

    To sum up, when I define your showConfig as

    implicit val showConfig: Show[Config] = {
      import cats.implicits._
    
      // is already defined in cats.implicits._
      //implicit val showDuration: Show[Duration] = Show.fromToString
    
      implicit val showInetAddress: Show[InetAddress] = Show.fromToString
    
      implicit def showEnumEntry[E <: EnumEntry]: Show[E] = Show.show(_.entryName)
    
      implicit def showNonEmptyString: Show[NonEmptyString] = Show.show(nes => nes)
    
      // Show.show[Config](x => s"api = ${x.api} appName = ${x.appName} environment ${x.environment}")
      semi.show
    }
    

    it compiles for me.

    P.S. is there any good reason why you put your AppEnvironment under ciris.* package? I'd say that generally putting your custom code into packages of 3-rd party library is an easy way to mess things up.