Introduction aux Macros

748 views
605 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
748
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
15
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Introduction aux Macros

  1. 1. INTRODUCTION AUX MACROS ! @ahoy_Jon
  2. 2. Jonathan Winandy ! ‘BI Platform Engineer at Viadeo'
  3. 3. LES MACROS ET MOI 2012 : [ScalaDays London] “S’il n’y a rien de nouveau dans Scala, je vais finir par faire du Clojure …” -> MaKro
 
 2013 : “Atelier : Dans S’cas là”, utilisation des macros pour augmenter les tests unitaires. ! 2014 : Type providers ? So 2008 !
  4. 4. 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.
  5. 5. 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” ]>)
  6. 6. 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
  7. 7. ! ! 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")))) ) ) L’AST SCALA { val a = 2 val b = 3 a + b }
  8. 8. 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 ! ) !
  9. 9. UTILISATION DANS LA NATURE • Wartremover • Datomisca • Expecty • Async • MacWire
  10. 10. 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 }
  11. 11. DATOMISCA val queryFindActorsInTitle = Query(""" [ :find ?name :in $ ?title :where [?movie :movie/title ?title] [?actor :actor/acts-in ?movie] [?actor :actor/name ?name] ] """)
  12. 12. ! 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 pong Person(Fred,42) */ EXPECTY
  13. 13. ASYNC def 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
  14. 14. MACWIRE class 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] }
  15. 15. DEMO MACRO
  16. 16. 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
  17. 17. UN PEU PLUS AVANCÉ …
  18. 18. QUASIQUOTING (MACROPARADISE OU SCALA 2.11) https://github.com/squito/learn_macros sbt/sbt "project macrotests" console import import import import import language.experimental.macros reflect.macros.Context scala.annotation.StaticAnnotation scala.reflect.runtime.{universe => ru} ru._
 val e = q"def x = 4” e: reflect.runtime.universe.DefDef = def x = 4 
 val q"def $m = $v" = e m: reflect.runtime.universe.Name = x v: reflect.runtime.universe.Tree = 4
  19. 19. import scala.annotation.StaticAnnotation import scala.language.experimental.macros import 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 ) FAKE TYPE PROVIDER } ! val myInstance = Macros.makeInstance myInstance: AnyRef{def z: Int; def v: Int} = $anon$1@35d39d01 ! myInstance.z res7: Int = 13 ! myInstance.v res8: Int = 42 ! http://meta.plasm.us/posts/2013/07/12/vampire-methods-for-structural-types/

×