.center.middle .x6[ Macros ] .x3[Without] .huge[ Parentheses ]
Luc Fueston slides linked from post on ### mrluc.github.com --- .center.middle ## I'm from .x2[MN] ## But I live half of each year in .lg[South America.] --- .center ### No waves = .x2[Lisp-style macros] for .x2[CoffeeScript] .sm[in < 100 lines] ![ hammock driven development ](./img/ecu_code.jpg) --- # Not because I'm a wise Lisp Greybeard. # Because it's fun, and ## I ♥ Ruby and CoffeeScript, and .x2[macros are what you miss about Lisp.] --- # Not because I'm a wise Lisp Greybeard. # Because it's fun, and ## I ♥ Ruby and CoffeeScript, and .x2[macros are what you miss about Lisp.] .center.x2[ `.lisp (setf x 4)` ] .x4[ When you inevitably] .x6[ leave. ] --- .middle # 1. What are macros? # 2. Introducing macros.coffee # 3. The view from Ruby --- .center.middle macros are # Named AST transforms non-snarky reminder: # String -> Tokens -> AST --- .center.middle # So, if you want macros. ## What do you do to the AST? --- .center.middle # Do Two Things to the AST ![Macro Stage Visualized](img/macros_diagram.png) ## Find and compile named definitions ## Apply definitions to calls --- .center.middle # So, once you have macros. ## What do you do with them? --- .middle # 1. Domain Specific Languages # 2. Control Flow --- # Domain-Specific Languages Macros can be used for the same purpose as macro methods. .ruby obj = fighter do acts_like :beserker attack do flail_wildly do end # if Ruby had macros, might compile to: obj = Fighter.new.instance_exec { include get_behavior_module(:beserker) def attack() super @enemy, :flail_wildly end self } --- # Control Flow Though Experiment ## Compile-time > run-time for control structures. def iif(cond, ok, notok) if cond ok.call() # slowness d'oh! else notok.call() # slowness d'oh! end end iif 2 > 1, ->{ p "success!"}, # user d'oh! ->{ p "humbuggery!"} # user d'oh! # and 2 lambdas per 'elseif' ## Macros have no inherent runtime penalty. --- .middle # Know what you want to say? # Know what you want it to mean? # Both expressions parse?
# .x2[Macros can do that.] --- .x3.center.middle in coffeescript --- .center.middle .lg[macros.coffee is] #### a straightforward implementation that works as described ![Macro Stage Visualized](img/macros_diagram.png) --- .middle ## .center.x2[ Not a fork] # .center[ Wraps CoffeeScript, does two things ] nodes: (str, strict=@strict)=> @ast = CoffeeScript.nodes str if !strict or uses_macros(@ast) @find_and_compile_macros() #1 definitions @macroexpand() until @all_expanded() #2 calls @ast --- .center.middle 87 lines Annotated source with Docco ![docco](img/implementation.jpg) --- .middle.center .x2[`npm install macros.coffee`] Boy, it would be embarrassing if that didn't work right now. --- # Require it, then require files with macros ## No macros in the entry file require 'macros.coffee' app = require 'my-app' #now it hooks nodejs require() app.start() ## Declaration in files with macros .coffeescript 'use macros' mac instant_2_plus_2 (n)-> backquote {z:2}, quote -> z + z instant_2_plus_2() --- .middle # Namespaces are coming (easy) ## They'll be declarations too: .x2[ `.ruby 'use macros.name'` ] .x2[ `.ruby 'in macros.name'` ] --- .middle.center Debugging is ... interesting. Compiling works, but is meh. jashenkas could break AST deepcopy with a single well-placed closure don't have a 'vision' yet for lots of biggish questions. --- .x2.middle.center # what good is it? --- .x2.middle if you're doing: ### language experiments ### ast mangling (control flow) --- .big ## .center[ QUIZ ] x = 37 x for x in [1..10] console.log x (a) `.coffeescript x is 37` (b) `.coffeescript x is 10` --- .big ## .center[ QUIZ ] x = 37 x for x in [1..10] console.log x
(a) `.coffeescript x is 37`
(b) `.coffeescript x is 10`
(c) `.coffeescript x is 11` --- .middle # gensyms to the rescue ## 'para': list comprehensions in spanish .coffeescript x = 37 para .hl[x] en [1..10] si (.hl[x] > 3) hazte eso pues -> .hl[x] x is 37 inside the loop .javascript for (_i = 0, _len = _ref.length; _i < _len; _i++) { .hl[x_g9] = _ref[_i]; if (.hl[x_g9] > 3) { .hl[x_g9]; } } --- .middle.big # async control flow .coffeescript if x = async_fn .hl[cc()] p x else p "Onoz" --- .middle.big ## `cc` is a 20-line macro .javascript agreet(function(__thang) { var x; if (x = __thang) { p(x); } else { p('Ohnoz'); } }); --- .middle.big # If you mangle the AST, # why not use an abstraction built for it? --- .middle.big # Closure. # Compilation. ## two abstractions that work great together --- .big # Macros only work in: - Lisps and '()-like languages, including Clojure --- .big # Macros only work in: - Lisps and '()-like languages, including Clojure - MetaLua - Potion (_why's language) - __Factor__ - __Julia__ - others ## Languages where macros turned out to be a mistake: --- .big # Macros only work in: - Lisps and '()-like languages, including Clojure - MetaLua - Potion (_why's language) - __Factor__ - __Julia__ - others ## Languages where macros turned out to be a mistake: - Maybe CoffeeScript, now. --- .big # Ruby Issues - _RubyMacros_ - real macros - use a special require - requires RedParse - about 2 years old - _rbx transforms_ - GoGaRuCo 2011 lightning talk on this. Yehuda seems interested in them. - Rubinius only - no one has written actual macros yet - but you could totally do it - __Ruby needs canonical representation.__ --- .middle.big # Books on Macros ## Free - _On Lisp_ - pg - _Let Over Lambda_ - Hoyte ## Non-free - _PAIP_ - Norvig --- .center.middle no way there's time for # .x2[ Questions? ] superluc@gmail.com github.com/mrluc/macros.coffee
Thanks Ruby.mn ♥