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.