Tuesday, February 21, 2006

The Way of Meta - Part III

The Way of Meta

~

V. 0.0.3

 

Writing Method Generators

I am going to write a simple example that illustrates how you can create a method generator: a construct used within a class definition as a shortcut to generate richer method semantics, in the same way that attr_accessor generates enrich class behaviour by adding new methods.

This example concerns the development of a 'synonym' generator.   A synonym is similar to an alias because it provides different calls to achieve the same result.  It is different from an alias in that an alias generates a copy of a method, rather than a different name to call the same method.

If an aliased method gets overridden, its aliases will keep calling the old code.   Conversely, a synonym of a method is a simple light code layer that keeps calling a certain method name.  If the aliased method gets overwritten, then all its synonyms will start calling the new version of the method.

Let's start from our intent; let's visualise how we would like to use synonyms.

class Person

    def say

        "hi"

    end    

 

    def walk

        "one step"

    end

 

    synonym      :say   => [ :talk ,   :discuss , :greet ],

                 :walk => [ :stride, :run,     :jump  ]

end

 

Here we are declaring that both the method 'say' and the method 'walk' have several synonyms.

We also expect the following statements to be true

p1 = Person.new

 

p p1.talk       == "hi"

p p1.discuss    == "hi"

p p1.greet      == "hi"

 

p p1.stride == "one step"

p p1.run    == "one step"

p p1.jump   == "one step"

 

They should become false when we override the old methods:

class Person

    def say

        "yo"

    end

end

p p1.say        == "yo"

p p1.talk       == "yo"

p p1.discuss    == "yo"

p p1.greet      == "yo"

 

Enough talking, we have all the intent we need for a first stab at the problem.   Let's dive straight into the code.

First of all we need a convenient way to process the arguments passed to the 'synonym' generator.

Consider how we will need to elaborate the following hash:

    :say   => [ :talk,   :discuss, :greet ],

    :walk => [ :stride , :run ,     :jump   ]

 

This is just a convenient idiom that we use to express the following relationships:

    [

    [ :say,  :talk   ],

    [ :say,  :discuss],

    [ :say,  :greet  ],

    [ :walk, :stride ],

    [ :walk, :run    ],

    [ :walk, :jump   ]

]

 

Let's write some code that can take us from the first compact format to the second explicit representation.   We will call the relationships 'associations' and we will provide a method that allows us to iterate over a hash one association at a time.

# expands a => [b1,b2,b3] associations to [a,b1], [a,b2], [a,b3], etc..

class Hash

    def each_association &block

        self .each_pair do | assoc_key, assoc_targets|

            assoc_targets.to_a.each do |assoc_target|

                block.call assoc_key, assoc_target

            end

        end

    end   

end

 

Now that we have a way to visit our associations, we will generate a new synonym method for each association:

class Module

    # associations: ( old_name => [new names] )*

    def synonym associations

        associations.each_association do

            | original_name_sym, new_name_sym|

           

            define_method( new_name_sym){ |* args|

                self .send original_name_sym, *args

            }

           

        end

    end

   

end

 

For each association specified in the synonym generator we inject a new method in our class.   The new method has the name specified on the right hand side of the association, and all it does is to invoke the original method of the class.

The synonym generator has been defined within the Module class, so that it becomes automatically available within the context of the classes 'Module' and 'Class'.

We can now use the generator even to expand existing classes with new developer friendly names.  

Here we tweak the Array class:

Array.class_eval do

    synonym :size => [ :count, :n_elements ]

end

 

p [1, 2,3 ].count         == 3

p [1, 2,3 ].n_elements    == 3

 

And here we provide a way to talk to a Person, rather than giving him orders!

class Person

    def calculate( a,op,b)

        a.send op, b

    end

end

 

p (p1.calculate 3, :+, 2) == 5

 

class Person

    synonym :calculate => :how_much ?

end

 

p (p1.how_much? 3, :+, 2) == 5

 

It is worth pointing out a small syntactic trick that we used to handle both cases of single ( :calculate => :how_much ?) and multiple synonyms (:size => [ :count , :n_elements ]).

 

We get the right hand side of the hash element and we apply 'to_a' to it, turning it into an array.

            assoc_targets.to_a.each do |assoc_target|

            end

 

