简体   繁体   中英

Importing required external implicits or merging implicits to get one common import

I am creating a library to handle fixed-length using Scala.

For encoding and decoding the strings I use type class based system. I have provided my own Read[A] and Write[A] for handling this actions.

My Write type class is using Show from Cats under the hood. It works, but it requires the user to explicitly import cats' implicits, like:

import com.github.atais.util.Read._
import cats.implicits._
import com.github.atais.util.Write._

the example could be seen on the Github project:
https://github.com/atais/Fixed-Length/blob/702d7d242e5b1f6e1c6b581ad7356f36ca6ed8d9/src/test/scala/com/github/atais/fixedlength/simple/CodecTest.scala

Is there any walk-around? I would like to hide the cats import or (if possible) merge all three into a single implicit object.

cats stores typeclass instances inside special traits that you can extend to turn your objects into providers themselves. Instances are grouped by type however, so you can take all instances for Int , AnyVal or List , but not "anything with Show ".

For example, you can extend AnyValInstances , so this will compile (in a worksheet)

import cats.Show
import cats.instances.AnyValInstances


trait Write[A]

object Write extends AnyValInstances {
    implicit def writeForCatsShow[A](implicit s: Show[A]): Write[A] = new Write[A] { }
}

import Write._

implicitly[Write[Int]] // Show[Int] is in scope

There is a caveat, however. Importing instances from cats will cause compilation error due to Show[Int] being ambiguous:

import Write._
import cats.implicits._

// implicitly[Write[Int]] // this will fail to compile b/c we have Show[Int] twice

My preference would be to write some boilerplate code to avoid burdening users with imports at all.

object file1 {
  // pretend it's a different file
  import cats.Show
  import cats.implicits._ 

  trait Write[A]

  object Write {

    // implement generic Show <-> Write here
    private def fromShow[A](implicit s: Show[A]): Write[A] = new Write[A] { }

    // manually create instances for types you need
    implicit val intWrite  = fromShow[Int]
    implicit val longWrite = fromShow[Long]
    implicit val doubleWrite = fromShow[Double]
    implicit val stringWrite = fromShow[String]

    // BONUS: define instances for other types that have Show
    // without causing any ambiguities
    implicit def listWrite[A](implicit wa: Write[A]): Write[List[A]] = ???
  }
}

object file2 {
  import file1.Write

  object Methods {
        def testWrite[A](a: A)(implicit write: Write[A]) = 42
    }
}

object file3 {
  import file2.Methods

  // We don't need to import cats.Show OR Write at all
  // because Show instances are resolved in object Write code
  // and Scala automatically looks up Write[A] instances in
  // companion object, so Write[Double] will be picked
  def main(args: Array[String]) = {
        println(Methods.testWrite(2.11))
    }
}

Runnable version of above code is available here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM