-
-
Save dissolved/1893514 to your computer and use it in GitHub Desktop.
The code of the talk from my Feb 22nd Arlington Ruby talk 'There is No Such Thing as Metaprogramming'.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is the code from my 'There is No Such Thing as Metaprogramming' talk, | |
# which premiered at the Arlington, VA Ruby Users Group on Feb 22nd. | |
# Without the deliver and walk-through to the solution below this example | |
# will be missing quite an important bit of content (mainly the tracking of | |
# 'self' while developing the solution, but it still a useful read. | |
# Here is the Toddler with no metajuju. Note that the developer, as well as | |
# the code, is completely unuaware of the interpreter. A developer with a | |
# background in compiled languages would be comfortable looking at this. | |
class Toddler | |
def play_dragonvale | |
puts "Dragonvale is fun!" | |
end | |
def watch_octonauts | |
puts "I'm watchin me some great Octonauts" | |
end | |
end | |
anthony = Toddler.new | |
anthony.play_peek_a_boo | |
anthony.watch_dora_the_explorer | |
#Now lets execute this line: | |
anthony.public_methods.sort | |
# is THAT metaprogramming? No, but it is code that is starting to reason | |
# about its own existence... | |
# In this example, are we metaprogramming yet? I don't think this is 'code | |
# that writes code' any more than the above example is, but it is 'code that is | |
# aware of the interpreter interpreting it'. A compile-driven developer would | |
# have a hard time reading this, but to the interpreter it is pretty much the | |
# same thing as above. | |
Object.const_set(:Toddler, Class.new).class_eval do | |
define_method("play_dragonvale") do | |
puts "Dragonvale is fun!" | |
end | |
define_method("watch_octonauts") do | |
puts "I'm watchin me some great Octonauts!" | |
end | |
end | |
anthony = Toddler.new | |
anthony.play_peek_a_boo | |
anthony.watch_dora_the_explorer | |
# and in the ~15 minutes of the talk, we work towards this solution. Notice that | |
# the end result of the Toddler class is compatible with the example above. This | |
# example by itself is the end result of the talk, but the important part of my | |
# talk is the journey getting here. The Nerd, Jock, and Toddler class have been | |
# written in a style sometimes called 'declarative programming' or an 'internal | |
# domain specific language'. | |
module Personality | |
def watches(entertainment_source) | |
define_method("watch_#{entertainment_source}") do | |
puts "I'm watchin me some great #{entertainment_source}!" | |
end | |
end | |
def plays(game_type) | |
define_method("play_#{game_type}") do | |
puts "#{game_type} is fun!" | |
end | |
end | |
end | |
class Nerd | |
extend Personality | |
watches :star_trek | |
watches :stargate | |
plays :minecraft | |
end | |
class Jock | |
extend Personality | |
watches :football | |
plays :football | |
plays :beer_pong | |
end | |
class Toddler | |
extend Personality | |
watches :octonauts | |
plays :dragonvale | |
end | |
dave = Nerd.new | |
dave.watch_star_trek | |
# I'm watching me some great star_trek! | |
dave.play_minecraft | |
# Minecraft is fun! | |
peter = Jock.new | |
peter.watch_football | |
# I'm watchin me some great football! | |
peter.play_beer_pong | |
#Beer Pong is fun! | |
anthony = Toddler.new | |
anthony.play_dragonvale | |
anthony.watch_octonauts | |
# as your own exercise, write a 'tells' method in personality so we can say | |
# things like: | |
tells :knock_knock_joke | |
tells :dirty_joke | |
tells :blonde_joke | |
# My major assertion of this talk is that people define Metaprogramming as 'code | |
# that writes code'. I assert that in Ruby, because we have the interpreter, all | |
# our code does that. In the first Toddler example, neither the code nor the | |
# developer is aware of that. In the second toddler example the code is aware of | |
# it, and in the Personality module we are using that knowledge of | |
# interpretation to our advantage. In Ruby, its not such much that we can 'write | |
# code that can write code', but that we can 'write code that reasons about its | |
# own interpretation'. This isn't metaprogramming, its just 'Ruby programming'. | |
# The term 'metaprogramming' is the lie that lets people see the truth - but it | |
# also sets up a barrier that people need to tear down, otherwise they are just | |
# compiler-driven developers in an interpreted language. | |
# | |
# Finally, we draw a lot of observations from the process of writing this | |
# code... notice that we have taken some complexity that was spread out amongst | |
# the original Toddler class and concentrated it into a module, leaving the new | |
# Toddler class very simple and self-descriptive. This technique for bundling up | |
# reusable code is powerful, and Rails itself is a great example of that. How | |
# many people, as they were learning Rails, didn't know or care whether | |
# 'has_many' or 'validates_presence_of' was a language keyword or a method call? | |
# The tradeoff: we are lowering the bar of our API users and raising the bar of | |
# our library maintainers. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment