Nous n'avons pas besoin du typage statique

Note : Je me rends compte que j’aurais mieux fait de parler de typage implicite et de typage explicite plutôt que de typage dynamique et de typage statique. En effet, il s’agit bien moins de déterminer le type à la compilation ou à l’exécution que d’avoir à déclarer le type d’une valeur/variable ou de laisser le langage le déduire du contexte. S’il est vrai que le typage implicite est plutôt l’apanage des langages dynamiques et que le typage explicite est plutôt une propriété des langages statiques, OCamL est un contre-exemple flagrant à cette règle. En effet, OCamL est un langage statique fortement typé dans lequel tout le typage est implicite et entièrement déduit du contexte de déclaration. Ceci étant dit, je maintiens l’idée que le typage dynamique offre des possibilités que le typage statique n’offre pas. Mais je reconnais que ce n’est pas sans conséquence, en particulier sur la performance. Ainsi, si je suis formel sur l’affirmation que le typage explicite devrait disparaître, la disparition du typage statique reste encore sujet à discussion, au moins pour les quelques années à venir. Je n’ai cependant aucun doute sur l’idée que, les progrès matériels aidant, ce débat n’aura un jour plus court et que le typage dynamique l’emportera sur le typage statique[1].

 

Récemment, j’en suis venu à débattre avec un collègue des avantages et des inconvénients comparés des langages statiques et des langages dynamiques. Sa position, c’était que les langages dynamiques offrent davantage de moyens pour prévenir les problèmes de type. Exemple classique : j’appelle ma méthode/fonction/grenouille avec des valeurs d’un mauvais type. J’y passe une chaîne de caractère là où est attendu un entier, etc.

Ma position à moi est que se concentrer sur les types, c’est en fait se concentrer sur un faux problème. J’explique : qu’est ce qui est important lorsque je veux vérifier le type d’un objet ? On ne vérifie pas le type d’un objet juste pour se marrer. Ce serait complètement con. Non, ce qu’on cherche vraiment lorsque l’on vérifie le type d’un objet, c’est : cette opération est-elle possible sur cet objet ? En d’autres termes : puis-je appeler cette méthode sur cet objet ?

Et vous savez quoi ? On n’a pas besoin de typage statique pour vérifier ça. Il y a plusieurs manières de s’en assurer, du typage semi-statique au typage dynamique. Par exemple, Go et OCamL utilisent le typage structurel. C’est à dire que si votre type présente une  méthode avec le même nom et les mêmes paramètres de le type attendu, alors votre type est considéré comme étant du type attendu. OCamL, d’ailleurs, est un exemple brillant de langage compilé avec un typage fort dans lequel il n’est pas nécessaire de typer les valeurs. Java, lui, vous impose l’utilisation des interfaces. Putain, ce que je déteste les interfaces !

Mais les langages dynamiques sont clairement supérieurs de ce point de vue : duck-typing FTW. Le type de l’objet est déduit du contexte. Il peut être ou peut ne pas être possible de passer tel message à tel objet, mais ce n’est pas au langage de dire que je peux ou ne peux pas le faire. Possiblement, je peux même vouloir appeler une méthode qui n’existe pas sur un objet. Vous trouvez ça stupide ? Vous êtes stupides. Ça ne l’est pas. C’est ce que je disais dans mes notes sur la POO : appeler une méthode qui n’existe pas n’est pas toujours une erreur. Ça a des applications très utiles. Comme les dynamic finders de GORM. Cédric Champeau, l’un des développeurs principaux de Groovy, a d’ailleurs écrit un très bon article sur 10 choses qu’un langage dynamique peut faire et qu’un langage statique ne peut pas.

Mais il y a effectivement un problème : il est possible de faire, avec un langage dynamique, des choses pas naturelles qu’un langage statique ne permettrait pas. Parce qu’un langage statique vérifie tout le temps tous les types. Et qu’il est possible d’être fatigué un jour. Et qu’une variable reçue d’ailleurs peut être passée là où faut pas. Et que vous pouvez ne pas vous rendre compte que la variable était un entier et pas une chaîne de caractères. Et que vous l’avez passée à la mauvaise méthode… Bon… Les conneries, ça arrive, nan ?

Nope.

Ok, j’admet que mon collègue a marqué un point en m’affirmant que le typage statique ajoute une couche de sécurité que le typage dynamique ne fournit pas. L’argument, c’est : échouer tout de suite plutôt que plus tard. Et comme la compilation arrive avant — et potentiellement bien avant — l’interprétation, il semble raisonnable que l’application se vautre dès la compilation.

Ma réponse à son argument, c’était : on a les tests unitaires pour ça. Maintenant, je réalise que cette réponse n’était pas bonne du tout. Parce que, soyons réalistes : plus l’application est complexe, moins vous aurez le courage de tout tester à 100%. Et même à ce moment-là, y’a aucune assurance que les tests unitaires couvrent correctement le code. Parce que… ben… il arrive de se foirer. Parfois dans des proportions épiques. Et rien ne peut vous empêcher d’échouer lamentablement. J’imagine que nous, développeuses et développeurs, avons un tas d’histoires sous le manteau, de fois où nous étions si certains que le test était bon que nous en aurions mis les mains au feu… pour se rendre compte que le test testait du vent.

Donc oui, effectivement. Mieux vaut se foirer à la compilation qu’à l’interprétation. Mais vous savez ce qui arrive bien avant la compilation et l’interprétation ? L’analyse syntaxique de votre IDE. Eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeet… c’est pour ça que les devs Python ne se ratent pas plus que les devs Java[2].

Oh, bien sûr, vous pouvez entendre certaines personnes vous affirmer qu’eux ou elles sont des vraies, qu’eux ou elles utilisent emacs. Qu’eux ou elles n’utilisent pas de cliquodrômes.

Y’a probablement peu de choses que j’exècre plus que l’élitisme. Quand j’entends ce genre d’inepties, j’ai une irrépressible envie de cogner la tête de l’auteur tellement fort avec une clef à molette qu’elle se vaporiserait instantanément. Refuser de se simplifier la vie avec les bons outils ne fait pas de vous un bon développeur ou une bonne développeuse. Ça fait juste de vous une tête de bite.

Donc, à part cette catégorie particulièrement poisseuse, ils ne viendrait à l’idée d’aucune personne saine d’esprit l’idée d’écrire du code Java sans utiliser Eclipse — berk — NetBeans — attends… hein !? — ou le meilleur IDE de l’univers : IntelliJ[3]. Ne serait-ce que parce que développer un projet Maven sans IDE, c’est la plaie. Les IDE ont pris une place considérable dans la vie des développeuses et développeurs ces dernières années. L’existence d’un bon IDE est d’ailleurs un indice de bonne santé et de maturité d’un langage.

Et les langages modernes tendent à prendre le chemin d’une certaine forme de typage dynamique. Même les plus statiquement typés. C++ a introduit le mot-clef

auto

, Scala et Kotlin ont

val

et

var

, C♯ a

dynamic

. Même Java présente certaines formes de typage dynamique via la réflexion, l’effacement de type des génériques ou l’instruction

invokedynamic

.

Alors c’est quoi l’idée ? L’idée, c’est que les outils et les pratiques ont changé. Que les langages modernes se débarrassent de plus en plus des accolades ou des point-virgules parce que les compilateurs sont devenus suffisamment malins pour comprendre l’indentation ou les sauts de lignes comme structurants pour le programme. Mon argument, c’est que le typage statique, c’est comme les point-virgules : une relique du passé.

Notes de bas de page :
  1. Mais j’admets pouvoir me tromper…
  2. Je dirait même qu’ils ont tendance à moins se louper que les devs Java, d’ailleurs. Parce que développer en Java, c’est quand-même déjà un échec en soi. Comment ça je suis de mauvaise foi !?
  3. Ouais, ok, chuis un peu de mauvaise foi

