mercredi 9 janvier 2008

Typage statique ou dynamique : la "Groovy Alternative"

Notion de programmation plus complexe qu'il n'y paraît, le typage est souvent la source de trolls (statique vs dynamique). Pour un petit rappel, l'article Wikipedia, ou un excellent "tuto" (plutôt une présentation en fait) sur le SDZ.

Basiquement, l'opposition entre typage statique et dynamique reposent entre autre sur les arguments suivants :
  • Le typage statique est lourd pour le programmeur, mais plus sûr.
  • Le dynamique est beaucoup plus agréable, mais plus "glissant" et peut-être plus difficile à tester.
Je rentre pas dans le débat... Je me pose seulement la question de l'influence du typage sur les performances (vitesse d'exécution) d'un langage : significatif ou non ?

Pour rappel, un petit tableau :

Typage Fort Faible
Statique Ada, Fortran C
Dynamique Ruby, Groovy Javascript

Le coup du typage fort ou faible : c'est assez relatif, je rentre pas là dedans...

Un exemple en Ruby :



# Test typage
def meth_4_hash hashhh
hashhh.each_key{|k|
puts k.to_s
}
end

conan = {
:dieu => "Crom !",
:arme => "Hache de bataille +4"
}

meth_4_hash conan
#meth_4_hash 455
# ==> erreur de type !

def meth_4_hash2 hashhh
if hashhh.is_a? Hash then
hashhh.each_key{|k|
puts k.to_s
}
end
end

meth_4_hash2 conan
meth_4_hash2 456

def autre_meth hashhh
raise "erreur de type" unless hashhh.respond_to? :each_key
hashhh.each_key{|k|
puts k.to_s
}
end

autre_meth conan
autre_meth 42 #error raised !


Il n'y a pas de type en Ruby : que des objets.

La méthode meth_4_hash doit "implicitement" recevoir un hachage, ou du moins un objet possédant la méthode each_key (doit pas y en avoir 50 !). Mais rien ne m'interdit à priori de lui donner autre chose à manger : 455 par exemple. Dans ce cas, le programme lève une exception et s'arrête.

Une façon de rendre son code plus "sûr" est de tester les entrées ou cibles de ses méthodes. Dans meth_4_hash2, le contenu de la méthode ne s'exécute que si l'input est du bon type (est une instance de la bonne classe plus rigoureusement...). Cette façon de "tester" en entrée de fonction permet ici d'éviter une exception, donc l'arrêt du programme...

... Mais ce n'est pas forcément satisfaisant. Si l'on souhaite être plus rigoureux, on se doit de lever soi-même les exceptions en cas d'erreur de type. C'est ce qui est fait dans autre_meth avec raise qui permet de lever l'exception avec un message qui pourrait être plus explicite (ici "erreur de type : un Hash est attendu en entrée" par exemple). A noter que dans mon exemple, cela comporte peu d'intérêt : il faudrait que le test soit placé en amont dans le code (avec une variable pas codé en dur...).

Groovy est un langage dynamique...



alcibiade = 5
println alcibiade.getClass()

alcibiade = [4,5,6]
println alcibiade.getClass()

assert alcibiade.getClass() == java.util.ArrayList


Tout ce qu'il y a de plus dynamique donc...

... mais peut être statique !



Integer alcibiade = 5
println alcibiade.getClass()

alcibiade = [4,5,6] // erreur !
println alcibiade.getClass()


En effet, sur la première ligne, dans un style "Java", on indique à l'interpréteur (ou au compilateur) que le type de alcibiade doit être statique : ne peut être modifié.

Groovyyyyyyyyyy !

J'aime bien cette fonctionnalité : en fait, j'apprécie assez quand un langage a un comportement par défaut connu (ici le typage dynamique), logique (en accord avec sa "philosophie", ses principes ou ses objectifs) et consistant ("ça marche toujours comme ça" : un minimum d'exceptions aux règles), mais qu'il permet aussi, moyennant un effort minimum, de faire autrement.

1 bon point pour Groovy !

2 commentaires:

Thyresias a dit…

Sympa. Une pitite suggestion:
Plutôt que is_a?, je pense qu'il vaut mieux utiliser respond_to? :

def autre_meth2 hashhh
raise "erreur de type" unless hashhh.respond_to? :each_key
hashhh.each_key{|k|
puts k.to_s
}
end

En effet, is_a? demande que l'objet passé respecte toutes les méthodes définies pour Hash, alors que la seule qu'on utilise est each_key. Avec cette modification, la méthode est plus générale, car elle accepte des objets qui ne sont pas forcément des Hash (en héritent), mais se comportent comme eux, pour ce dont on a besoin (ici each_key).

Sobe a dit…

Effectivement, c'est plus logique et rend la méthode plus générale.

Je corrige.