Add to your sbt (2.11, 2.12):
libraryDependencies += "io.scalaland" %% "catnip" % "1.1.1"
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross sbt.CrossVersion.patch)
or, if you use Scala.js:
libraryDependencies += "io.scalaland" %%% "catnip" % "1.1.1"
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross sbt.CrossVersion.patch)
or with Scala 2.13:
libraryDependencies += "io.scalaland" %% "catnip" % "1.1.1" // or %%% if cross compiling with Scala.js
scalacOptions += "-Ymacro-annotations"
From now on you can add implicit Kittens-generated type classes for your case classes with a simple macro-annotation:
import io.scalaland.catnip._
import cats._
import cats.implicits._ // don't forget to import the right implicits!
import alleycats.std.all._ // might also come handy
@Semi(Eq, Monoid, Show) final case class Test(a: String)
Test("a") === Test("b") // false
Test("a") |+| Test("b") // Test("ab")
Test("a").show // "Test(a = a)"
You can also test it with ammonite like:
import $ivy.`io.scalaland::catnip:1.1.1`, io.scalaland.catnip._, cats._, cats.implicits._
interp.load.plugin.ivy("org.scalamacros" % "paradise_2.12.4" % "2.1.1")
@Semi(Eq, Monoid, Functor) final case class Test[A](a: A)
Test("a") === Test("b") // false
Test("a") |+| Test("b") // Test("ab")
Test("1").map(_.toInt) // Test(1)
cats.Eq
, cats.PartialOrder
, cats.Order
,
cats.Functor
, cats.Foldable
, cats.Traverse
, cats.Show
,
cats.derived.ShowPretty
, cats.Monoid
, cats.MonoidK
,
cats.Semigroup
, cats.SemigroupK
, alleycats.Empty
, alleycats.Pure
.
Macro turns
@Semi(cats.Semigroup) final case class TestSemi(a: String)
@Semi(cats.SemigroupK, cats.Eq) final case class TestSemiK[A](a: List[A])
into
final case class TestSemi(a: String)
object TestSemi {
implicit val _derived_cats_kernel_Semigroup = cats.derived.semi.semigroup[TestSemi]
}
final case class TestSemiK[A](a: List[A])
object TestSemiK {
implicit val _derived_cats_SemigroupK = cats.derived.semi.semigroupK[TestSemiK];
implicit def _derived_cats_kernel_Eq[A](implicit cats_kernel_Eq_a: cats.kernel.Eq[List[A]]) = cats.derived.semi.eq[TestSemiK[A]]
}
In order to do so it:
[A]
is reuses TypeClass
to create an implicit TypeClass[A]
argumentShow
which would require additional type class
(shapeless.Typeable[A]
), they are defined in config after the generator
function and separated by commas
Therefore, you should be able to extend the abilities of the macro by expanding
the content of derive.semi.conf
. (Some merge strategy for resources I guess?
That and making sure that compiler sees the resources, since if you define them
in the same project you want compiler to use them it is not the case).
You should be able to extend the abilities of the macro by expanding
the content of derive.semi.conf
. You can create this file and add it to your library
if you want Catnip to support it as all files with that name are looked through during
compilation. When it comes to sbt it doesn't always export resources to Compile
scope,
so your configs might not be visible in your modules while they would be available
in created JARs. (Creating somewhat inconsistent experience).
Personally, I fixed this by adding something like
val myProject = project.in(file("my-project"))
// other settings
.settings(Compile / unmanagedResourceDirectories += baseDirectory.value / "../src/main/resources")
to sbt. This will make your customizations immediately available to your modules.
Take a look at an example project to see how customization works in practice.
To debug you can use catnip.debug
flag:
> sbt -Dcatnip.debug=debug # show info about derivation
> sbt -Dcatnip.debug=trace # show even more infor about derivation
Type checker complains if you use type aliases from the same compilation unit
type X = cats.Eq; val X = cats.Eq
@Semi(X) final case class Test(a: String)
// scala.reflect.macros.TypecheckException: not found: type X
same if you rename type during import
import cats.{ Eq => X }
@Semi(X) final case class Test(a: String)
// scala.reflect.macros.TypecheckException: not found: type X
However, if you simply import definitions or aliases already defined somewhere else, you should have no issues.