Déjà 31 avis pertinents dans Nous n'avons pas besoin du typage statique

  • > Je me rends compte que j’aurais mieux fait de parler de typage implicite et de typage explicite plutôt que de typage dynamique et de typage statique.

    Ce qui change le débat du tout au tout. Tu devrais écrire ce P.S. au début de l’article ;-)

  • ctk
    Qu’on m’ôte d’un doute, dans « Mon argument, c’est que le typage dynamique, c’est comme les point-virgules : une relique du passé. » l’intention ne serait pas, par hasard, de dire « typage statique » ?
    Signé un gars qui aime pas quand ça plante à l’exécution dans la prod.
  • clem
    attention, il y a une erreur sur le lien « 10 choses qu’un langage dynamique peut faire et qu’un langage statique ne peut pas »
  • Cloug
    Un bon petit troll bien velu !

    J’ai eu beaucoup de plaisir à te lire …. :-) très documenté et je reconnais la discussion socratique mais à la réponse finale j’aurais eu tendance à dire même si je ne fais que du python : pourquoi aurions besoin de langage dynamique alors que nous disposons de l’inférence de type ! Ah contradiction quant tu nous tiens. Très bonne fin de semaine à toi !

  • steph
    Je vois de nombreux avantages au typage fort :
    – meilleure lisibilité du code
    – preuve à la compilation et non à l’exécution
    – suggestion de méthode et attributs dans les IDE
    – meilleure performant à l’exécution (au bas mot 10x)

    Je vois peu d’avantages au typage faible :
    – ne pas taper « int  » devant un nom de variable.
    Avantage qui disparaît avec l’inférence de type (comme en OCaml).

    Bref mon langage parfait serait python avec typage fort et inférence de type.

  • T
    Ouiaiaie !

    Excuse-moi mais je trouve l’article extrêmement confus dans les concepts. Et ce n’est sans doute pas sans rapport avec ta note du début où tu admets mélanger un peu dans ton argumentaire typage statique/dynamique et typage explicite/implicite. Pourtant ce sont des notions qui n’ont fondamentalement *rien a voir*.

    La question du typage explicite est un « simple » problème de construction de langage. Et effectivement les progrès théoriques ont permis aux langages objets statiquement typés de se passer de plus en plus du typage explicite, comme le montrent des langages comme le Scala. Les adeptes de langages fonctionnels souriront gentiment et signaleront au passage que les langages inclu la notion de sous-typage se heurtent à des limitations qui les empêchent de profiter des formes les plus puissantes d’inférence (voir par exemple http://stackoverflow.com/questions/7234095/why-is-scalas-type-inference-not-as-powerful-as-haskells).

    Au final, assez peu de monde plaide pour les bienfaits du typage explicite. Les quelques rares arguments que j’ai entendu se limitent à une soi-disante plus grande clarté du code. Ce qui reste franchement assez limité et est facilement résolu par une bonne instrumentation comme tu le fais justement remarqué.

    Bref le typage explicite est en déclin, et assez peu de personnes le pleureront. Par conséquent critiquer le typage statique en parlant de typage explicite est au mieux une erreur, au pire un sophisme de l’épouvantail.

    Par ailleurs, je pense que tu es un peu trop optimiste concernant les capacités des analyseurs statiques de code. En effet, un des énormes avantages des langages a typage statique est qu’il permettent un niveau d’instrumentation *qui n’est tout simplement pas possible* pour les typages dynamiques !

    Je t’invite a consulter les discussions qui ont lieu sur le sujet (par exemple http://lambda-the-ultimate.org/node/1519). A ma connaissance, les approches existantes se limite a un sous ensemble « acceptable » (comme https://code.google.com/archive/p/rpython/), ou se rapprochent du typage statique (et explicite !) au travers d’annotations de type (http://www.mypy-lang.org/).
    Cette dernière approche a d’ailleurs été poussée par Guido Von Rossum lui-même ! https://www.infoq.com/news/2014/08/python-type-annotation-proposal

    Il n’est pas anodin de voir qu’après une période de fort presse, beaucoup de développeurs s’intéressent de plus en plus aux langages statiques. L’exemple le plus frappant pour moi est le Typescript, qui introduit le typage statique au coeur de ce qui était pourtant le plus grand bastion du typage dynamique: le dev web frontend !
    Coïncidence amusante, ton article sort a peu près au même moment que la version 2.1 de Typescript. En lisant les commentaires élogieux des développeurs qui en parlent (voir https://news.ycombinator.com/item?id=13124033), on peut se dire que le typage statique à encore des beaux jours devant lui.

    Pour ma part, je vois deux avantages majeurs au système de typage statique.
    Le premier est qu’il joue le rôle une batterie de tests intégrée directement dans le code ! J’ai parfois l’impression que si je proposais a certains adeptes du TDD un système permettant la génération automatiques de tests garantissant une couverture exhaustive dans leur domaine a partir d’un système d’annotations qui permet de garantir que les tests évoluent en même temps que la base de code, ils sauteraient de joie. Or c’est *exactement* ce que propose un système de typage statique.
    Alors certes, certains systèmes statiques sont moins élégants, plus verbeux que d’autres. Mais rejeter le principe de le typage statique pour ca, c’est un peu comme rejeter le principe de faire des tests sous prétexte que certains frameworks sont mal fichus…

    Le second avantage, j’en ai déjà parler, c’est que le typage statique permet de fournir une instrumentation du code a la fois plus facile et plus complète. A ma connaissance, il y a certaines limitations à l’analyse de langages dynamiques qui sont fondamentales et ne pourront pas être complètement résolues. Voir par exemple ce message dans la discussion précédemment citée sur Typescript: https://news.ycombinator.com/item?id=13125863

    Pour résumer ma pensée, typage statique et dynamiques ont tous les deux leurs forces, et je les pense complémentaires:
    – le typage dynamique est très adapté pour les phases de développement *exploratoires*, où on veut itérer et pouvoir évoluer rapidement sans encombrer de détails
    – le typage statique est puissant dans les phases de *consolidation*, lorsque l’on veut s’assurer de la qualité du produit et de pouvoir le faire évoluer sans risquer de régression

    C’est pour cela que je trouve les approches hybrides particulièrement intéressantes, et ce n’est pas un hasard que les initiatives dans ce sens se multiplient. En plus de mypy pour le Python et des initiatives de typage statique pour js (ce que permet d’une certaine manière Typescript), on peut aussi citer le « optional typing » de Clojure.

    A mon sens, a l’avenir beaucoup de langage permettront de régler le niveau de typage désiré (au travers d’annotations optionnelles par exemple). Cela permettra de concevoir avec un même langage des systèmes composés d’un noyau dur typé statiquement entouré de code « glue » moins important donc moins typé, un peu a la manière des combos C/Cpp + bash/python/lua que l’on observe aujourd’hui

  • T
    Je vais essayer de ne pas trop rentrer dans les diatribes interminables, mais simplement ajouter quelques points en complément (mon premier commentaire était beaucoup trop long, en fait je l’ai soumis accidentellement alors que je voulais le réduire).

    Tout d’abord je ne voulais pas dire que tu confondais les deux concepts (statiques vs explicite), je voulais dire que ton billet portait sur les deux, ce qui a mon avis rendait le billet (et non pas toi) confus. Je m’excuse si la formulation a pu paraitre insultante, ce n’était pas mon propos.

    Concernant Javascript et Typescript, je suis tout a fait d’accord avec l’article de Sam. Le js est un langage des plus mal foutus, et il est tout a fait possible de faire un langage dynamique qui soit autrement plus élégant et cohérent. Par contre il me semble que ton argumentaire sur le Typescript correspondrait plutôt à un langage comme Coffescript (que tu cites). Le Typescript lui revendique clairement offrir des avantages liés au typage statique, comme on peut le lire sur la page officielle du projet:

     » Types enable JavaScript developers to use highly-productive development tools and practices like static checking and code refactoring when developing JavaScript applications.

    Types are optional, and type inference allows a few type annotations to make a big difference to the static verification of your code. Types let you define interfaces between software components and gain insights into the behavior of existing JavaScript libraries. »

    Après chacun décide s’il estime les avantages avancés réels et suffisamment intéressants…

    Par contre attention à la suite de ton argumentaire. Autant mon opinion sur les langages « hybrides » n’engage que moi, autant je trouve tes exemples mal choisis (je laisse le dev web de côté, même si tu omets de noter le fait qu’historiquement beaucoup d’applis web « entreprises » ont leur backend en Java).

    En effet dans les cas que tu cites , le Python est justement principalement utilisé comme *wrapper* autour de noyaux développés dans d’autres langages !
    – pour Spark, la lib python n’est qu’une API pour faire appel a la plateforme qui est codée en Scala.
    – Pour numpy (la très fameuse lib python de calcul scientifique): le coeur de l’application est en C (performances oblige)
    – Pandas s’appuie sur numpy, et est donc sujet à la même remarque, de même que SciPy et Scikit-learn a ma connaissance.

    Dans le domaine du machine learning, on peut aussi trouver ce genre de fonctionnement avec Tensorflow de Google (coeur en C++, wrapping en Python), Torch (coeur en C, wrapping en Lua).

    On voir donc que Python (ou des langages équivalents), loin d’être d’un contre-exemple « flagrant », est en fait assez souvent utilisé comme langage « glue » autour d’un cœur critique qui sera plutôt codé dans un langage statique (ce qui permet d’obtenir de meilleures performances). C’est justement ce genre d’exemples qui m’a amené a mes réflexions sur le potentiel de langages « hybrides » qui pourraient jouer les deux rôles.
    Après sur ce point il s’agit d’avis plus personnels qui n’engagent que moi.

    Concernant ton dernier point et ton challenge avec l’exemple de code java, tout d’abord permet moi de dire qu’arguer des faiblesses des langages statiques en tapant sur Java, c’est un peu comme arguer des faiblesses des langages dynamiques en tapant sur Javascript. C’est pas fair-play de s’attaquer aux handicapés ! :D
    Je te mets au défi de reproduire cette « anomalie » dans un langage avec un système de type beaucoup plus puissant, comme OCaml ou Haskell.

    Par ailleurs, tu n’as pas donné ton exemple de code dynamique qui correspond fidèlement à l’implem’ Java que tu donnes et qui retourne le résultat attendu.
    Pour ma part je suis arrivé (avec beaucoup de mauvaise foi), au code Python (3) suivant:

    def foo(x): # only strings please!
      return 1
    
    def foo(x): # only integers please!
      return 2
    
    def foo(x): # anything goes
      return 3
    
    arr = ['a string', 1337, 1.0]
    
    for o in arr:
      print(foo(o))
    

    Qui retourne (bien entendu) le résultat:

    3
    3
    3
    

    Autant dire que c’est pas beaucoup mieux >:->

    Alors certes je fais exprès, et il existe du code Python beaucoup plus idiomatique (et beaucoup moins stupide) qui retournera le résultat attendu. Mais il en va évidemment de même pour le Java (et ne parlons pas de l’excellent concept de Pattern-matching, offert par tous les langages à typage statiques modernes qui se respectent).
    Cela dit je reste sincèrement curieux de voir quel code tu avais en tête !

    En relisant mon commentaire, je m’aperçois que je me suis finalement lancé dans des diatribes interminables, je suppose que mes commentaires sont voués à être aussi verbeux que des langages à type explicite !

  • T
    PS: désolé ma tentative de formatage de code à raté lamentablement. Du coup les indentations dans mon code Python ont sauté :/ (heuresement elles sont triviales)
  • Celti
    Oui. Du coup, j’ai levé le pied sur les rendus à faire en HTML5 pour me consacrer à d’autres sujets moins… *coding*… et je pense utiliser ton article, rien que pour en faire un glossaire terminologique pour michu :-) Enfin… dès que je trouve un peu de temps #libre… :-)
  • jacti
    Serais-tu content de recevoir un missile sur la tête juste parce qu’à l’exécution il y a une erreur de type ?
    Le typage statique est INDISPENSABLE pour certaines applications et dans certains environnements (systèmes complexes à logiciel prépondérant comme les centrales nucléaires, le guidage de missile, certains système d’armes, etc.)
  • Pour moi les meilleurs typages sont les typages statiques forts avec inférence de types, comme en Golang, Scala.
    Comment aimer le typage dynamique ? Quel est l’avantage de ne se rendre compte d’un bug que lorsqu’il est exécuté par le client car les tests ne couvrent pas tous les cas, alors que le compilateur d’un langage au typage statique oui, et sans avoir à ecrire un seul test ?
    De plus quand je code avec des langages au typage faible je ne sais jamais ce que je manipule, ce que telle fonction est censée prendre en argument, ce qu’elle retourne, alors qu’ avec un typage fort un simple hover sur la fonction en question et la réponse apparaît tout de suite en affichant la signature de la fonction.
  • Watchinofoye
    Je suis d’accord avec l’auteur, dans le sens où un langage à types explicites ne protège pas systématiquement des erreurs de type. Par exemple, rien ne peut garantir que la taille d’un int sera toujours la même d’une plateforme à l’autre. Il peut être sur 4 octets mais il peut aussi être sur 2 ou 8 octets, voire 1 octet (oui, c’est chelou, mais ça existe).
    Pour autant, je trouve qu’il n’est pas très sage de se reposer sur l’IDE pour essayer de déceler des erreurs de type. De la même façon que je ne ferai jamais entièrement confiance à un compilateur ou un interpréteur.

    Perso, j’ai été habitué à expliciter les types. Si je peux le faire en Python, je le fais. Sauf si ça me complique la tâche inutilement (pour un petit script écrit à l’arrache, c’est pas la peine).

Les commentaires sont fermés.