CSC/ECE 517 Spring 2013/ch1b 1k hf

From PG_Wiki
Jump to: navigation, search
  • Here is a link to the writing assignment for this topic.
  • Here is a link to previous work on this topic.
  • The main purpose of this wiki entry is to provide insight into Dr. Gehringer's lecture on Metaprogramming in Ruby found at this link


A Basic Definition of Metaprogramming

The most basic definition of metaprogramming is: writing code that writes or manipulates code. <ref name="Video Lecture on Metaprogramming by Dr. Gehringer">Video</ref> <ref name="wiki metaprogramming">Wikipedia Article on Metaprogramming</ref>

A metaprogram can either manipulate itself, or it can manipulate some other program.

Below are three important terms associated with a metaprogramming<ref name="wiki metaprogramming">Wikipedia Article on Metaprogramming</ref>:
  • metalanguage - the language in which a metaprogram is written
  • object language - the language of the program that is being manipulated by a metaprogram
  • reflection - the ability of a programming language to be its own metalanguage (also known as reflexivity)


Uses of Metaprogramming

Metaprogramming can allow programmers to write code that is more concise and more flexible. It can be more concise because a program that can write code is able to generate more code than it is initially given. It can be more flexible, because it can allow the program to change the way it behaves without having to recompile.

One of the best examples of metaprogramming is that of the compiler<ref>Overview of Metaprogramming</ref>. A compiler uses code written in one language in order to generate code that can be executed by a computer.
Metaprogramming is also used for building frameworks such as Ruby on Rails. In this case, the use of convention over configuration allows developers to write more concise and consistent code. Following specific conventions, developers write a relatively small amount of code. The Ruby on Rails scaffold uses the code the code written by the developer to generate code that is expansive, interconnected, and standardized.
One of the most powerful uses for metaprogramming, is the ability to change a program during runtime. In languages such as Ruby and Groovy, programmers can<ref>Coding Insights Blog</ref>:
  • Create a new class at runtime
  • Create new methods at runtime
  • Modify existing classes and/or methods at runtime
  • Respond to calls made on non-existent methods


Features in Ruby that Enable Metaprogramming

In dynamically typed languages, the majority of its type checking is performed at run-time, as opposed to compile time.<ref>Wikipedia Article on the Type System</ref> Because variables are not type-checked until they are used in program execution, it is possible to use types that are created during program execution.
Interpreted languages avoid explicit compilation<ref>Wikipeida Article on Interpreted Language</ref>. Because Ruby is being interpreted as it is being executed, it is possible to create new classes and methods during program execution.
  • Ruby classes are open
Most object-oriented programming languages adhere to the open/closed principle<ref>Wikipedia Article on the Open/Closed Principle</ref>. This means that the core components that are part of the language cannot be modified. In Ruby, the classes are open, meaning that all of the classes and methods be changed.


Examples of Metaprogramming in Ruby

Creating a New Class at Runtime

Below is an example of creating a new class at runtime:<ref name="DrGCodeNotes">Dr. Gehrenger's notes on Metaprogramming</ref>

  3.times do
     class MyClass
        puts "MyClass defined"
     end
  end

In example shown above, the times method is invoked on the 3 object. Inside of the times method call, the class MyClass is defined. MyClass is actually defined three times, once for each of the three iterations. Even though the class is defined three times, it has the same effect as only defining it once. The important thing to note here, is that a new class is defined at runtime, inside of a method call.


Modifying a Class at Runtime

Below is an example of modifying a class, and and example of creating a new method at runtime:<ref name="DrGCodeNotes">Dr. Gehrenger's notes on Metaprogramming</ref>

  3.times do
     class MyClass
        def one
           puts "one"
     end
  end

In the example shown above, the class MyClass is modified again to include a new method. This method, the one method is defined three times, just like the class is defined three times. Even though it has been defined three times, it has the same effect as only defining it once. The important thing to note here is that a new method is defined at runtime.


Creating a Method at Runtime

Below is an additional example of modifying a class at runtime:<ref name="DrGCodeNotes">Dr. Gehrenger's notes on Metaprogramming</ref>

   class MyClass
     def two
        puts "two"
  end

