scala macros - jfokus@adamwarski scala macros • written in scala • have access to and can...
TRANSCRIPT
![Page 1: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/1.jpg)
SCALA MACROS Adam Warski, Jfokus 2014
@adamwarski
![Page 2: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/2.jpg)
@adamwarski
WHAT WILL YOU FIND OUT?
• What is a macro? • How to write a macro?
• So … anybody uses macros?
• What’s “macro paradise”?
![Page 3: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/3.jpg)
@adamwarski
WHAT IS A MACRO?
• Macro (large): expands into something larger
• Function: code => code!• Invoked at build/compile-time
![Page 4: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/4.jpg)
@adamwarski
SCALA MACROS • Written in Scala
• Have access to and can manipulate the AST • Use compiler/reflection APIs
• Type-safe
![Page 5: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/5.jpg)
@adamwarski
MACROS IN OTHER LANGUAGES C/C++ – preprocessor
• #define BUFFER_SIZE 1024 • #define min(X, Y) ((X) < (Y) ? (X) : (Y))
Lisp/Clojure, Racket (Scheme)
• code is data (list) • quoting • “Since macros are so much harder to use than functions, a
good rule of thumb is: don't use defmacro if defun will work fine” from http://www.apl.jhu.edu/~hall/Lisp-Notes/Macros.html
![Page 6: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/6.jpg)
@adamwarski
MOTIVATION TO ADD MACROS TO SCALA
(it’s not a lean language already!)
ü Remove boilerplate
ü Replace run-time reflection
ü Generate type-checked code
ü Deep embedding of DSLs
ü Type-check external DSLs ü Simplify compiler in the long run
![Page 7: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/7.jpg)
@adamwarski
REACTIONS TO MACROS Mixed ;)
![Page 8: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/8.jpg)
@adamwarski
ABOUT ME During the day: coding @ SoftwareMill
SoftwareMill: a great software house! Afternoon: playgrounds, Duplo, etc.
Evening: blogging, open-source • Original author of Hibernate Envers • ElasticMQ, Veripacks, MacWire
http://www.warski.org
![Page 9: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/9.jpg)
@adamwarski
“DEF” MACROS
• Available since Scala 2.10 (Jan 2013)
• Only one type of many possible macro types
• Experimental status
![Page 10: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/10.jpg)
@adamwarski
WRITING A MACRO STEP-BY-STEP Goal – transform this:
debug(x*amount)
To:
println("x*amount = " + (x*amount))
So that it outputs: x*amount = 10.23
![Page 11: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/11.jpg)
DEMO
WRITING A SIMPLE MACRO
![Page 12: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/12.jpg)
@adamwarski
WHERE ARE MACROS USED? • Slick
• Akka • Async
• MacWire
examples are mostly from the projects’ websites
![Page 13: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/13.jpg)
@adamwarski
SLICK @table(name="COFFEES") case class Coffee(
@column(name="NAME") name: String,
@column(name="PRICE") price: Double
)
val q = Queryable[Coffee]
val r = q.filter(_.price > 3.0).map(_.name)
![Page 14: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/14.jpg)
@adamwarski
ASYNC val f1 = Future { beSlow(); 19 }
val f2 = Future { beSlow(); 23 }
val futureResult = for {
v1 <- f1
v2 <- f2
} yield v1 + v2
![Page 15: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/15.jpg)
@adamwarski
ASYNC val futureResult = async {
val f1 = async { beSlow(); 19 }
val f2 = async { beSlow(); 23 }
await(f1) + await(f2)
}
![Page 16: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/16.jpg)
@adamwarski
ASYNC val future = async {
val f1 = async { beSlow(); true }
val f2 = async { beSlow(); 42 }
if (await(f1)) await(f2) else 0
}
• Also possible with Akka Dataflow & CPS (but that’s so ’09) • Can also use for-comprehensions; but quickly gets tricky
![Page 17: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/17.jpg)
@adamwarski
ERRORS • Cryptic errors?
• Can be, if generated code doesn’t compile • But we can provide user-friendly errors
context.error(
c.enclosingPosition,
"You can’t do that")
![Page 18: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/18.jpg)
DEMO
MACWIRE
![Page 19: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/19.jpg)
@adamwarski
AND OTHERS! • Scala Pickling
• Akka 2.2 typed channels • ScalaMock
• Typesafe Logging
• Scala Blitz
• Expecty
• …
![Page 20: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/20.jpg)
@adamwarski
OTHER TYPES OF MACROS • Coming in Scala 2.12+
• Also available as a compiler plugin in 2.10/2.11 • Macro Paradise
based on the examples from http://scalamacros.org/
![Page 21: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/21.jpg)
@adamwarski
DEF MACROS • What we’ve seen so far
• Look like a method invocation • Generate code basing on:
• The parameters • Enclosing method/class • Implicit lookups
![Page 22: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/22.jpg)
@adamwarski
IMPLICIT MACROS • Useful for Type Classes
trait Showable[T] { def show(x: T): String }
def useShow[T](x: T)(implicit s: Showable[T]) = s.show(x)
implicit object IntShowable {
def show(x: Int) = x.toString }
![Page 23: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/23.jpg)
@adamwarski
IMPLICIT MACROS We want to provide a “default implementation” for a type
trait Showable[T] { def show(x: T): String }
object Showable {
implicit def materialize [T]: Showable[T] =
macro ...
}
We can get access to T at compile-time and generate what’s needed
![Page 24: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/24.jpg)
@adamwarski
MACRO ANNOTATIONS trait Foo {
def m1(p: Int): Long
def m2(p1: String, p2: Date): Double
}
class FooWrapper(@delegate wrapped: Foo)
extends Foo {
def m1(p: Int) = wrapped.m1(p)+1L
}
![Page 25: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/25.jpg)
@adamwarski
MACRO ANNOTATIONS Annotation-drive macros
• Any definitions can be annotated
class delegate extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro ???
}
![Page 26: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/26.jpg)
@adamwarski
MACRO ANNOTATIONS • Annottees is:
• Annotated class + companion object • Method parameter, owner, companion
• Can expand classes
• Can create companion objects
![Page 27: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/27.jpg)
@adamwarski
QUASIQUOTES • Similar to string interpolators • Extract from trees: val q"def $name[..$tparams](...$vparamss): $tpt
= $body” = methodTree
• Pattern match tree match {
case q"def $name[..$tps](...$vps): $tpt
= $body” =>
}
![Page 28: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/28.jpg)
@adamwarski
QUASIQUOTES Construct
• Terms: q"future{ $body }" • Types: tq"Future[$t]" • Cases: cq"x => x" • Patterns: pq"xs @ (hd :: tl)"
![Page 29: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/29.jpg)
@adamwarski
POTENTIAL PROBLEMS • Hard to write
• Code may be harder to understand • And to debug
![Page 30: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/30.jpg)
@adamwarski
WHEN TO WRITE A MACRO? • Always think it through
• Lots of repeated, boilerplate code • Unavoidable copy-paste (patterns)
• Library code
macro: power => responsibility
![Page 31: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/31.jpg)
@adamwarski
LINKS • http://www.warski.org/blog/2012/12/starting-with-scala-
macros-a-short-tutorial/
• http://scalamacros.org/ • http://slick.typesafe.com/
• https://github.com/scala/async
• https://github.com/adamw/macwire
• https://github.com/adamw/scala-macro-tutorial
![Page 32: SCALA MACROS - Jfokus@adamwarski SCALA MACROS • Written in Scala • Have access to and can manipulate the AST • Use compiler/reflection APIs • Type-safe](https://reader036.vdocuments.mx/reader036/viewer/2022070722/5f01dadc7e708231d4015cd1/html5/thumbnails/32.jpg)
@adamwarski
http://codebrag.com COME & GET A STICKER
Adam Warski @adamwarski