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
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
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
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
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:
- maru batsu is played by two players on a 3x3 board
- a player uses X marks, while the other uses O marks
- the players take turns
- a player, in his turn, can put his mark on the empty squares of the board
- a player that aligns three of his symbols on the board (Horizontally, vertically or diagonally) on the board wins.
- if nobody has won and no new moves are possible, then it's a draw.
"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
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?