Metaprogramming
Mehmet Emin İNAÇ
Tech Lead @ VNGRS
github.com/meinac
What does meta mean?
Meta (from the Greek preposition and prefix meta-
(μετά-) meaning "after", or "beyond") is a prefix used
in English to indicate a concept which is an
abstraction from another concept, used to complete
or add to the latter.
Metaprogramming?
Metaprogramming is the writing of computer
programs with the ability to treat programs as their
data. It means that a program could be designed to
read, generate, analyse and/or transform other
programs, and even modify itself while running.
• Imagine a guy who builds cars. Say it's the same
thing as using a computer. At some point he
realizes he's always doing the same thing, more or
less.
• So he builds factories to build cars, and it's much
better. He's now programming ! Nevertheless, once
again, at some point, he realizes he's always doing
the same thing, to some extent.
• Now he decides to build factories that build
factories that build cars. That's metaprogramming.
Metaprogramming benefits
• Keeps code small, simple, DRYer, lighter, more
intuitive and more scalable
• Rapid prototyping
• Minimize the LOC to express a solution
• Makes good programmers really productive
Metaprogramming related
topics
• Macros
• DSL
• Reflection
• Introspection
• Annotations
• Byte code or AST transformation
C Macros
• C constants allow us to fix program behaviour at
development time
• C variables allow us to modify program behaviour
at execution time
• C macros allow us to modify program behaviour at
compilation time
C Macros
#if __WORDSIZE == 64
#define LONG_MAX 9223372036854775807L
#else
#define LONG_MAX 2147483647L
#endif
C Macros
#define ITERATE_LIST(n, list) 
for(Node *n = (list)->head; n; n = n->next)
#define max_of(x, y) (x > y) ? x : y
C Macros
#include <stdio.h>
#define max_of(x, y) (x > y) ? x : y
int main(int argc, const char * argv[]){
printf("The biggest one of 2 and 5 is %dn", max_of(2, 5)); //prints 5
printf("The biggest one of 3.14 and 6.02 is %.02fn", max_of(3.14, 6.02)); //prints 6.02
printf("The biggest one of 'a' and 'z' is '%c'n", max_of('a', 'z')); //prints 'z'
}
Reflection
Ability of a computer program to examine and modify
the structure and behaviour (specifically the values,
meta-data, properties and functions) of the program
at runtime.
JAVA Reflection
public class Main {
public void foo(){
System.out.println("I'm foo");
}
public void bar(){
System.out.println("I'm bar");
}
public static void main(String[] args){
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Please enter a method name: ");
try {
String method_name = bf.readLine();
Object obj = Class.forName("com.metaprogramming.Main").newInstance();
Class klass = obj.getClass();
Method method = klass.getMethod(method_name, null);
method.invoke(obj, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
JAVA Reflection
Object obj = Class.forName(“Main").newInstance();
Class klass = obj.getClass();
Method method = klass.getMethod(“foo”, null);
method.invoke(obj, null);
AST
In computer science, an abstract syntax tree (AST),
or just syntax tree, is a tree representation of the
abstract syntactic structure of source code written in
a programming language. Each node of the tree
denotes a construct occurring in the source code.
AST
AST Transformation in
Groovy
package com.metaprogramming
import groovy.transform.ASTTest
import org.codehaus.groovy.ast.expr.BinaryExpression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.syntax.Token
import org.codehaus.groovy.syntax.Types
class Foo {
String bar
@ASTTest(phase=SEMANTIC_ANALYSIS,value={
def classNode = node.declaringClass
def field = classNode.getDeclaredField('bar')
def assignment = new BinaryExpression(
new VariableExpression(field),
Token.newSymbol(Types.EQUAL, 0, 0),
new ConstantExpression('initialized!')
)
node.code.addStatement(new ExpressionStatement(assignment))
})
public void init() {
}
}
AST Transformation in
Groovy
def f = new Foo()
f.init()
println(f.bar) //writes 'initialized!' to the
standart output
Metaprogramming in Ruby
method_missing define_method
define_singleton_method method send
instance_eval class_eval is_a?
alias_method instance_variable_set
remove_method call method_removed
class_variable_set kind_of? instance_of?
Introspection/Reflection:
instance variables
class Foo



def initialize

@bar = 'Bar'

end



end
f = Foo.new
f.instance_variable_get(‘@bar') #'Bar'
f.instance_variable_set(‘@baz', 'Baz')
f.instance_variable_get(‘@baz') #'Baz'
f.instance_variables #[:@bar, :@baz]
Introspection/Reflection:
class variables
class Foo



@@bar = 'Bar'



end





Foo.class_variable_get('@@bar') #'Bar'

Foo.class_variable_defined?(‘@@bar') #true

Foo.class_variable_set('@@baz', 'Baz')

Foo.class_variable_get('@@baz') #'Baz'

Foo.class_variables #[:@@bar, :@@baz]
Introspection: methods
class Foo



def bar; 'I'm bar' end



private

def baz; 'I'm baz' end



protected

def zoo; 'I'm zoo' end



end



f = Foo.new

f.methods #[:bar, :zoo, ...]

f.private_methods #[:baz, ...]

f.protected_methods #[:zoo, ...]
Reflection: methods
class Foo



def bar; 'I'm bar' end



private

def baz; 'I'm baz' end



protected

def zoo; 'I'm zoo' end



end



f = Foo.new

%w(bar baz zoo).each do |method|

f.send(method)

end
Defining methods on the fly
class Foo



attr_reader :foo, :bar



end
Defining methods on the fly
def self.attr_reader(*attrs)

attrs.each do |attr|

define_method(attr) do

instance_variable_get("@#{attr}")

end

end

end
method_missing
class Foo



def method_missing(method_name, *args, &block)

'You have called a method which is not declared yet'

end



end
method_missing
class Foo



def say(expression)

expression.gsub(/_/, ' ')

end



def method_missing(method_name, *args, &block)

method_name.to_s =~ /^say_(.+)_to_me$/ ? say($1) : super

end



end
method_missing
&
define_method
class Foo



def method_missing(method_name, *args, &block)

if method_name.to_s =~ /^say_(.+)_to_me$/

self.class.class_eval do

define_method(method_name) do

__method__ =~ /^say_(.+)_to_me$/

$1.gsub(/_/, ' ')

end

end

send(method_name)

else

super

end

end



end
I recommend you to
don’t use eval!

Metaprogramming

  • 1.
    Metaprogramming Mehmet Emin İNAÇ TechLead @ VNGRS github.com/meinac
  • 2.
    What does metamean? Meta (from the Greek preposition and prefix meta- (μετά-) meaning "after", or "beyond") is a prefix used in English to indicate a concept which is an abstraction from another concept, used to complete or add to the latter.
  • 3.
    Metaprogramming? Metaprogramming is thewriting of computer programs with the ability to treat programs as their data. It means that a program could be designed to read, generate, analyse and/or transform other programs, and even modify itself while running.
  • 4.
    • Imagine aguy who builds cars. Say it's the same thing as using a computer. At some point he realizes he's always doing the same thing, more or less. • So he builds factories to build cars, and it's much better. He's now programming ! Nevertheless, once again, at some point, he realizes he's always doing the same thing, to some extent. • Now he decides to build factories that build factories that build cars. That's metaprogramming.
  • 5.
    Metaprogramming benefits • Keepscode small, simple, DRYer, lighter, more intuitive and more scalable • Rapid prototyping • Minimize the LOC to express a solution • Makes good programmers really productive
  • 6.
    Metaprogramming related topics • Macros •DSL • Reflection • Introspection • Annotations • Byte code or AST transformation
  • 7.
    C Macros • Cconstants allow us to fix program behaviour at development time • C variables allow us to modify program behaviour at execution time • C macros allow us to modify program behaviour at compilation time
  • 8.
    C Macros #if __WORDSIZE== 64 #define LONG_MAX 9223372036854775807L #else #define LONG_MAX 2147483647L #endif
  • 9.
    C Macros #define ITERATE_LIST(n,list) for(Node *n = (list)->head; n; n = n->next) #define max_of(x, y) (x > y) ? x : y
  • 10.
    C Macros #include <stdio.h> #definemax_of(x, y) (x > y) ? x : y int main(int argc, const char * argv[]){ printf("The biggest one of 2 and 5 is %dn", max_of(2, 5)); //prints 5 printf("The biggest one of 3.14 and 6.02 is %.02fn", max_of(3.14, 6.02)); //prints 6.02 printf("The biggest one of 'a' and 'z' is '%c'n", max_of('a', 'z')); //prints 'z' }
  • 11.
    Reflection Ability of acomputer program to examine and modify the structure and behaviour (specifically the values, meta-data, properties and functions) of the program at runtime.
  • 12.
    JAVA Reflection public classMain { public void foo(){ System.out.println("I'm foo"); } public void bar(){ System.out.println("I'm bar"); } public static void main(String[] args){ BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Please enter a method name: "); try { String method_name = bf.readLine(); Object obj = Class.forName("com.metaprogramming.Main").newInstance(); Class klass = obj.getClass(); Method method = klass.getMethod(method_name, null); method.invoke(obj, null); } catch (Exception e) { e.printStackTrace(); } } }
  • 13.
    JAVA Reflection Object obj= Class.forName(“Main").newInstance(); Class klass = obj.getClass(); Method method = klass.getMethod(“foo”, null); method.invoke(obj, null);
  • 14.
    AST In computer science,an abstract syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language. Each node of the tree denotes a construct occurring in the source code.
  • 15.
  • 16.
    AST Transformation in Groovy packagecom.metaprogramming import groovy.transform.ASTTest import org.codehaus.groovy.ast.expr.BinaryExpression import org.codehaus.groovy.ast.expr.VariableExpression import org.codehaus.groovy.ast.expr.ConstantExpression import org.codehaus.groovy.ast.stmt.ExpressionStatement import org.codehaus.groovy.syntax.Token import org.codehaus.groovy.syntax.Types class Foo { String bar @ASTTest(phase=SEMANTIC_ANALYSIS,value={ def classNode = node.declaringClass def field = classNode.getDeclaredField('bar') def assignment = new BinaryExpression( new VariableExpression(field), Token.newSymbol(Types.EQUAL, 0, 0), new ConstantExpression('initialized!') ) node.code.addStatement(new ExpressionStatement(assignment)) }) public void init() { } }
  • 17.
    AST Transformation in Groovy deff = new Foo() f.init() println(f.bar) //writes 'initialized!' to the standart output
  • 18.
    Metaprogramming in Ruby method_missingdefine_method define_singleton_method method send instance_eval class_eval is_a? alias_method instance_variable_set remove_method call method_removed class_variable_set kind_of? instance_of?
  • 19.
    Introspection/Reflection: instance variables class Foo
 
 definitialize
 @bar = 'Bar'
 end
 
 end f = Foo.new f.instance_variable_get(‘@bar') #'Bar' f.instance_variable_set(‘@baz', 'Baz') f.instance_variable_get(‘@baz') #'Baz' f.instance_variables #[:@bar, :@baz]
  • 20.
    Introspection/Reflection: class variables class Foo
 
 @@bar= 'Bar'
 
 end
 
 
 Foo.class_variable_get('@@bar') #'Bar'
 Foo.class_variable_defined?(‘@@bar') #true
 Foo.class_variable_set('@@baz', 'Baz')
 Foo.class_variable_get('@@baz') #'Baz'
 Foo.class_variables #[:@@bar, :@@baz]
  • 21.
    Introspection: methods class Foo
 
 defbar; 'I'm bar' end
 
 private
 def baz; 'I'm baz' end
 
 protected
 def zoo; 'I'm zoo' end
 
 end
 
 f = Foo.new
 f.methods #[:bar, :zoo, ...]
 f.private_methods #[:baz, ...]
 f.protected_methods #[:zoo, ...]
  • 22.
    Reflection: methods class Foo
 
 defbar; 'I'm bar' end
 
 private
 def baz; 'I'm baz' end
 
 protected
 def zoo; 'I'm zoo' end
 
 end
 
 f = Foo.new
 %w(bar baz zoo).each do |method|
 f.send(method)
 end
  • 23.
    Defining methods onthe fly class Foo
 
 attr_reader :foo, :bar
 
 end
  • 24.
    Defining methods onthe fly def self.attr_reader(*attrs)
 attrs.each do |attr|
 define_method(attr) do
 instance_variable_get("@#{attr}")
 end
 end
 end
  • 25.
    method_missing class Foo
 
 def method_missing(method_name,*args, &block)
 'You have called a method which is not declared yet'
 end
 
 end
  • 26.
    method_missing class Foo
 
 def say(expression)
 expression.gsub(/_/,' ')
 end
 
 def method_missing(method_name, *args, &block)
 method_name.to_s =~ /^say_(.+)_to_me$/ ? say($1) : super
 end
 
 end
  • 27.
    method_missing & define_method class Foo
 
 def method_missing(method_name,*args, &block)
 if method_name.to_s =~ /^say_(.+)_to_me$/
 self.class.class_eval do
 define_method(method_name) do
 __method__ =~ /^say_(.+)_to_me$/
 $1.gsub(/_/, ' ')
 end
 end
 send(method_name)
 else
 super
 end
 end
 
 end
  • 28.
    I recommend youto don’t use eval!