CSC/ECE 517 Fall 2010/ch4 4g HW

From Expertiza_Wiki
Jump to navigation Jump to search

Metaprogramming in Dynamically Typed Languages


Introduction

Drawing from the concepts discussed in Chapter 2 Section 24, Metaprogramming in Statically Typed Languages, we will continue to examine metaprogramming concepts in the environment of dynamically typed languages. As always, the focus will be on object-oriented languages to demonstrate the common usages of metaprogramming.

As you may recall from the previous chapter, metaprogramming can be defined as "the technique that allows us to write programs that can manipulate other programs or themselves as a part of their data" [[1]]. Thus it allows us to add behavior to classes or objects that was not previously specified, often done at compile time instead of runtime. The same basic concepts still apply here, although in a few different fashions for dynamically typed languages instead of those that are statically typed.

This chapter will begin with a brief overview of object-oriented dynamic programming languages, or languages with an object-oriented bent, and progress into a further analysis of dynamic typing within those dynamic languages. The majority of the chapter will then cover metaprogramming within the previous described environment, offering examples, common methods of use, and existence in the real-world of software development. Examples will be given to illustrate important concepts, and will be expressed in sample object-oriented dynamically typed languages.

Dynamic Languages

Dynamic programming languages are loosely defined as languages that are designed to exhibit, or provide as extra functionality, behaviors at run-time that are otherwise normally found at compile-time. This difference from static languages is exemplified in behaviors such as (during execution) adding new methods, classes or objects, extending existing classes or objects, and modifying the type system [[2]].

Common and useful examples of object-oriented dynamic languages include Ruby, Python, and Perl.

Ruby: Ruby is considered a pure object-oriented dynamic language with dynamic typing.

Python: Python is not technically (by definition) purely object-oriented, but it was designed mainly for such purposes, is dynamic and dynamically typed, and so is relevant to our discussions.

Perl: Perl is not completely object-oriented, but has essential object-oriented elements. It was designed as procedural but has since been extended to implement object-oriented mentalities, and is also dynamic and dynamically typed.

Dynamic Typing

Most but not all dynamic programming languages are also dynamically typed. Dynamic typing describes the method of type checking that occurs mainly at run-time, instead of compile-time. Put simply, the type of a variable is not statically defined; instead, a variable refers to a value, and only the value has a strict type [[3]]. Thus the variables can potentially take on different types and different values at various times. There need be no distinction between preexisting classes in a language and user-defined classes at run time. This enables a more fluid style of programming, especially in areas like metaprogramming as we soon shall see.

Example

Duck typing is a well-known and interesting form of dynamic typing that appears prominently in Ruby. Here, the type of the object is ignored in favor of the usable aspects of an object. As is often said, "if it walks like a duck, and quacks like a duck, then it is a duck" [[4]]. Suppose we wanted to code the concatenation of information about a song to a string. Without duck typing, we might write it as:

def append_song(result, song) # test we're given the right parameters 
 unless result.kind_of?(String)
  fail TypeError.new("String expected") 
 end
 unless song.kind_of?(Song) 
  fail TypeError.new("Song expected")
 end
 result << song.title << " (" << song.artist << ")" 
end
result = ""
append_song(result, song)    # =>    "I Got Rhythm (Gene Kelly)"

This sort of tedious checking need not really be done. If whatever object is passed in 'song' responds to .title and .artist, then things will be alright. If not, the exceptions will be thrown anyway, regardless of if we're explicitly checked and then failed. Thus duck typing enables us to more succinctly write [[5]]:

def append_song(result, song) 
 result << song.title << " (" << song.artist << ")"
end
result = "" 
append_song(result, song)	# =>	"I Got Rhythm (Gene Kelly)"

Metaprogramming

Now that we've established clearly what dynamic programming languages that use dynamic typing are, and shown a typical example of how this appears in simple code, we can immerse ourselves further into the more complex arena of metaprogramming with these kinds of languages.

Common Techniques

Metaprogramming techniques will certainly vary from language to language, even amongst the subset of dynamic languages that we are now considering. It will depend on what aspects of metaprogramming access the language implements, but in general these techniques will serve well in most dynamic metaprogramming pursuits [[6]].

1. Dynamically create a class or module. This is the very essence of metaprogamming, that is, the ability to create (or modify, really) things at runtime instead of compile time.

2. Replace a method or extend its implementation. Again, this is very central to metaprogramming, and can be used in almost any circumstance desired.

3. Write a domain-specific language (DSL) using classes and methods that rewrite sub-methods. A DSL is simply a way to describe a piece of code that solves a particular problem (instead of being extrapolated to a larger set of problems) [[7]]. So here, our DSL would act as a specific way to rewrite lower pieces of code: metaprogramming!

More techniques such as these do exist, but begin to become heavily language-specific. Let us now turn to look at how some object-orient dynamically typed programming languages actually employ metaprogramming.

Examples

Ruby

Let's consider how we might implement our append_song method from above using metaprogramming techniques to create its superclass and then dynamically define the method itself.

info = Class.new
info.class_eval do
 define_method: append_song(song) do
   self << song.title << " (" << song.artist << ")"
 end
info.new.append_song(RandomSong)

class_eval sets self for the duration of the block just as if it was inside the class definition body. Thus the method defintion append_song will define an instance method of that name.


Python

