Presentation is loading. Please wait.

Presentation is loading. Please wait.

Spindle, Mutilate, & Metaprogram Markus Matt

Similar presentations


Presentation on theme: "Spindle, Mutilate, & Metaprogram Markus Matt"— Presentation transcript:

1 Spindle, Mutilate, & Metaprogram Markus Roberts @markusq Matt Youell @built

2 You Betcha! The hot new game show where programmers sacrifice their dignity* in the hopes of winning a T-shirt *Other risks include loss of confidence, confusion, and hair loss. Losing contestants may be subject to peer pressure and badgered into making animal noises, participating in ill-conceived medical experiments, or trying new programming techniques. In a recent study an independent panel concluded that the game was rigged. They were correct. If you have read this far you have very good eyesight and too much time on your hands. A group of programmers walk into a bar. The bar tender asks, "What'll it be?" The C programmer says, "You see that bottle?" pointing to a large green bottle marked only with a "*". "Give me whatever's in it." The Lisp programmer says, "Give me (on the bar in front of me) a glass (a small glass (or plastic cup)) of (imported (German)) beer. (If that's not available, then wine (red (California (Cabernet))).) The assembly language programmer says, "Take a glass from position two in the glass rack and place it on the bar in position zero ;;in front of you. Take a bottle from position seven in the bottle rack and pour the contents into position zero on the bar. Stop after three seconds. Put the bottle in position seven of the bottle rack. Take the glass from position zero on the bar and place it in position four on the bar ;;in front of me." The Smalltalk programmer says, "Bartender get vodka bottle. Vodka bottle pour into glass. Hand lift glass. Mouth drink." The Visual Basic programmer says, "You see those two guys sitting over there?" pointing at a big bald guy and a smaller one wearing glasses. "I'll take whatever they're willing to let me have." The SQL programmer says, "Take all the bottles containing beer, call them 'beers'. Take all the bottles that come from companies with output of less than one million units per year, call them 'micros'. Take the intersection of 'beers' and 'micros', and pour me the first one." The Forth programmer says, "White wine, give me." The Prolog programmer says, "I'll have coke if I want caffeine. I'll have vodka if I want to get drunk. I'll have water if I'm thirsty. I want caffeine if I need to stay awake. I'm thirsty if it's a hot day. I need to stay awake if I still have work to do today. It's a hot day if it's greater than 80 degrees Fahrenheit. I still have work to do today." The UI designer says, "I'd like a small glass with a thin edge so it's easy to drink from. It should be large enough to grasp easily, but not so large that it's too heavy. It should have a picture on it of a person drinking (thus showing how to use the glass)." "But what do you want in it?" asks the bar tender. "I don't care," says the UI designer........................................................................................................................................................... Welcome to...

3 ● We show you code ● We ask a question ● You answer! ● First one to answer correctly gets a point ● Then we show and briefly discuss the rest of the code The game is pretty simple: The person with the highest score gets a T-shirt with an incredibly witty saying on it. The person with the lowest score gets to help us with the next part of the presentation. (Hope you brought a towel.)

4 Game On! Photo: shaggy359

5 #include int main(void) { puts("Hello World!"); int i = 7,n = 999999; printf("Did you know that %i is %i times %i?\n",n,i,n/i); return 0; } This code is part of a program written in...?

6 #!/usr/bin/env ruby def method_missing(msg,*args, &block) (proc &block).call if block_given? end #include int main(void) { puts("Hello World!"); int i = 7,n = 999999; printf("Did you know that %i is %i times %i?\n",n,i,n/i); return 0; } Want to try again?

7 n = 1; d = 3; x = n/d; This is a little ruby. What is the value of x?

8 0 #!/usr/bin/env ruby n = 1; d = 3; x = n/d; print x,”\n”

9 program Example(Input,Output) begin writeln('What''s it Wirth to you?'); end This code is part of a program written in...?