If the element is not an array, it gets turned into one.   If it is already an array, it is left unchanged.  Whatever the case, we are left with a simple array to operate on in the end.


 

Sunday, February 19, 2006

The Way of Meta - Part II

The Way of Meta

V. 0.0.2

Exploring Ruby Metaprogramming Capabilities

 

Getting our Feet Wet

Tired of all this talk?  Then it is time to dip our feet in the deep waters of rubesque metaprogramming.  We are going to start our journey exploring the metaprogramming features that are illustrated in the pragmatic programmers' PickAxe book, while at the same time we will go through the less known libraries present in the ruby distribution.  At the same time we will ask for some help to the wonderfully brief and clear 'Seeing Metaclasses Clearly' by Why the Lucky Stiff.

 

Out-of-the-Box Metaprogramming Mechanisms

Ruby provides several straight out of the box metaprogramming mechanisms with its core standard libraries.  Although using these mechanisms can take you very quickly to unmanageable levels of complexity it is worth looking at them to have a feeling of what is possible.  Later on we will encapsulate these mechanisms within our own metaprogramming protocol.


Loading Code

In many languages, the mechanism that allows your source code to refer to a library or to another piece of code is handled by the language in an opaque way.  Not so in Ruby.

When using Ruby you are in charge of the code loading.

The 'load' command loads some ruby code, right where you are calling it.

 

load "myfile.rb"

 

The 'require' command adds a little check that prevents the loading of the same file twice from different points in the code.

 

require "filename"

 

Load and require are not special commands for the compiler, they are dynamically executed and can be embedded within blocks, control statements, etc.  The name of the file that they load can also be constructed dynamically.

The load construct in particular allows you to reload code definitions any number of times if they get changed inside the file.  It's now easy to see how your code could generate code files, maybe using a templating mechanism, and then load them back in the runtime.

 

Principles of Ruby Metaprogramming I: Source Code Files are Dynamically Loaded, not Statically Included

 

There are two environment variables that can turn out to be helpful when dealing with dynamic source code loading.

$LOAD_PATH is an array of directories that are searched for source code by 'load' and 'require'. 

$LOADED_FEATURES is an array containing the filenames of the files that have already been loaded.




The Way of Meta - Part I

I might have mentioned it, but I am planning to write a book on Liquid Development.  A big part of Liquid Development is about shaping code and knowledge after our own linguistic structures.  Metaprogramming becomes a technique instrumental to shape and bend a language to your wishes.

Today I am going to post the first part of a rough draft on metaprogramming that I am putting together for the book.

The Way of Meta

V. 0.0.1

Exploring Metaprogramming in Ruby

I have been a fan of metaprogramming techniques for a long time, but I have been able to fully indulge into this deplorable habit only after having taken up programming in Ruby.

What's the deal with this metaprogramming thing?  There are lots of tutorials and introduction out there on the web, so I am not going to go into the historical details here, but I will give you my own personal definition of metaprogramming. 


Metaprogramming as Deep Magick

Metaprogramming has meant different things at different times, but at any particular time what metaprogramming seems to mean, is the ability of somehow getting into the inner working of the language that you are using, so that you can produce what is sometimes referred to as "deep magick".  It does look and feel like magick, because when you go 'meta' with programming, you are moving outside the realm of everyday modelling, to step into the world that defines the very rules of ordinary programming.

Many of the concepts of object orientation feel like metaprogramming the first time you see them.  I clearly remember the sharp feeling of power and magick the first time I saw C++ operator overloading in action as a young man.  It felt like I could plunge into the core of the language, inside the very definition of the metaphysics of programming and change the way I could represent the world, on a whole new level.

 

Metaprogramming considered Harmful

The term metaprogramming covers lots of ground, and it represents more a philosophical focus than a specific set of techniques.  The capabilities of Metaprogramming can be potentially so powerful and far reaching that can also be completely devastating.

If you are able to tweak the underlying layer on top of which you build your representations, you can change the semantics of all the relationships that you have built between your 'ordinary' conceptual objects.  This can add new features (aspects) to your program, or it can simply make it wrong, breaking its intent, or it can make it totally unworkable and inconsistent.  It could also cause unforeseen interferences between your meta-code and the program, between two different sets of code, or within the libraries your code relies on.

 

