samedi 12 janvier 2008

Métaprogrammation et Sorcellerie

Le nom est effrayant. "Métaprogrammation". Brrrr !

Dans son très bon article, Giles Bowket essaie de mettre en avant, par diverses citations de Rubyistes reconnus, dont _Why et Russ Olsen (interview - auteur de "Design Patterns in Ruby"), le fait que la métaprogrammation n'est pas un concept obscur et arcanique.

Il y a quelques temps, je vous donnais un lien vers un bon petit tutoriel à ce sujet.

De fait, en Ruby, la métaprogrammation est encouragée, souvent transparente et très utile.

Encouragée, car finalement assez simple syntaxiquement (voir exemples ci-dessous), et surtout déjà implémentée !

Transparente. Si si. Un exemple entre mille : les attr_accessor. Un attr_accessor en début de classe implémente automatiquement les méthodes d'accès en lecture et en écriture d'une variable d'instance donnée. Le saviez-vous ? Pour ma part, j'utilisais les attr bien avant que ce soit le cas !

Très utile. Que celui qui n'a jamais tapé "toto.methods.sort" dans irb me jette la première cacahouète nucléaire. Avant d'avoir la moindre idée de ce qu'était la métaprogrammation ou l'introspection, j'utilisais des méthodes associées à ces concepts (redéfinition de méthodes, inspection des méthodes d'un objet, inclusion d'un module ou non dans une classe, respond_to?, etc...).

Un premier exemple :

Pour montrer l'utilité, et défendre la métaprogrammation contre ses accusations de sorcellerie, ce code implémente 2 fonctionnalités sympas :
  • method_added (méthode de classe) réagit ici à la (re)définition de méthodes pour la classe String. Ici par exemple, on souhaite que ma_meth ne soit pas redéfinie ultérieurement, et on soulève donc une exception dans ce cas. Il existe également method_removed se construisant sur le même schéma dans le cas de suppression de méthode (via undef).
  • method_missing est appelée en cas d'appel à une méthode d'instance inexistante. Si ça c'est pas utile... A noter qu'il s'agit cette fois d'une méthode d'instance.



class String
def ma_meth
# Pour x raisons, cette methode ne doit
# pas etre modifiee
puts "C'est pas faux..."
true
end

def self.method_added(sym)
if sym == :ma_meth
raise "Pas de modif autorisee pour ma_meth !"
else
puts "Methode #{sym} (re)definie"
end
end


def method_missing(m, *params)
puts "Utilise des methodes existantes ! TOJO !!!"
end
end

a = String.new "toto"

puts "Methode inexistante"

a.lalalalaleeeeere

puts "Redefinition"
class String
def ma_meth
puts "autre..."
false
end
end
#erreur soulevee

a.ma_meth


Alors, où est la sorcellerie ?

Dans les références présentées en début d'article, il est dit que la métaprogrammation ne doit pas être désignée par une "pratique avancée". Giles Bowkett propose de dire simplement que c'est une "nouvelle technique" plutôt qu'avancée. Modestement, je serais tentée qu'il s'agit simplement d'une fonctionnalité avancée du langage : cela ne signifie nullement qu'elle serait réservée aux programmeurs "avancés", mais plutôt que le langage est "avancé".

On en reparlera...

0 commentaires: