Jakub Kozłowski - Scala Developer @ Scalac
To learn...
how to avoid writing boilerplate
how to avoid runtime overhead
how to add additional static checks
...with macros
Metaprogramming
Code that writes code
During the compilation
used as an annotation
@typeclass trait Numeric[T]{
@op("+") def add(a: T, b: T): T
}
e.g. adds methods, typeclass helpers
currently only with macro-paradise
used as a function
val result = cached (15.minutes) {
expensiveCall(args)
}
can transform its arguments' ASTs
Syntax representation in compilers
Abstract, independent of grammar
println("hello, world")
case class Person(name: String, age: Int, hometown: String){
def toJson = JsonObject(
"name" -> name,
"age" -> age.toString,
"hometown" -> hometown
)
}
object Person{
def fromJson(json: JsonObject) = Person(json[String]("name"), ...)
}
val person = Person("Joe", 26, "NYC")
val personJson = person.toJson
//...
val person2 = Person.fromJson(personJson)
val person = Person("Joe", 26, "NYC")
val personJson = person.toJson
//...
val person2 = personJson.convertTo[Person]
That's all (with the right macro-based library)
//given these classes...
case class Address(street: String, city: String, postcode: String)
case class Person(name: String, age: Int, address: Address)
//update person's address's street
val person = Person("Joe", 18, Address("Long", "NYC", "99999"))
//omg
val newPerson =
person.copy(
address = person.address.copy(
street = person.address.street + " st."
)
)
Use Lens (e.g. from shapeless)
//given these classes...
case class Address(street: String, city: String, postcode: String)
case class Person(name: String, age: Int, address: Address)
//update person's address's street
val person = Person("Joe", 18, Address("Long", "NYC", "99999"))
import shapeless._
val streetLens = lens[Person].address.street
val newPerson = streetLens.modify(person)(_ + " st.")
Creating generic array of elements
def createArray[T: ClassTag](size: Int, el: T) = {
val a = new Array[T](size)
for (i <- 0 until size) a(i) = el
a
}
Uses boxing
You can make use of specialization...
def createArray[@specialized T: ClassTag](size: Int, el: T) = {
val a = new Array[T](size)
for (i <- 0 until size) a(i) = el
a
}
...which is viral and heavyweight
def createArray[T: ClassTag](size: Int, el: T) = {
val a = new Array[T](size)
def specBody[@specialized U](ell: U) {
for (i <- 0 until size) a.asInstanceOf[Array[U]](i) = ell
}
classTag[T] match {
case ClassTag.Int => specBody(el.asInstanceOf[Int])
...
}
a
}
boilerplate!!!!111
def specialized[T: ClassTag](code: Any) = macro ...
def createArray[T: ClassTag](size: Int, el: T) = {
val a = new Array[T](size)
specialized[T] {
for (i <- 0 until size) a(i) = el
}
a
}
Generates specialized code...
without specializing the whole call chain
What's wrong with this code?
def future[T](body: => T) = ...
def receive = {
case Request(data) =>
future {
val result = transform(data)
sender ! Response(result)
}
}
Sender can change
def future[T](body: Spore[T]) = ...
def spore[T](body: => T): Spore[T] = macro ...
def receive = {
case Request(data) =>
//here
future(spore {
val result = transform(data)
sender ! Response(result) // won't compile
})
}
The spore macro will analyze free variables
and fail on suspicious behavior
"org.scala-lang" % "scala-reflect" % "2.11.8"
Compile first, use later
import scala.reflect.macros.blackbox
(or whitebox)
a compiler plugin if using macro paradise
Macros need to be compiled before you use them
e.g.
sbt console
blackbox - returned type is exactly as declared
whitebox - can return a type more specific than declared
def hello: Unit = macro helloImpl
def helloImpl(c: blackbox.Context): c.Expr[Unit] = {
import c.universe._
reify {
println("hello!")
}
}
Write a macro that transforms a function
into its derivative
f(x) = -2(x2 + 4x - 5)
derive(f) = f'(x) = -4x - 8
opportunity for writing cryptic code
basically (type-safe) code generation
still code - potential bugs
compilation overhead
not trivial to debug
Trivial client usage
Not rocket science
macros will be dropped
replaced by scala.meta
easier to write macros
better tooling support (e.g. IDEs)
no whitebox macros
code rewriting in Dotty
Yes, certainly
only when you absolutely need to
We've learned:
what macros are
how they help
how they don't
how to write them
Questions!
Slides: kubukoz.github.io/macro-sourcery-slides
Code: github.com/kubukoz/macro-sourcery-code
Contact me:
Created by Hakim El Hattab / @hakimel
reveal.js enables you to create beautiful interactive slide decks using HTML. This presentation will show you examples of what it can do.
Slides can be nested inside of each other.
Use the Space key to navigate through all slides.
Nested slides are useful for adding additional detail underneath a high level horizontal slide.
That's it, time to go back up.
Not a coder? Not a problem. There's a fully-featured visual editor for authoring these, try it out at http://slides.com.
Press ESC to enter the slide overview.
Hold down alt and click on any element to zoom in on it using zoom.js. Alt + click anywhere to zoom back out.
Presentations look great on touch devices, like mobile phones and tablets. Simply swipe through your slides.
Hit the next arrow...
... to step through ...
... a fragmented slide.
There's different types of fragments, like:
grow
shrink
fade-out
current-visible
highlight-red
highlight-blue
You can select from different transitions, like:
None -
Fade -
Slide -
Convex -
Concave -
Zoom
reveal.js comes with a few themes built in:
Black (default) -
White -
League -
Sky -
Beige -
Simple
Serif -
Blood -
Night -
Moon -
Solarized
Set data-background="#dddddd"
on a slide to change the background color. All CSS color formats are supported.
<section data-background="image.png">
<section data-background="image.png" data-background-repeat="repeat" data-background-size="100px">
<section data-background-video="video.mp4,video.webm">
Different background transitions are available via the backgroundTransition option. This one's called "zoom".
Reveal.configure({ backgroundTransition: 'zoom' })
You can override background transitions per-slide.
<section data-background-transition="zoom">
function linkify( selector ) {
if( supports3DTransforms ) {
var nodes = document.querySelectorAll( selector );
for( var i = 0, len = nodes.length; i < len; i++ ) {
var node = nodes[i];
if( !node.className ) {
node.className += ' roll';
}
}
}
}
Code syntax highlighting courtesy of highlight.js.
Item | Value | Quantity |
---|---|---|
Apples | $1 | 7 |
Lemonade | $2 | 18 |
Bread | $3 | 2 |
These guys come in two forms, inline:
“The nice thing about standards is that there are so many to choose from”
and block:
“For years there has been a theory that millions of monkeys typing at random on millions of typewriters would reproduce the entire works of Shakespeare. The Internet has proven this theory to be untrue.”
You can link between slides internally, like this.
There's a speaker view. It includes a timer, preview of the upcoming slide as well as your speaker notes.
Press the S key to try it out.
Presentations can be exported to PDF, here's an example:
Set data-state="something"
on a slide and "something"
will be added as a class to the document element when the slide is open. This lets you
apply broader style changes, like switching the page background.
Additionally custom events can be triggered on a per slide basis by binding to the data-state
name.
Reveal.addEventListener( 'customevent', function() {
console.log( '"customevent" has fired' );
} );
Press B or . on your keyboard to pause the presentation. This is helpful when you're on stage and want to take distracting slides off the screen.