One way that python uses metaprogramming is through decorators. Drawn from the concept of the Decorator Pattern, a decorator in python is a specific change to syntax that allows us to change functions and methods (although not classes as of yet) [[8]]. In the example below, the use of a decorator lets us define a function inside a function (so one modifies the other).

def elementwise(fn):
   def newfn(arg):
       if hasattr(arg,'__getitem__'):  # is a Sequence
           return type(arg)(map(fn, arg))
       else:
           return fn(arg)
   return newfn
@elementwise
def compute(x):
   return x**3 - 1

print compute(5)        # => prints: 124
print compute([1,2,3])  # => prints: [0, 7, 26]
print compute((1,2,3))  # => prints: (0, 7, 26)

This decorater enhances the original compute function by adding the functionality of elementwise [[9]]. Now we can operate on one thing, or a series of things, a very useful tactic that is applicable in many dynamic programming situations.


Perl

Perl 6 is a revised version of that language, and provides greater functionality and support for object-oriented programming along with the various methods of metaprogramming [[10]]. The follow piece of code demonstrates how perl can dynamically edit a class and methods via this added functionality.

use v6;
class Ninja {
 has Str $.name is rw;
}   
my Ninja $drew .= new( name => 'Drew' );
augment class Ninja {            # adds a method
 method battle_cry {
  say $.name ~ ' says hiya!';
 }
}
$drew.battle_cry;   # => prints: Drew says hiya!
my $sword_symbol = '********';
$drew.^add_method( 'swing', method ( Str $sound_effect ) {
 say "{$.name}: {$sword_symbol} {$sound_effect}";
});
$drew.swing( 'slash!' );    # => Drew: ********* slash!

As we saw above, using perl you can define a class, dynamically add a method to it, and additionally define another method (swing) on an instance ($drew) that accesses the instance’s state by closing over local scope [[11]].


Real-World Impact and Development

One huge and intriguing example of heavily using metaprogramming in Ruby is Rails. Rails, ie Ruby on Rails, is a very popular application framework for Ruby, and is often considered to be in large part the reason that language became as popular as it is today [[12]]. Rails employs techniques as discussed above like a DSL (exemplified in Active Record), extending existing types (as is done using Fixnum), and more nifty methods like automatic English pluralization [[13]]. As an example, consider this:

Class Users < ActiveRecord::Base
 set_table_name :unconventional_name
 has_many :friends
end

Here we see that set_table_name is executed against self, and that Users inherits from ActiveRecord so we don't need to worry about defining that method as long as ActiveRecord takes care of it! Metaprogramming can indeed be a very useful tool to cut down on the multitude of lines needed to code such things otherwise.

Summary

In this section, we reviewed the basic concepts of dynamically typed object-oriented languages and metaprogramming, each separately and then how one can employ metaprogramming techniques when working with said languages. We delved into common ways that metaprogramming appears as well as the specifics of three different object-oriented dynamically typed languages: Ruby, Python, and Perl. The benefits given by metaprogramming are far-reaching and numerous in nature due to the variety of possible combinations, extensions, or modifications, and it is strongly encouraged to learn to discern when and where techniques may be used instead of merely the specific ways that a particular language works.

References

[1] CSC/ECE 517 Fall 2010/ch2 S24 NS, Retrieved October 15th, 2010, from Textbook Wikipedia page: http://pg-server.csc.ncsu.edu/mediawiki/index.php/CSC/ECE_517_Fall_2010/ch2_S24_NS#What_is_Metaprogramming.3F

[2] Dynamic Programming Language, Retrieved October 15th, 2010, from Wikipedia page: http://en.wikipedia.org/wiki/Dynamic_programming_language

[3] Dynamic Typing, Retrieved October 15th, 2010, from Wikipedia page: http://en.wikipedia.org/wiki/Dynamic_typing#Dynamic_typing

[4] Duck Typing, Retrieved October 15th, 2010, from Wikipedia page: http://en.wikipedia.org/wiki/Dynamic_typing#Duck_typing

[5] Thomas, David, et al. "Coding like a Duck." Programming Ruby 1.9, the Pragmatic Programmers' Guide. Raleigh (N.C.): Pragmatic helf, 2009. p. 375.

[6] Ruby Metaprogramming Techniques, Retrieved October 17th, 2010, from: http://ola-bini.blogspot.com/2006/09/ruby-metaprogramming-techniques.html

[7] Domain-specific Language, Retrieved October 19th, 2010, from Wikipedia page: http://en.wikipedia.org/wiki/Domain-specific_language

[8] What is a Decorator, Retrieved October 19th, 2010, from PythonWiki page: http://wiki.python.org/moin/PythonDecorators#WhatisaDecorator

[9] Charming Python: Decorators make magic easy, Retrieved October 20th, 2010, from: http://www.ibm.com/developerworks/linux/library/l-cpdecor.html

[10] Perl 6, Retrieved October 17th, 2010 from Wikipedia page: http://en.wikipedia.org/wiki/Perl_6

[11] Anyone for Perl6 programming?, Retrieved October 16th, 2010, from: http://transfixedbutnotdead.com/2010/01/14/anyone-for-perl-6-metaprogramming/

[12] Ruby on Rails, Retrieved October 20th, 2010, from Wikipedia page: http://en.wikipedia.org/wiki/Ruby_on_rails

[13] Crossing borders: Domain-specific languages in Active Record and Java programming, Retrieved October 20th, 2010, from: http://www.ibm.com/developerworks/java/library/j-cb04046.html