Metaprogramming on the Leash

So, is Metaprogramming a good or a bad thing?  I would say that it is good if used parsimoniously, when no other conventional technique can yield the same elegance and results.  It should also be used when it can help removing duplication in your code, while adding clarity to it.

However it should be used in a very controlled way.  Metaprogramming almost always involve some degree of run-time creation and customisation of code that gets injected into the realtime machinery of your program.  The code generated by your 'meta-code' is usually invisible to the debugger and you will not have any chance to inspect it and debug it from the inside.  I leave it to you to decide how dangerous and error prone this can be.

All your metaprogramming code, once verified that it is working, should be abstracted away into a set of more rigid mechanisms, with a precise name and meaning that can be put under test and documented.  All these mechanisms should also support ways to output the code they generate and to probe the realtime looking for potential lexical conflicts.

A simple example of 'controlled metaprogramming' is the attr_accessor mechanism in Ruby.  When inside a class you declare a symbol as being an attr_accessor with

 

 attr_accessor :my_symbol

 

you are using a metaprogramming technique that generates a @my_symbol attribute, and both a getter and setter method for that attribute.

 

Metaprogramming Protocols

Playing with the laws of reality can become a very messy thing very quickly; even more so when you are not the only one doing it.

What happens if you override the method_missing mechanism of the Object class to implement your flavour of metaprogramming, only to discover that a library you were using was leveraging on the very same technique and you just interfered with it?

You will soon start applying your meta-code as a sequence of patches that assume the presence or the absence of other libraries and become rigidly dependent on which libraries are present and ion their loading order.

When you start playing with metaprogramming you will soon come to realise that you don't even need third party code to mess things up.  You will probably perfectly manage to bring havoc on yourself within a few visits to the metaphysics underlying the Kernel and Object classes.

A Metaprogramming protocol could be the only way to get things in order at this point.  A metaprogramming protocol offers an orderly way to access to some of the more dangerous and interference-prone mechanisms of the meta-layer.  Rather than acting directly on the meta-mechanisms, you ask a meta-framework to do it for you, while specifying exactly what you need.  The meta-framework will take care of registering your request and to handle the interference between similar requests coming from different libraries.  The meta-framework will usually act as a kind of multi-dispatching system with a call-back mechanism on its callers.



Monday, February 13, 2006

Digging into Ruby Symbols - O'Reilly Ruby

Another very simple intro to Ruby metaprogramming

Read more at www.oreillynet.com/ruby...


Sunday, February 12, 2006

Haskell

I really should spend some time and learn the language. I did it at Uni but I didn't dig it at the time and I haven't really done much with it. As I get older and I start appreciating the functional flavour I should have a go at it.

Read more at perl.com/pub/a/2005/09/...


Saturday, February 11, 2006

Ruby Social Club

Roberta and I have organised a nice evening amongst Ruby programmers in the Rome area on Thursday night.  It has been a good evening, spent at a pub in the centre of Rome drinking lager, eating nice food and talking about our experiences.  I will write more about it, but for the time being I just want to thank everybody that turned up.  It was good to meet up with so many good people sharing my very same interests.

Here are a couple of web pages both in italian and english, describing what we Ruby socialites are up to.
 



Beyond mainstream object-oriented programming

Thanks to Gabriele Renzi for pointing this out on his blog.

This is a nice article on interesting thoughts around and beyond OOP.

Read more at jaortega.wordpress.com/...


Thursday, February 09, 2006

Ruby Metaprogramming

This is an excellent OSCON presentation written by Vanderburg: www.vanderburg.org/Spea...

After reading it, I have started publishing some draft excerpts on Ruby Metaprogramming from the 'Liquid Development' book that I am writing:

The Way of Meta - Part I
The Way of Meta - Part II
The Way of Meta - Part III



Sunday, February 05, 2006

Distilling Intent from Descriptions

This is a follow up to my previous posts on the use of intents to describe the game of maru batsu (well, maru-batsu sounds much cooler than tic-tac-toe :-)

Every now and again I practice this small maru-batsu kata to see how my perception of things changes in time as I learn new ways of approching the encoding of intent.