In the example shown above, the classMyClass is reopened and the two method is added to it.


Below is an example of what happens when the code written above is executed:<ref name="DrGCodeNotes">Dr. Gehrenger's notes on Metaprogramming</ref>

  
  mc = MyClass.new
  mc.one
  mc.two
  

The output of the code will be:

  one
  two


Modifying a Method at Runtime

Below is an additional example of modifying a method at runtime:<ref name="DrGCodeNotes">Dr. Gehrenger's notes on Metaprogramming</ref>

   class MyClass
     def two
        puts "one two"
  end

In the example shown above, the two method is reopened and modified.


Below is an example of what happens when the modified code written above is executed:<ref name="DrGCodeNotes">Dr. Gehrenger's notes on Metaprogramming</ref>

  
  mc = MyClass.new
  mc.one
  mc.two
  

The output of the code will be:

  one
  one two

Responding to a Non-Existent Method Call

Whenever a call to an undefined method is made on an object, Ruby provides an option to intercept the call. This is done by implementing the method method_missing within the class definition. Ruby passes, as parameters, the name of the method called and the arguments passed to it.

Define a module (or class) Roman. This class contains a method_missing method that intercepts calls to “class methods” that are undefined. It then tries to interpret the method name as a Roman numeral. For example,

  • evaluating Roman.xix calls the xix,method of module Roman.
  • Roman has no xix method, so method_missing is invoked with xix as the argument.
  • The id2name method of class Symbol is invoked on :xix, returning "xix".
  • The "xix" is then parsed according to the rules for evaluating Roman numerals, and evaluates to 19.
 class Roman 
 DIGITS = {
   'I' => 1,
   'V' => 5,
   'X' => 10,
   'L' => 50,
   'C' => 100,
   'D' => 500,
   'M' => 1000,
 }
 def roman_to_integer(roman_string)
   last = nil
   
 roman_string.to_s.upcase.split(//).reverse.inject(0) do
 |memo, digit|
     if digit_value = DIGITS[digit]
       if last && last > digit_value
         memo -= digit_value
       else
         memo += digit_value
       end
 last = digit_value
     end
 memo
   end
 end
 def method_missing(method)        
   str = method.id2name 
   roman_to_integer(str)      
 end


Metaprogramming in Other Languages

The C preprocessor (CPP)<ref>The Art of Metaprogramming</ref>

First let's look at metaprogramming that involves textual macro languages. Textual macros are macros that directly affect the text of the programming language without knowing about or dealing with the meaning of the language.

The SWAP macro:
  #define SWAP(a, b, type) { 
      type __tmp_c; c = b; b = a; a = c; 
  }

This macro allows you to swap the two values of the given type. A macro is effective in this situation because:

  • A function call would take too much overhead for a simple operation
  • The functions would require variables' addresses to be passed which is messier and prevents the compiler from keeping the values in registers.
  • A different function would need to be coded for each type of item you wanted to swap.


 #define SWAP(a, b, type) { type __tmp_c; c = b; b = a; a = c; }
 int main()
 {
     int a = 3;
     int b = 5;
     printf("a is %d and b is %d\n", a, b);
     SWAP(a, b, int);
     printf("a is now %d and b is now %d\n", a, b);
     return 0;
 }

Meta Programming in Java<ref>Metaprogramming in Java</ref>

The generics can be used in classes, interfaces, methods and constructors. Two new types:

  • Parametrized types
  • Type variables

A type variable is an unqualified identifier. Class and interface declarations can have type arguments (type variables)

  • Metalevel: Class implements GenericsDeclaration

Method and constructors definitions can have type arguments (type variables)

  • Metalevel: Method, Constructor implements GenericsDeclaration
 public interface List<E> { 
   void add(E x);
   Iterator<E> iterator();
 }
 public interface Iterator<E> { 
   E next();
   boolean hasNext();
 }
 List<String> anExample;
 anExample.add(”sdfdfss”);
 anExample.add(new Object()); // compile time error
 String aTest = anExample.iterator().next();

References

<references/>