10 #include #define program int #define Example(a,b) main() #define begin { #define writeln(s) printf(dequote(#s)); printf("\n"); #define end } char* dequote(char* str) { char *result; int pass,r,w; for (pass=1;pass <= 2;pass++) { for(r=0,w=0;str[r] != '\0';r++) { if (pass==2) result[w] = str[r]; if ((str[r+1] == '\'') || (str[r] != '\'')) w++; }; if (pass==1) result=malloc(sizeof(char)*(w+1)); } result[w]='\0'; return result; } program Example(Input,Output) begin writeln('What''s it Wirth to you?') end

11 n = 1; d = 3; x = n/d; This still ruby. What is the value of x?

12 1/3 #!/usr/bin/env ruby require “mathn” n = 1; d = 3; x = n/d; print x,”\n”

13 000010 IDENTIFICATION DIVISION. 000020 PROGRAM-ID. HELLO-WORLD-PROG. 000030 AUTHOR. TIMOTHY R P BROWN. 000040*The standard Hello world program 000050 000060 ENVIRONMENT DIVISION. 000070 000080 DATA DIVISION. 000090 WORKING-STORAGE SECTION. 000100 01 TEXT-OUT PIC X(12) VALUE 'Hello World!'. 000110 000120 PROCEDURE DIVISION. 000130 MAIN-PARAGRAPH. 000140 DISPLAY TEXT-OUT 000150 STOP RUN. This code is part of a program written in...?

14 Yeah, it was COBOL.

15 n = 1; d = 3; x = n/d; Ruby again. What is the value of x?

16 0.[3] #!/usr/bin/env ruby require “mathn” class Rational def to_s(*args) seen = {} whole_part,n = numerator.divmod denominator result = whole_part.to_s + "." while not seen[n] seen[n] = result.length digit,n = (n*10).divmod denominator result += digit.to_s return result if n == 0 end result[0...seen[n]]+"["+result[seen[n]..-1]+"]" end n = 1; d = 3; x = n/d; print x,”\n” 1.767[857142] Which for 1/3 gives us Likewise, for 95/96 we'd have

17 MOV AX, [BP + StartY] ; # of bytes offset MOV SI, AX ; AND AX, 0x03 ; ADD SI, PATTERN_BUFFER ; This is a fragment of a program written in...?

18 #!/usr/bin/env perl use strict; use constant StartY => 123; use constant BP => 0; use constant PATTERN_BUFFER => 0x7F800; sub register { my $reg = shift; return sub { return $$reg if( scalar(@_) == 0 ); $$reg = new_value(shift, $$reg); } sub new_value { my ($arg, $current_value) = shift; if( ref($arg) eq 'CODE' ) { return $arg->(); } elsif( ref($arg) eq 'ARRAY' ) { return @$arg[0]; } else { return $arg eq "" ? $current_value : $arg; } } sub MOV { my $partial = shift; $partial->(shift); } sub AND { my $register = shift; $register->( $register->() & shift ); } sub ADD { my $register = shift; $register->( $register->() + shift ); } my ($AX, $SI) = 0; sub AX { register(\$AX); } sub SI { register(\$SI); } MOV AX, [BP + StartY] ; # of bytes offset MOV SI, AX ; AND AX, 0x03 ; ADD SI, PATTERN_BUFFER ; print "AX is now: $AX\n"; print "SI is now: $SI\n";

19 n = 1; d = 3; x = n/d; This time it's C++. What is the value of x?

20 “One third” #include using namespace std; class BendyNumber { int value; public: BendyNumber(); void operator=(int val); string operator/(int val); }; BendyNumber::BendyNumber() { value = 0; } void BendyNumber::operator=(int val) { value = val; } string BendyNumber::operator/(int val) { string numerator,denominator; switch (value) { case 0: return "Nothing"; case 1: numerator = "One "; break; case 2: numerator = "Two "; break; case 3: numerator = "Three "; break; default: numerator = "Many "; } switch (val) { case 0: denominator = "unicorn"; break; case 1: return numerator; break; case 2: denominator = "half"; break; case 3: denominator = "third"; break; default: denominator = "of many"; } ostringstream oss; oss << numerator << denominator; if (value > 1 and val < 4) { oss << "s"; } return oss.str(); } int main() { BendyNumber n; int d; string x; n = 1; d = 3; x = n/d; cout << x << "\n"; return 0; }

