introduction aux macros

19
INTRODUCTION AUX MACROS @ahoy_Jon

Upload: jonathan-winandy

Post on 25-May-2015

535 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Introduction aux Macros

INTRODUCTION AUX MACROS

!@ahoy_Jon

Page 2: Introduction aux Macros

Jonathan Winandy !

‘BI Platform Engineer at Viadeo'

Page 3: Introduction aux Macros

LES MACROS ET MOI2012 : [ScalaDays London] “S’il n’y a rien de nouveau dans Scala, je vais finir par faire du Clojure …” -> MaKro2013 : “Atelier : Dans S’cas là”, utilisation des macros pour augmenter les tests unitaires. !

2014 : Type providers ? So 2008 !

Page 4: Introduction aux Macros

QU’EST CE QU’UNE MACRO ?

• Une macro est une “méthode” définie par l’utilisateur qui est appelée lors de la compilation.

• En Scala on a les def macro.

Page 5: Introduction aux Macros

UN EXEMPLE

!

!def assert(cond: Boolean, msg: Any) = macro Asserts.assertImpl!object Asserts { def assertImpl(c)(...,...) : ... = ????}!// ----------------- !!!!

assert(x < 10, "limit exceeded”)

assertImpl(c)(<[ x < 10 ]>, <[ “limit exceeded” ]>)

Page 6: Introduction aux Macros

CLOJURE - DEMO !(defn ifp [pred thenf elsef] "if in point fix style (= ((ifp even? inc dec) 2) 3)" (fn [a] (if (pred a) (thenf a) (elsef a)))) (defn reverse-at [n col] "(= (reverse-at 2 [1 2 3]) [2 1 3])" (let [[part1 part2] (split-at n col)] (concat (reverse part1) part2)))!(defmacro infix [form] (clojure.walk/postwalk (ifp seq? (partial reverse-at 2) identity) form))!;; (1 + 2) est une forme infix, normalement en Clojure on écrit :;; (+ 1 2)(def e (quote (infix ((+ partial 1) map [(1 + (2 * 3)) (2 - 3)]))))!(eval e) ;=> (8 0) ; List(8,0)(macroexpand e) ;=> (map (partial + 1) [(+ 1 (* 2 3)) (- 2 3)]);; c’est “l’AST” qui va être exécuté après l’expansion des macros

Page 7: Introduction aux Macros

!!Expr(Block(List(ValDef(Modifiers(), newTermName("a"), TypeTree(), Literal(Constant(2))

), ValDef(Modifiers(), newTermName("b"), TypeTree(), Literal(Constant(3))

)), Apply(Select(Ident(newTermName("a")), newTermName("$plus")), List(Ident(newTermName("b"))))

))!

{val a = 2val b = 3a + b

}

L’AST SCALA

Page 8: Introduction aux Macros

UN PEU PLUS INTERACTIF!

!On peut inspecter les expressions dans une session de REPL avec les commandes suivantes : !> import scala.reflect.runtime.{universe => u}!> u.showRaw( u.reify {{ val a = 2 ; val b = 3 ; a+b }})

!(showRaw ne suffit pas parfois!scalac -Xplugin macro-paradise_2.10.2-2.0.0-SNAPSHOT.jar -deprecation -Xprint:parser -Ystop-after:parser -Yshow-trees-compact *.scala!)!

Page 9: Introduction aux Macros

UTILISATION DANS LA NATURE

• Wartremover

• Datomisca

• Expecty

• Async

• MacWire

Page 10: Introduction aux Macros

WARTREMOVER! safe { def x[A](a: A) = a // x(100) x(())! 100 }! safe { // Won't compile: null is disabled val s: String = null }! safe { // Won't compile: var is disabled var x = 100 }

Page 11: Introduction aux Macros

DATOMISCA val queryFindActorsInTitle = Query(""" [ :find ?name :in $ ?title :where [?movie :movie/title ?title] [?actor :actor/acts-in ?movie] [?actor :actor/name ?name] ] """)

Page 12: Introduction aux Macros

EXPECTY

!case class Person(name: String = "Fred", age: Int = 42) { def say(words: String*) = words.mkString(" ")}!val person = Person()val expect = new Expecty()// Failing expectation!val word1 = "ping"val word2 = "pong"!expect { person.say(word1, word2) == "pong pong"}!/*Output:!java.lang.AssertionError:!person.say(word1, word2) == "pong pong"| | | | || | ping pong false| ping pongPerson(Fred,42)*/

Page 13: Introduction aux Macros

ASYNCdef combined: Future[Int] = async { val future1 = slowCalcFuture val future2 = slowCalcFuture await(future1) + await(future2)}

def combined: Future[Int] = for { r1 <- future1 r2 <- future2} yield r1 + r2

Page 14: Introduction aux Macros

MACWIREclass DatabaseAccess()class SecurityFilter()class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)class UserStatusReader(userFinder: UserFinder)!trait UserModule { import com.softwaremill.macwire.MacwireMacros._! lazy val theDatabaseAccess = wire[DatabaseAccess] lazy val theSecurityFilter = wire[SecurityFilter] lazy val theUserFinder = wire[UserFinder] lazy val theUserStatusReader = wire[UserStatusReader]}

Page 15: Introduction aux Macros

DEMO MACRO

Page 16: Introduction aux Macros

import language.experimental.macros!import reflect.macros.Context!!case class Query(s:String)!object TreeCleaner { def query(s:String):Query = macro queryValidationImpl! def queryValidationImpl(c:Context)(s:c.Expr[String]) = { import c.universe._ val Literal(Constant(s_query: String)) = s.tree println(showRaw(s)) println(s_query) reify ( new Query(s.splice) ) }

https://github.com/ahoy-jon/demoMacro

Page 17: Introduction aux Macros

UN PEU PLUS AVANCÉ …

Page 18: Introduction aux Macros

QUASIQUOTING (MACROPARADISE OU SCALA 2.11)

import language.experimental.macrosimport reflect.macros.Contextimport scala.annotation.StaticAnnotationimport scala.reflect.runtime.{universe => ru}import ru._

val e = q"def x = 4”e: reflect.runtime.universe.DefDef = def x = 4val q"def $m = $v" = em: reflect.runtime.universe.Name = xv: reflect.runtime.universe.Tree = 4

https://github.com/squito/learn_macrossbt/sbt "project macrotests" console

Page 19: Introduction aux Macros

FAKE TYPE

PROVIDER

import scala.annotation.StaticAnnotationimport scala.language.experimental.macrosimport scala.reflect.macros.Context class body(tree: Any) extends StaticAnnotation object Macros { def makeInstance = macro makeInstance_impl! def makeInstance_impl(c: Context) = c.universe.reify[Any] { class Workaround { def z: Int = 13 @body(42) def v: Int = macro Macros.selectField_impl } new Workaround {} } def selectField_impl(c: Context) = c.Expr( c.macroApplication.symbol.annotations.filter( _.tpe <:< c.typeOf[body] ).head.scalaArgs.head ) }!val myInstance = Macros.makeInstancemyInstance: AnyRef{def z: Int; def v: Int} = $anon$1@35d39d01!myInstance.zres7: Int = 13!myInstance.vres8: Int = 42!

http://meta.plasm.us/posts/2013/07/12/vampire-methods-for-structural-types/