Samurai
SAM-like macro annotation for Scala
Note this project is experimental and probably doesn't cover all cases correctly.
You can use compiler-supported SAM even in Scala 2.11 with
-Xexperimental
flag.This project will not be loger maintained.
Scala 2.12 introduced simplified syntax for defining instances of classes/traits that contain single abstract method. Considering following interface:
trait Show[T] {
def show(x: T): String
}
Instead of
val intShow: Show[Int] = new Show[Int] {
def show(x: Int): String = x.toString
}
we can simply write
val intShow: Show[Int] = (x: Int) => x.toString
Unfortunately, in Scala 2.11 such a syntax is not possible and for every type we have to either define separate helper function or live with this boilerplate. It's possible with -Xexperimental
.
Samurai library defines macro annotation called @sam
that brings this nice syntax also Scala 2.11.
Setup
You have to add following lines to your build.sbt
:
libraryDependencies += "io.scalaland" %% "samurai" % "1.1" // triple %%% if you're on Scala.js
resolvers += Resolver.sonatypeRepo("releases")
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
Usage
Just prepend your val
/def
definitions with @sam
annotation.
import samurai._
@sam val intShow: Show[Int] = (x: Int) => x.toString
This will expand in compile-time to the following piece of code:
val intShow: Show[Int] = new Show[Int] {
def show(x: Int): String = x.toString
}
Advanced example
import samurai._
@sam implicit val intShow: Show[Int] = (x: Int) => x.toString
@sam implicit val intStr: Show[String] = (x: String) => x
@sam implicit def tupleInst[A: Show, B: Show]: Show[(A, B)] =
(p: (A, B)) => s"(${implicitly[Show[A]].show(p._1)}, ${implicitly[Show[B]].show(p._2)})"
Last definition will be expanded to something similar to:
implicit def tupleInst[A, B](implicit s1: Show[A], s2: Show[B]): Show[(A, B)] =
new Show[(A, B)] {
def show(s: (A, B)): String = s"(${s1.show(p._1)}, ${s2.show(p._2)})"
}
Quirks
I observed that this approach might not work for every local type definition, because of the quirks of scala reflection API, so recommended approach is to define all your traits/abstract classes that you want to instantiate with @sam
annotation as top-level ones.
Cross compilation
The library is cross-compiled on 2.11 and 2.12. On Scala 2.11 it performs real expansion described above, while on 2.12 it relies on native support provided by compiler, so it actually doesn't transform the original definition in any way.