21 import π from mathematical constants function volume(radius,dimensions) volume(r,0) ≡ 0 volume(r,1) ≡ 2 r volume(r,2) ≡ π r² volume(r,3) ≡ 4 π r³ -------- 3 volume(r,4) ≡ 2 4 π r -------- 2 volume(r,5) ≡ 2 5 8 π r ---------- 15 What widely known and used programming language could this be written in?

22 #!/usr/env ruby require "jane_kelly" ________ __ __FLEE__ __THIS__ __COOP__ ________ _______ __ __END__ __THE__ __PEN__ _______ __________ __ __ JUMP __ __ THAT __ __SHARK!__ __________...fly free... --מנא,מנא, תקל, ופרסין (Mene, Mene, Tekel u-Pharsin) import π from mathematical constants function volume(radius,dimensions) ⋮ It happens to be ruby, but really it could have been any Turing-complete language.

23

24 ● Perfectly valid code in language X, through it may not have looked like it. ● Code that did what it appeared to do, though maybe not in the way you might have thought. ● Something that no one would seriously write. Some of these examples showed: ● Perfectly valid code in language X, that also looked like it was written in language X ●...that did something reasonable, though maybe not what you might have expected Other examples showed We're going to explore what it means for code to “look like” it's written in a language......but first we're going to play with electricity.

25 Photo: Marcin Wichary "I do believe in stereotypical bimodal dichotomies founded on glib, over-broad generalizations"

26 C perl python ruby 7*i+2 my @x printf def f(x) f(4) mean,mode = * stats 0..100,&f prolog lisp We tend to program in a compact core of our languages (in some languages more than others)

27 C perl python ruby 7*i+27*i+2 f(4)f(4) prolog But that's really a small subset of the realm of all possible languages (not to scale)

28 What you can actually do in a Turing-complete language C perl python ruby 7*i+27*i+2 f(4)f(4) prolog But the limitation is far more in our use of the languages than in the languages themselves

29 The shortest program to ever win (Mark Biggar, UNiSYS): P; Larry Wall's mind bender: #define iv 4 #define v ;(void #define XI(xi)int xi[iv*'V']; #define L(c,l,i)c(){d(l);m(i);} #include int*cc,c,i,ix='\t',exit(),X='\n'*'\d';XI(VI)XI(xi)extern(*vi[])(),(* signal())();char*V,cm,D['x'],M='\n',I,*gets();L(MV,V,(c+='d',ix))m(x){v) signal(X/'I',vi[x]);}d(x)char*x;{v)write(i,x,i);}L(MC,V,M+I)xv(){c>=i?m( c/M/M+M):(d(&M),m(cm));}L(mi,V+cm,M)L(md,V,M)MM(){c=c*M%X;V-=cm;m(ix);} LXX(){gets(D)||(vi[iv])();c=atoi(D);while(c>=X){c-=X;d("m");}V="ivxlcdm" +iv;m(ix);}LV(){c-=c;while((i=cc[*D=getchar()])>-I)i?(c?(c<i&&l(-c-c, "%d"),l(i,"+%d")):l(i,"(%d")):(c&&l(M,")"),l(*D,"%c")),c=i;c&&l(X,")"),l (-i,"%c");m(iv-!(i&I));}L(ml,V,'\f')li(){m(cm+!isatty(i=I));}ii(){m(c=cm = ++I)v)pipe(VI);cc=xi+cm++;for(V="jWYmDEnX";*V;V++)xi[*V^' ']=c,xi[*V++] =c,c*=M,xi[*V^' ']=xi[*V]=c>>I;cc[-I]-=ix v)close(*VI);cc[M]-=M;}main(){ (*vi)();for(;v)write(VI[I],V,M));}l(xl,lx)char*lx;{v)printf(lx,xl)v) fflush(stdout);}L(xx,V+I,(c-=X/cm,ix))int(*vi[])()={ii,li,LXX,LV,exit,l, d,l,d,xv,MM,md,MC,ml,MV,xx,xx,xx,xx,MV,mi}; IOCCC -- The International Obfuscated C Code Contest (http://www.ioccc.org/) short main[] = { 277, 04735, -4129, 25, 0, 477, 1019, 0xbef, 0, 12800, -113, 21119, 0x52d7, -1006, -7151, 0, 0x4bc, 020004, 14880, 10541, 2056, 04010, 4548, 3044, -6716, 0x9, 4407, 6, 5568, 1, - 30460, 0, 0x9, 5570, 512, -30419, 0x7e82, 0760, 6, 0, 4, 02400, 15, 0, 4, 1280, 4, 0, 4, 0, 0, 0, 0x8, 0, 4, 0, ',', 0, 12, 0, 4, 0, '#', 0, 020, 0, 4, 0, 30, 0, 026, 0, 0x6176, 120, 25712, 'p', 072163, 'r', 29303, 29801, 'e' }; Sjoerd Mullender & Robbert van Renesse had this to say:

