Glenn Vanderburg presented on Metaprogramming Ruby. I thought it was quite interesting as I had only done this previously in ANSI Common Lisp. My notes from the talk are below.
Metaprogramming (MP)
- programming your programming
- change the way you program in your programming lang
- transform your general-purpose lang
- make it domain specific
- program ing a lang designed for the problem you’re solving
Lisp
- origination of metaprog
Ruby
- MP is rediscovered
- style and idioms are changing and adapting
- rails leverages mp heavily (to great effect)
- ruby is a natural for MP
Why?
- Dynamic and reflective
- everything is open to change
- blocks allow writing new control structs (add continuations to get fancy)
- Most declarations are executable statements
- only slightly less malleable than Lisp (no macros)
- a fantastic syntax
- neutral and unobtrusive
- enough to distinguish different kinds of constructs
- not enough to complicate straightforward statements
Built-in Examples
- declaring object properties: attr_reader :id, :age attr_writer :name attr_accessor :color
- These are just methods from Module (not syntax)
- How are they written? Unfortunately, in C. Still instructive but doesn’t tell you how to do it in Ruby.
Mathieu Bouchard’s X11 Library
- Wrote a DSL which defined the X11 Protocol Spec in a way similar to the way in which the spec itself was written.
Styles Have Changed
- A DSL was created in similar fashion for the Java Debug Wire Protocol.
- executable protocol spec which could be printed from source
Dave Thomas’s Summer Project
- RubyConf 2002 “How I spent my summer vacation”
- Created a DSL which would build objects based on the contents of a DB or generate the DDL for the database or generate the graph code for an ER diagram.
How to think about metaprogramming
- Def new constructs for your programming lang
- Ok, but constructs to do what?
- Whatever your DSL needs to do.
Another way to think about it
- a new bag of tricks for eliminating duplication (and other smells) from your code
- two ways to go about it: write your app and extract (Rails), layout design first (ActionStep).
Conventional Constructs
- DSLs need: types, literals, declaration, expressions, operators, statements, control structs
Most DSLs also deal with things you don’t usually find in gp langs
- context-dependence
- commands and sentences
- units (unit conversion)
- large vocabularies ( sometimes unbounded)
- hierarchy
Contexts
Establish a context for a set of statements:
- constrain those statements to the context
- multiple, concise operations on the context
What’s a context? A scope.
Commands & Sentences
- multipart, complex statements or declarations
field(autoinc, :reg_id, pk)
- It’s just a method call (optional parens are handy for sentences
- First param is a method call: restricts the arguments to predefined alternatives (prevent errors)
- Second param is a symbol: restrict syntax to goo name-like strings
- Additional params are method calls:
- options, constraints
- methods return objects that encode constraints
Units
- general purpose langs deal with scalars
- most DSLs deal with quantities
Impl Units
Classes representing quantities
- don’t forget to support math
- user operator overloading if it makes sense
- may require mixed-based arithmetic (time certainly does)
Next: natural expression
Large Vocab
- Roman numerals (Roman.CCXX, Roman.XLII)
- Jim Weirich’s XmlMarkup class (used in Rails):
How do you impl large vocabularies?
- override method_missing
- Be careful! Bugs lurk here. Sometimes method missing intercepts something that really is a name in an outer scope — it’s also hard to find bugs if you misspell a real method name.
Hierarchy
- XmlMarkup again (see slides)
How to implement?
- call from method_missing:
You could use instance_eval to avoid typing xm. before every call. But don’t. If you make a method call within the block and use instance_eval, the method is looked up in the wrong context and get a no such method error. Slides are available here.