Something that I notice by reading my early posts is that, while the code is not bad, I tend to get too technical too early.  I know I shouldn't abstract too early.  I know I shouldn't create abstractions unless my intents prove that I need them (this process of spawning abstractions is described in Shape Code After Language). 

Yet I always fall into the sin of over-abstraction.  It's not a big deal; sometimes it's just a matter of abstracting a Player class, because I feel it's a natural element of the maru-batsu discourse.  They taught us to make classes out of concepts, just in case we might need them.

More I practice this kata, more I realise that I can do with less and less.  It feels pleasant to feel how much lighter the intents (and the code) become.  The Player doesn't need to be a class.  Most of the times it can simply be a symbol, and the way it is used can be made implicit by the context and illustrated by intents and stories.

Today I will practice the kata one more time, in an iterative way.  Be patient, the code doesn't need to run as-it-is, as it is just a first exploratory attempt at designing intents.

This is a first rough natural language description of maru batsu:
Then I get each statement in turn and I try to make it a bit more formal.  I will also put some comments of problems that I noticed only after the fact.

"maru batsu is played by two players on a 3x3 board"
    expect marubatsu.players.size == 2
    expect marubatsu.board.width == marubatsu.board.height == 3

Why players.size when I could have n_of_players ?  Why did I feel the need to make explicit the inner structure of marubatsu?  This just creates just more coupling without any immediate additional benefit.  An ethnographer would probably point out that I am giving implicit importance to the concept of Player.  I am probably reifying  players because they belong to my everyday universe of discourse.  The same line of reasoning applies to board.width.  These declarations of structure are actively implying a universe of games made out of boards and players.  While this might be a useful abstraction, it is an abstraction that I did not produce consciously.  It just seemed to be the 'natural' abstraction.  I erroneously perceived it as being 'the-way-the-real-world-is'.

"a player uses X marks, while the other uses O marks"
    expect (marubatsu.symbol_of :player_A) == :X
    expect (marubatsu.symbol_of :player_B) == :O

Here I resisted using strings to represent X and O.  They are symbols, they stand for things, for marks on the board.  They represent universal concepts.  They relate to the fly-weight structural pattern.

Yet I used 'symbol_of :player_A' rather than ' symbol_of_player_A'.  Again, why did I feel the need to abstract this, as if I could want to play maru batsu with a variable number of players?  It's so hard to forget your symbolic perceptual stance and see things as they are.  I wonder if reading again Drawing on the Right Side of the Brain could help me to get back to a more immediate reality.

"the players take turns"
    marubatsu.turn?.should_be :player_A
    marubatsu.next_turn!
    marubatsu.turn?.should_be :player_B
    marubatsu.next_turn!
    marubatsu.turn?.should_be :player_A

Again I implicitly assume a multitude of players.  Why not 'is_turn_of_player_A?'

"a player, in his turn, can put his mark on the empty squares of the board"
    marubatsu.turn?.should_be :player_A
    dont_expect marubatsu.board.has_mark_at? row=2, col=1
    marubatsu.board.mark! row=2, col=1
    expect (marubatsu.board.mark_at? row=2, col=1) == :X


Besides the over-complex designs that I have already pointed out, I have managed to do a couple of nice moves here.  I use the ' row=2' parameter passing notation to specify the meaning of the number passed.  I also changed an initial 'marubatsu.board.mark_at?(row=2, col=1) ' to '(marubatsu.board.mark_at? row=2, col=1)'.  There is no semantic difference between the two notations, but I prefer the second one as I like to visualize what is inside the brackets as a single entity on which I do an operation.  In the former case the brackets were used to delimit a function parameters.  I tend to prefer to think in terms of entities than in terms of flows of data between functions.  This doesn't mean I don't like the functional style of programming.. mmh this is an ambiguous point I'll have to expand on in a future post on Stanzas and Dyamic Aggregates.

Take note of the fact that the sentence "a player, in his turn, can put his mark on the empty squares of the board" also implies that "a player, in his turn, can NOT put his mark on the NON empty squares of the board". I am going to state this intent as well.