30 ● Perfectly valid code in language X, through it may not look like it. ●...that does what it appeared to do, though maybe not in the way you might have thought. ●...that people used every day in a production environment, and no one really objects to. Now we're going to look at: ● Perfectly valid code in language X, that also looks like it was written in language X ●...that does something reasonable and expected in the context though maybe not what is “normal” for the language We'll also see examples of:

31 *http://www.cs.utexas.edu/~EWD/transcriptions/EWD04xx/EWD418.html Edsger Dijkstra's “Guarded commands. non-determinacy and a calculus for the derivation of programs”* explored the concept of non-deterministic control structures. Despite their power, few languages have them built in nondeterministic case s when /^#.*$/: comment(s) when /^#?$/: boring(s) when /LOL/: silly(s) else normal(s) end nondeterministic case

32 def nd_case(v,branches) possibilities = branches.keys.select {|cond| cond === v} (branches[possibilities.random || :else] || proc {}).call end 5.times { nd_case(7, Integer => proc { print "Integer\n" }, "tree" => proc { print "tree\n" }, 0..10 => proc { print "0..10\n" }, 7 => proc { print "7\n" }, :else => proc { print "No matches\n"} ) }

33 def nd_case(v,*branches) branches = branches.inject({}) { |h,p| h.update p } possibilities = branches.keys.select {|cond| cond === v} (branches[possibilities.random || :else] || proc {}).call end def nd_when(cond,&action) {cond => action} end def nd_else(&action) {:else => action} end 5.times { nd_case(7, nd_when(Integer) do print "Integer\n" end, nd_when("tree") do print "tree\n" end, nd_when(0..10) do print "0..10\n" end, nd_when(7) do print "7\n" end, nd_else do print "No matches\n" end ) }

34 $nd_opts = [] class Nd_cond def initialize(val) @val = val end def ===(other) (@val===other) and (callcc {|cont| $nd_opts<<cont; return false}; true) end def self.else return yield if $nd_opts.empty? winner = $nd_opts.random $nd_opts = [] winner.call end def nd_(x) Nd_cond.new(x) end 5.times { case 7 when nd_(Integer): print "Integer\n" when nd_("tree"): print "tree\n" when nd_(0..10): print "0..10\n" when nd_(7): print "7\n" else Nd_cond.else { print "No matches\n"} end }

35 5.times { nondeterministic-case 7 when Integer: print "Integer\n" when "tree": print "tree\n" when 0..10: print "0..10\n" when 7: print "7\n" else-deterministic { print "No matches\n" } end } Uses normal ruby case-syntax except: “nondeterministic-case” instead of “case” “else-deterministic” instead of “else” Only requires lambda formation on else Always triggers (else is optional)

36 class A_wave_function def initialize : end def else_block(block) : 0 end def collapse : end def -(x) return x if collapsed collapse end def nondeterministic A_wave_function.new end def deterministic(&block) $current_wave_function.else_block(block) end

37 class A_wave_function attr_reader :possible_realities,:nested_wave_function,:patches, :collapsed def initialize @nested_wave_function = $current_wave_function $current_wave_function = self @possible_realities = [] @collapsed = false if @nested_wave_function @patches = nested_wave_funtion.patches else @patches = {} ObjectSpace.each_object(Module) { |m| if m.instance_methods(false).include? "===" @patches[m] = m.instance_method(:===) end } @patches.keys.each { |m| m.module_eval %Q{ def ===(other) cwf = $current_wave_function cwf.patches[#{m}].bind(self).call(other) and (callcc { |cont| $cwf.possible_realities << cont return false}; true) end } end :

38 class A_wave_function : def else_block(block) possible_realities << block if possible_realities.empty? 0 end def collapse @collapsed = true @modules_with_case_equ.keys.each { |m| m.send(:define_method,:===,patches[m]) } unless nested_wave_function $current_wave_function = nested_wave_function (possible_realities.random || proc {}).call end : end

39 fib(X) when X 1; fib(X) -> fib(X-1) + fib(X-2). Erlang (and Haskell) lets us write things like this: It might be nice to have this pattern available in other languages...

40 from guard import * @when("n in (0, 1)") def fib(**kargs): return 1 @when() def fib(**kargs): n = kargs['n'] return fib(n=(n-1)) + fib(n=(n-2)) print fib(n=20)

41 from guard import * known_fibs = {0:1,1:1} @when("n in known_fibs") def fib(**kargs): return known_fibs[kargs['n']] @when() def fib(**kargs): global known_fibs n = kargs['n'] known_fibs[n] = fib(n=(n-1)) + fib(n=(n-2)) return known_fibs[n] print fib(n=20)

42 class Guard: dispatch_table = [] @classmethod def register(cls, func, condition): cls.dispatch_table += [(func, condition)] @classmethod def satisfies_predicate(cls, predicate, params): import sys context = sys._getframe(2).f_globals return eval(predicate, context, params) if predicate else True def when(condition=""): def decorator(func): Guard.register(func, condition) def dispatcher(**args): possibles = [method for (method, predicate) in Guard.dispatch_table if method.__name__ == func.__name__ and Guard.satisfies_predicate(predicate, args)] return possibles[0](**args) if possibles else func(**args) return dispatcher return decorator

43 Adding closures to C (or “How to get your name on a billboard”) A quick google for closures in C turns up multiple implementations, including: ● Using gcc's nested functions and longjumping to set up the state you want ● Using the fact that with C you can access memory directly to modify memory and making some clever transformations* ● Building a struct to wrap function pointers and hold context ● A stack mungling game that even we don't understand, but appears to work * This was the one that inspired @evanphx to tweet this generous offer: “If I ever find this code (http://bit.ly/mSRgU) in someone's code base, I'll take out a billboard in Time Square to shame them.” So if you're looking for fame, there's your ticket. There are, of course, doubtlessly countless other ways to do it.

44 getTransport: function() { return Try.these( function() { return new XMLHttpRequest() }, function() { return new ActiveXObject('Msxml2.XMLHTTP') }, function() { return new ActiveXObject('Microsoft.XMLHTTP') } ) || false; } prototype.js and friends $('item1', 'item2', 'item3').invoke('hide'); $('link').hasAttribute('href');

45 class Book < ActiveRecord::Base has_many :authors, :through => :contributions def see_also Book.find_by_title(title) – [self] end class Author < ActiveRecord::Base has_many :authorships has_many :books, :through => :contributions def coauthors books.collect_authors - [self] end class Contribution < ActiveRecord::Base belongs_to :author belongs_to :book end book = Book.find_or_create_by_ISBN('0201103311') Rails, ActiveRecord, etc.

46 module Enumerable def method_missing(meth,*args) if meth.to_s =~ /^all_(.*\?)$/ all? { |el| el.send($1,*args) } elsif meth.to_s =~ /^collect_(.*)\?$/ collect { |el| el.send(meth,*args) } else super(meth,*args) end if meth.to_s =~ /^all_(.*\?)$/ instance_eval(%Q{ def #{meth}(*args) all? { |el| el.send(#{$1.inspect},*args) } end }) send(meth,*args) Or if we think it may get called a lot:

47 2 -b + Sqrt[ b - 4 a c ] ----------------------- 2 a 2 2 2 x x Sqrt[r - x ] + r ArcTan[------------- ] 2 2 Sqrt[r - x ] ----------------------------------------- - 2 *http://integrals.wolfram.com/index.jsp?expr=Sqrt[r^2+-+x^2]&random=false And Wolfram's online integrator* will gladly tell us that the integral of Sqrt[r^2-x^2] is: Most of us have seen the quadratic formula: But can we just paste these formulas into our programs? Heck yeah!

48 def linearize(s) lines_above,pivot_line,lines_below = lines_from(s).split_vertically last_text = nil pivot_line.split_horizontally.collect { |text,range| a = linearize lines_above.columns(range) b = linearize lines_below.columns(range) '*'.if(adjacent_values(last_text,text)) + case text when Whitespace: "[#{b}]".unless(b.blank?) + "**(#{a})".unless(a.blank?) when A_line_of_dashes: last_text = "((#{a})/(#{b}))" else last_text = text.strip end }.join end 2 -b + Sqrt[ b - 4 a c ] ----------------------- 2 a ((-b+Sqrt[b**(2)-4*a*c])/(2*a))

49 def linearize(lines) lines = lines.split(/\n/) if lines.is_a? String lines = lines.select { |l| not l =~ /^\s*$/ } return '' if lines.length == 0 i = (0..lines.length). sort_by { |i| lines[i] =~ /^( *)(-*)/; [$1.length,-$2.length,i] }. first { |i| good_divider(lines,i) } above,below = lines[0...i],lines[(i+1)..-1] last_text = nil col = 0 (lines[i]+' '*99). scan(/(\s+|[-+]?\d+\.?\d*|-+|\w+|.)/). flatten. collect { |text| range = col...(col+=s.length) implicit_mult = ((last_text=~/[a-z0-9.\)\]]$/i and text=~/^[a-z0-9.\(]/i))? '*' :'' a = above.collect { |x| x[range] || '' } b = below.collect { |x| x[range] || '' } implicit_mult + case text when /^ *$/: ((b =~ /^\s*$/) ? '' : "[#{ linearize(b)}]") + ((a =~ /^\s*$/) ? '' : "**(#{linearize(a)})") when /^---+$/: last_text = "((#{linearize(a)})/(#{linearize(b)}))" else last_text = text.strip end }.join end The “No Magic Please” version

50 def lines_from(s) s = src.split(/\n/).pad_to_match_longest if s.is_a? String s = s.select { |l| not l.blank? } def s.split_vertically i = index_range. sort_by { |i| self[i] =~ /^( *)(-*)/ [$1.length,-$2.length,i] }. first { |i| good_divider(self,i) } || 0 def (self[i]).split_horizontally return [] if nil? or blank? col = 0 scan(/(\s+|[-+]?\d+\.?\d*|-+|\w+|.)/). flatten. collect { |x| [x,col...(col+=x.length)] } end [rows(0...i),self[i],rows((i+1)..-1)] end s end

51 irb> require "symbolic_expressions.rb" => true irb> 7*8 => 56 irb> x*x+4 => ((x*x)+4) irb> F(x) => F(x) irb> F(x)+G(y) => (F(x)+G(y)) irb> def secret(a,b) irb> (1234*a*b+543*b)/9876 irb> end => nil irb> secret(7.0,3.0) => 2.78888213851762 irb> secret(7.0,b) => (((8638.0*b)+(543*b))/9876) irb> secret(a,3.0) => ((((1234*a)*3.0)+1629.0)/9876) irb> secret(a,b) => ((((1234*a)*b)+(543*b))/9876) irb> s = secret(a,b) => ((((1234*a)*b)+(543*b))/9876) irb> s.substitute(a => 7.0) => ((((1234*7.0)*b)+(543*b))/9876) irb> s.substitute(a => 7.0, b=> 5) => ((((1234*7.0)*5)+(543*5))/9876) irb> s.substitute(a=>7.0, b=>3).apply => 2.78888213851762 irb> s.substitute(a=>7.0, b=>5).apply => 4.64813689752936 irb> Symbolic expressions

52 module Symbolic_contagen def method_missing(*args) A_symbolic_expression.new(self,*args) end def coerce(other) [other.as_symbolic_expression,self] end def as_symbolic_expression self end class A_symbolic_expression include Symbolic_contagen def initialize(*components) @components = components end class Symbol include Symbolic_contagen end class Object def as_symbolic_expression A_symbolic_expression.new(self) end

53 class A_symbolic_expression def param_list “(#{@components[2..-1]..collect { |a| a.to_s }.join(",")})" end def to_s args = @components if args[0] case args.length when 1: "#{args[0]}" when 2: "#{args[0]}.#{args[1]}" else case args[1] when :+,:-,:*,:/ : "(#{args[0]}#{args[1]}#{args[2]})" else "#{args[0]}.#{args[1]}#{param_list" end else "#{args[1]}#{param_list}” end def inspect to_s end

54 def method_missing(meth,*args) if meth.to_s =~ /^[A-Z]/ and args.length > 0 A_symbolic_expression.new(nil,meth,*args) else super end class Object def _ A_symbolic_expression.new(self) end

55 class Object def apply self end class A_symbolic_expression def substitute(vals) A_symbolic_expression.new( *@components.collect { |x| case when x == nil : nil when vals.has_key?(x) : vals[x] when x.respond_to?(:substitute): x.substitute(vals) else x end } ) end def apply @components[0].apply.send(*@components[1..-1].{|x| x.apply}) end

56 Why Wouldn't You Do This? ● Fear of the unknown

57 Why Wouldn't You Do This? ● Fear of the unknown ● Such as job hunting in this economy

58 Why Wouldn't You Do This? ● Fear of the unknown ● Lack of imagination ● Such as job hunting in this economy

59 Why Wouldn't You Do This? ● Fear of the unknown ● Lack of imagination ● Such as job hunting in this economy ● Can't imagine how this could possibly work

60 Why Wouldn't You Do This? ● Fear of the unknown ● Lack of imagination ● Confusing people ● Such as job hunting in this economy ● Can't imagine how this could possibly work

61 Why Wouldn't You Do This? ● Fear of the unknown ● Lack of imagination ● Confusing people ● Such as job hunting in this economy ● Can't imagine how this could possibly work ●... including yourself in two weeks.

62 Why Would You Do This?

63 ● Cleans up your code

64 Why Would You Do This? ● Cleans up your code ● So clean, you wouldn't recognize it.

65 Why Would You Do This? ● Cleans up your code ● Easier for domain experts to follow ● So clean, you wouldn't recognize it.

66 Why Would You Do This? ● Cleans up your code ● Easier for domain experts to follow ● So clean, you wouldn't recognize it. ● And keep those pesky programmers out of it!

67 Why Would You Do This? ● Cleans up your code ● Easier for domain experts to follow ● Unlimited Power! ● So clean, you wouldn't recognize it. ● And keep those pesky programmers out of it!

68 Why Would You Do This? ● Cleans up your code ● Easier for domain experts to follow ● Unlimited Power! ● So clean, you wouldn't recognize it. ● And keep those pesky programmers out of it! ● And the power bills that come with it.

69 Spindle, Mutilate, & Metaprogram Markus Roberts @markusq Matt Youell @built Thanks For Listening!


Download ppt "Spindle, Mutilate, & Metaprogram Markus Matt"

Similar presentations


Ads by Google