"a player, in his turn, can NOT put his mark on the NON empty squares of the board" 
    marubatsu.turn?.should_be :player_A
    expect marubatsu.board.has_mark_at? row=1, col=1
 
    expect_exception( :SquareUsedException ){
        marubatsu.board.mark! row=1, col=1
    }


To show that this intent holds, we state that we expect an exception in case we try to tick twice the same square of the maru batsu board.  From a technical point of view, it's nice to be able to state explicitly what kind of exception I am expecting here.  However, from a legibility point of view, something different might have been better.  Something like "cannot do marubatsu.board.mark! ..., because :SquareUsed" might work even better!

"a player that aligns three of his symbols (Horizontally, vertically or diagonally) on the board wins."
 
    marubatsu.set_board [:X, :O,:O],
                        [:X, :-,:-],
                        [:X, :-,:-]
                       
    expect marubatsu.winner?
    marubatsu.winner.should_be :player_A
   
    marubatsu.set_board [:O, :O,:O],
                        [:X, :-,:-],
                        [:X, :-,:-]
                      
    expect marubatsu.winner?
    marubatsu.winner.should_be :player_B

    marubatsu.set_board [:X, :O,:O],
                        [:-, :X,:-],
                        [:-, :-,:X]
                       
    expect marubatsu.winner?
    marubatsu.winner.should_be :player_A


It might have been better to split this intent into three different intents, each one specifying one way to win (horizontal, vertical, diagonal).  Since I didn't want to have to play out a whole game to check winning conditions, I introduced a set_board verb that allows the injection of a board into the game.  I wonder if this is really a good thing.  From a TDD point of view it is a good thing since it introduces flexibility into the design, but this is not flexibility that is needed to describe the game play, so I am not so sure about it.  I am not sure about a valid alternative either, so I'll keep it for the time being.

Another problem is that the examples are not exhaustive at all.  I do not show the whole range of winning combinations, and the combinations that I show are not shown for all players.  There should be 8 (3+3+2) winning combinations for each player, for a total of 16 combinations.  In this simple game I could even enumerate all of them, but what about more complex games?  How would you test moves in a game of chess?

I like the way I set up the board using arrays, and how I resisted making up Board and Cell objects.  I don't like the fact that I tried to stay abstract using symbols and using :- to signify a blank cell.  It would have been much clearer to use strings.  Something like:

    marubatsu.set_board "XOO",
                        ".X.",
                        "..X"

or even something like

    marubatsu.set_board <<-board
                            XOO
                            .X.
                            ..X
                                                      board
 
which would then have the burden to decode the multiline board string.  While this may be too heavy for this simple case, this approach could also be used to introduce small DLS-like dialects inlined within the code.

As in a previous intent "a player that aligns three of his symbols (Horizontally, vertically or diagonally) on the board wins." also implies the following converse intent:

"there is no winner if three equal symbols are not aligned (Horizontally, vertically or diagonally) on the board"
 
    marubatsu.set_board [:-, :-,:-],
                        [:-, :-,:-],
                        [:X, :-,:-]

    dont_expect marubatsu.winner?

    marubatsu.set_board [:-, :X,:-],
                        [:-, :O,:-],
                        [:X, :O,:X]

    dont_expect marubatsu.winner?

but how can you 'prove' that there is no winner? how can you prove that there is a winner without enumerating all cases?  Do I need to prove things or do I simply need to enumerate some examples useful to elaborate working code?

"if nobody has won and no new moves are possible, then it's a draw."
 
    marubatsu.set_board [:X, :O,:O],
                        [:O, :X,:X],
                        [:X, :X,:O]

    dont_expect marubatsu.winner?
    expect marubatsu.draw?

again, should I enumerate all possible draws?

should I also state all the following?

"if someone has won then it's not a draw."
 
    marubatsu.set_board [:O, :O, :O],
                        [:X, :-, :-],
                        [:X, :-, :-]
                       
    expect marubatsu.winner?
    dont_expect marubatsu.draw?

"if nobody has won but new moves are possible, then it's not a draw."
 
    marubatsu.set_board [:X, :O, :O],
                        [:O, :X, :X],
                        [:X, :-, :O]

    dont_expect marubatsu.winner?
    dont_expect marubatsu.draw?



This page is powered by Blogger. Isn't yours?