[Atom] [Mail] [Twitter]
Liens : git · hacks · divers · cabale · buzz! · à propos +
Au menu
\/

De la douloureuse productivité du programmeur

#. Par bluestorm. Publié le 28.08 2010 à 17:04. Aucun commentaire.
autodidacte débutants formation pratique programmation

Depuis quelques billets, je suis coupable d’une forme de lâcheté et de procrastination tout à fait scandaleuse. En effet, je réponds rarement aux commentaires que les gens écrivent sur mes billets.

Dans le fond, je trouve que c’est très mal, mais j’ai aussi des excuses. D’une part, je publie souvent mes billets un certain temps après leur écriture, ou du moins le début de leur rédaction. Je suis très content quand j’ai l’impression qu’ils intéressent les gens, et je lis leurs commentaires avec avidité, mais je suis parfois moi-même un peu décalé et je renâcle devant l’effort de me replonger dans le bain, pour avoir une bonne vue d’ensemble du billet et pouvoir répondre à un commentaire en particulier.

D’autre part, vu la taille de mes billets, j’essaie plutôt de me faire discret dans la section commentaires. Après avoir attendu avec impatience les premiers commentaires, l’envie est forte d’y répondre une tartine sur le champ, mais il faut aussi savoir laisser les gens s’exprimer pour rendre du recul et ne pas écraser la conversation.

Sachez en tout cas que ce n’est pas parce que je ne réponds pas aux commentaires que je ne les vois pas. Je les lis tous, et ça me fait plaisir d’en avoir, alors n’hésitez pas à continuer.

Ce présent billet est en fait né comme une réponse à un commentaire. En effet, bien qu’il ne soit pas de moi, je me sens concerné par les commentaires sur le billet La voie de l’autodidacte (1/2). terres y a fait une remarque sur une des questions que pose rz0, remarque se plaçant en opposition à ma propre réponse. C’est intéressant et ça m’a donné envie de développer un peu plus mon point de vue. Ce qui est né comme un commentaire un peu plus long que d’habitude est finalement devenu un billet un peu plus court que les autres.

Pour bien replacer le contexte, je vais citer ici la question de rz0, la partie de ma réponse qui est concernée, et enfin la partie commentaire qui m’a fait réagir.

Question de rz0 :

Cela m’a pris un bon nombre d’années (je dirais bien six ou sept ans au moins) avant de produire du code utilisable « dans la vie ». Pour moi, il y a donc une phase improductive nécessaire, durant laquelle on apprend sans se soucier du résultat, juste pour apprendre, avant la phase productive, où, par définition, on tente de produire quelque chose (et l’on doit y sacrifier une partie du temps que l’on aurait pu passer à apprendre).

Le même constat vous a-t-il frappé ? Y a-t-il eu des rechutes dans l’improductivité ? Si oui, comment vous en êtes-vous sorti ?

Ma réponse :

Ce n’est pas l’état improductif qui est anormal ; c’est l’inverse ! Pendant l’apprentissage, tout est excitant, on essaie de nouvelles techniques et on apprend de nouvelles choses. Quand on essaie de produire quelque chose de définitif, il faut se mettre à se soucier de tous les détails pénibles (documentation utilisateur, distribution…). C’est encore pire quand on a des utilisateurs ! Se forcer à finir un produit, c’est accepter de passer du temps sur des choses sans intérêt, pour en conterpartie être utile à quelqu’un d’autre que soi-même.

Le commentaire de terres :

Contrairement à bluestorm, j’ai hâte de passer dans une phase productive pour pouvoir tester mes compétences et me rassurer sur mon niveau.

Et aussi pour ne pas avoir passé environ trois ans à apprendre sans rien produire.

En tant qu’amateur de programmation, quand j’écris un programme je m’intéresse au processus de développement, au code, etc. J’ai aussi souvent un regard de développeur sur les programmes des autres : je regarde leur code, comment ils ont transcrit leurs idées.

Par rapport à un programme, on peut aussi avoir une position d’utilisateur : on regarde le produit fini, ce qu’il fait et comment interagir avec lui. Ce sont deux aspects orthogonaux, mais que l’on peut combiner en appréciant les programmes à la fois d’un point de vue développeur et d’un point de vue utilisateur ; c’est d’ailleurs souvent comme ça que j’approche les logiciels libres.

Le terme « productif », sorti du contexte, est assez vague. Il désigne essentiellement le fait de faire un effort dans une direction avec efficacité : arriver à son but, et si possible rapidement.

Il y a donc deux ici dimensions de productivité :

  • Productivité pour le développement : est-ce que j’arrive à écrire du code propre et de qualité ? Est-ce que mon programme tient la montée à l’échelle, c’est à dire qu’il peut se complexifier et grandir en conservant une structure propre et modulaire ? Est-ce que je peux arriver à une version correcte du code, sans passer par un grand nombre d’étapes intermédiaires incorrectes et de cycles de corrections de bugs ? Est-ce que mon code est facile à maintenir et permet l’ajout de fonctionnalités ?

  • Productivité pour l’utilisation : est-ce que les fonctionnalités utiles sont présentes ? Le programme est-il facile à installer et à utiliser ? Y a-t-il des utilisateurs satisfaits de ce programme ? Est-ce qu’il est utile ?

La définition de rz0 est légèrement ambiguë : « produire du code utilisable dans la vie ». Est-ce qu’il est utilisable parce qu’il satisfait des standards de qualité, ou est-ce qu’il est utilisable parce qu’il est destiné à être utilisé ?

Alp semble traiter principalement le premier aspect, la productivité de développement. Ma réponse se concentrait au contraire sur le second aspect, le rapport aux utilisateurs.

Pour moi, un programme est destiné à être utilisé si l’on est dans l’une des situations suivantes :

  1. On se force à développer un programme dans le but de l’utiliser soi-même et-ou de pousser d’autres gens à l’utiliser. Situation courante chez les développeurs de petits projets libres.

  2. On est forcé par quelqu’un d’autre à écrire un programme qu’il souhaite utiliser pour répondre à ses propres besoins. Situation courante dans le monde de l’industrie, mais aussi pendant un stage, etc.

Bien sûr, il y a aussi des cas limite où la personne ayant besoin du programme est en fait un groupe de personne auquel on appartient. Je pense que pour bien distinguer les deux situations (ou un intermédiaire entre ces deux situations), il faut se poser la question suivante : « Va-t-il vous arriver quelque chose de désagréable si, finalement, le programme ne répond pas aux besoins ? »

Attention, je ne parle pas de la deuxième situation comme d’une forme d’exploitation ou de torture où le développeur est maltraité. Au contraire, je pense que c’est celle des deux que je préfère : un peu de contrainte pour garder sa motivation, des utilisateurs extérieurs avec qui discuter pour aider à résoudre certains choix délicats, et surtout la sensation agréable de travailler à améliorer la vie d’autrui. Bien sûr, il faut que le sujet soit intéressant.

Cependant, dans ces deux cas, ces programmes sont placés sous le signe de la contrainte. Et, dans ces deux cas, atteindre un stade où l’utilisation est agréable demande de faire des efforts sur un grand nombre de petits détails qui ne sont pas forcément intéressants ni même agréables, et dont on n’avait pas besoin de se soucier tant qu’on s’intéressait surtout à l’expérience de développement, la qualité du code, etc.

C’est une expérience différente, qu’il est bon d’avoir fait ou de faire, et qui est elle aussi très formatrice. Cependant, je la trouve tout de même sensiblement moins intéressante que l’expérience du développement du cœur du programme. C’est ce que j’ai essayé de faire passer dans ma réponse : apprendre c’est bien et on est libre, être utile c’est bien aussi mais il y a des contraintes qui vont avec.

Enfin, il est possible d’avoir le meilleur des mondes, en participant à un projet où ce sont d’autres personnes qui font l’interface avec les utilisateurs. Dans ce cas, surtout si notre participation concerne le cœur du programme, elle sera évaluée sur des critères de productivité de développement au moins autant que sur son action concrète sur le résultat du programme. C’est une des raisons qui fait qu’il est agréable et intéressant de travailler dans des projets où les exigence de qualité sont fortes.

[ tag:blog.huoc.org,2009:bluestorm/13 ]
Aucun commentaire · Commenter

/\ \/

Utiliser et configurer XMonad

#. Par bluestorm dans Administrivia. Publié le 18.07 2010 à 16:49. 6 commentaires.
<. Administrativia
administration gestionnaire_de_fenêtres haskell

Comme beaucoup d’utilisateurs d’interface graphique, je n’aime pas beaucoup la souris. Cela fait un certain temps que j’effectue la plupart des tâches avec principalement le clavier, en particulier tout ce qui doit être fait rapidement. La gestion des fenêtres en fait partie : on a envie de pouvoir passer très vite d’un logiciel à un autre (le classique Alt-Tab), d’un bureau virtuel à un autre, etc.

Cela fait donc quelques mois que j’utilise mon gestionnaire de fenêtres (quel qu’il soit, en pratique Kwin ou Xfwm selon l’humeur, mais à mon stage j’utilise Metacity) principalement avec le clavier. De là vient l’idée naturelle : pourquoi ne pas utiliser un gestionnaire de fenêtre pensé pour être utilisé avec le clavier ? Ce serait certainement encore plus adapté.

J’ai donc envisagé plusieurs alternatives (wmii, etc.), sans les tester (la flemme), et j’ai finalement choisi XMonad. C’est hype, c’est minimaliste, c’est codé dans un langage que j’aime bien (Haskell), bien conçu (Zipper et tout le tralala), bien documenté, ils insistent sur le fait d’avoir peu de lignes de code (j’aime bien), et ils essaient d’exprimer formellement et de vérifier des propriétés de leur programme (pas avec Coq, on ne peut pas tout avoir, mais avec QuickCheck).

J’ai donc fait l’effort d’installer XMonad, de lire la documentation de base (je n’ai retenu au départ que cinq raccourcis). Il y a quelques glitches (par exemple au lancement emacs n’occupe pas tout l’espace de sa fenêtre), mais qui n’en a pas ?

J’ai pris un peu de temps pour le configurer, en pratique ça veut dire installer et utiliser deux petits programmes qui « font une seule chose et le font bien », conseillés par la documentation XMonad :

xmobar

une petite barre de tâches que l’on configure en lui envoyant des infos sur l’entrée standard, simplissime.

dmenu

un outil de lancement de programme tout simple (on tappe le nom de l’exécutable jusqu’au moment où on peut auto-compléter, et on fait « Entrée ») ; écrit par les gens de suckless, qui sont cools et aiment Plan9.

Je me suis fait un fichier de configuration XMonad qui résume tout ça, au cas où ça vous intéresse. Dans la suite de ce billet, je détaillerai cette configuration. Ça n’est certainement pas une référence sur l’utilisation de ces outils, j’ai d’ailleurs sans doute fait une ou deux bêtises dans ces configurations, mais le but est de montrer que c’est plutôt facile, au cas où ça motiverait des vocations.


La base de la base

Pour installer XMonad, on peut télécharger la version de développement du code et l’installation à la main, ou passer par le gestionnaire de paquets de son système d’exploitation. La seconde version est évidemment plus simple.

Dans tous les cas, on se retrouve avec une installation plutôt de base, et en particulier un fichier de configuration essentiellement vide. Pour lancer XMonad, il suffit de partir d’une session X sans gestionnaire de fenêtres (par exemple en ayant lancé xinit depuis un terminal) et de lancer xmonad. Comme tout repose sur le clavier, il faut avoir à l’avance une idée des raccourcis ; man xmonad est là pour ça. D’après mon expérience, mod-j (changer de fenêtre), mod-shift-p (lancer une commande) et mod-space (changer de mode d’affichage) sont les seules commandes à retenir pour débuter.

mod-j, mod-k passer le focus à la fenêtre précédente/suivante
mod-[1..9] changer de bureau virtuel
mod-shift-[1..9] déplacer la fenêtre sous le focus vers un bureau virtuel
mod-space changer d’agencement des fenêtres
mod-p lancer dmenu
mod-shift-p lancer une commande

L’inconvénient du fichier de configuration minimaliste, c’est qu’il faut rajouter tout ce qu’on veut configurer en plus. On peut le faire en lisant la doc mais c’est assez délicat : je trouve qu’on s’en sort beaucoup plus vite en bidouillant un exemple qui fonctionne déjà. J’ai donc téléchargé le modèle de configuration XMonad, un long fichier Haskell qui fait la même chose que la configuration par défaut, mais plus bruyamment. On peut alors jeter un coup d’oeil aux différentes parties de la conf, et bidouiller ce qui nous plaît.

Le reste du post est organisé comme une description du diff entre ma configuration XMonad et ce modèle (template) fourni : un bout du diff, et un commentaire sur la signification de la chose, et éventuellement les informations et les fichiers liés.


Imports sans grand intérêt

+import XMonad.Layout.NoBorders
+import XMonad.Hooks.DynamicLog
+import XMonad.Hooks.ManageDocks

Trois imports de bibliothèque en haut du fichier de configuration. Certaines parties de la configuration que j’ai changées dépendent de ces imports. Vous allez voir.

Dans XMonad, la fenêtre qui a le focus est entourée d’une fine et élégante bordure rouge. NoBorders permettra de configurer les dispositions de fenêtres (Layout) pour que les fenêtres en mode plein écran ne soient pas entourées : une telle fenêtre étant seule à l’écran, on se doute bien qu’elle a le focus.

ManageDocks permettra de réserver un peu d’espace d’affichage pour la barre de tâches xmobar. DynamicLog servira à envoyer en continu à cette barre de tâches des informations sur le système.

Changement du terminal par défaut

 -- The preferred terminal program, which is used in a binding below and by
 -- certain contrib modules.
 --
-myTerminal      = "xterm"
+myTerminal      = "gnome-terminal"

Attention à ne pas confondre les -, + du diff, et les -- qui dénotent un commentaire Haskell.

J’utilise gnome-terminal par défaut, parce qu’il affiche bien l’unicode (j’utilise beaucoup de lettres grecques dans mes programmes) et possède des tabs, ce qui reste bien pratique. Les gens de suckless, que j’ai déjà mentionnés car ils s’ocupent de dmenu, ont un outil qui s’appelle tabbed, qui permet en théorie d’apporter des tabs de l’extérieur à des programmes individuels, c’est sympa mais j’ai la flemme d’essayer.

Choix de la touche mod pour les raccourcis

-myModMask       = mod1Mask
+myModMask       = mod3Mask

La touche nommée mod dans la parlance XMonad est la touche centrale, utilisée pour la grande majorité des raccourcis claviers. C’est une touche abstraite, et on peut choisir la touche physique du clavier à laquelle elle correspond. On pourra l’associer par exemple à la touche « Contrôle » pour avoir des raccourcis de la forme ctrl+j, ou à la touche « Alt » pour avoir alt+j.

En réalité, l’association n’est pas si directe, elle passe par le serveur X, qui reçoit de votre clavier des keycodes (« code des touches » ?), et les transforme en keysyms (« symbole des touches » ?), qui sont ensuite envoyés aux applications. Dans XMonad, on choisit quel keysym représentera la touche mod, mais on peut aussi changer la touche en amont en s’attaquant directement à la traduction des keycodes, qui est faite par xmodmap, un outil du serveur X.

C’est d’ailleurs ce que je fais : j’ai mis le symbole mod3 comme touche mod dans XMonad, mais j’utilise xmodmap pour envoyer la touche « Caps lock » vers le symbole mod3. C’est fait dans un petit script, que j’ai nommé startxmonad.sh, qui appelle xmodmap juste avant le lancement de xmonad :

setxkbmap us intl
xmodmap -e "remove Lock = Caps_Lock"
xmodmap -e "add Mod3 = Caps_Lock"
xmonad

On charge la disposition par défaut du clavier, on réassigne la touche Caps_Lock au keysym Mod3, et le tour est joué.

Bureaux virtuels

-myWorkspaces    = ["1","2","3","4","5","6","7","8","9"]
+myWorkspaces    = with_tags ["web", "bibli", "code", "redac"]
+                where with_tags li = li ++ map show [(1 + length li)..9]

XMonad a l’équivalent des bureaux virtuels, qu’il appelle workspaces. On peut se déplacer dans le bureau virtuel numéro N avec le raccourci mod-N (mod-1, mod-2…). Par défaut, le nom de chaque workspace est son numéro, et c’est ce que ma barre de tâches affiche. Ma modification sert à donner aux quatre premiers bureaux des noms plus mignons et sémantiques, qui reflètent l’utilisation que j’en fait. La ligne where sert à montrer que je maîtrise le Haskell à fond, et que les bureaux suivants gardent leur numéro jusqu’à 9.

Légères modifications des dispositions des fenêtres

 -- The available layouts.  Note that each layout is separated by |||,
 -- which denotes layout choice.
 --
-myLayout = tiled ||| Mirror tiled ||| Full
+myLayout = avoidStruts (tiled ||| Mirror tiled ||| noBorders Full)

Comme dit au moment des imports, XMonad permet de choisir entre plusieurs dispositions de fenêtres différentes. J’ai repris les modes déjà présents (un mode plein-écran, Full, et deux modes avec une fenêtre principale, occupant la moitié de l’écran, et des fenêtres secondaires), en la modifiant légèrement. noBorders retire l’encadré rouge du mode plein écran, et avoidStruts sert à réserver de l’espace pour la barre de tâches ; je ne l’ai pas inventé tout seul, je l’ai lu dans la documentation.

Ajout de cas particuliers de fenêtres flottantes

 myManageHook = composeAll
     [ className =? "MPlayer"        --> doFloat
     , className =? "Gimp"           --> doFloat
+    , className =? "Display"        --> doFloat
+    , className =? "XVroot"         --> doFloat
     , resource  =? "desktop_window" --> doIgnore
     , resource  =? "kdesktop"       --> doIgnore ]

Par défaut, XMonad essaie de faire en sorte que chaque fenêtre occupe le maximum d’espace disponible. C’est chouette en général, mais ça peut devenir très laid quand on veut par exemple voir une image, et qu’elle est redimensionnée, sans respect des proportions, pour remplir parfaitement la place libre entre les fenêtres existantes. doFloat sert à rendre certaines fenêtres flottantes (non redimensionnées), et className =? fait visiblement un test sur la classe de la fenêtre X. Je ne connais pas les subtilités des fenêtres X, mais il y avait des lignes déjà présentes pour montrer l’exemple, et j’ai lu sur internet qu’il fallait utiliser l’utilitaire xprop pour récupérer la classe d’une fenêtre X. J’ai rajouté display et xv, mes visionnaires d’image en ligne de commande préférés (saviez-vous que display d’ImageMagick gère même le format de graphes dot quand il est compilé avec les bonnes options ?), et ça marche.

Configuration de xmobar

 -- Run xmonad with the settings you specify. No need to modify this.
 --
-main = xmonad defaults
+main = xmonad =<< xmobar defaults

main est la grosse fonction de Haskell, le moment où on lance carrément XMonad et tout. Je l’ai modifiée comme suggéré par la documentation de DynamicLog, pour qu’elle envoie des informations à xmobar, ma barre de tâches.

xmobar est un petit programme qui affiche une barre de tâches, lui aussi écrit en Haskell. Il faut le configurer indépendamment, concrètement j’ai mis le fichier suivant sous le nom ~/.xmonad/xmobarrc :

Config { font = "xft:DejaVu Sans-8:"
       , bgColor = "black"
       , fgColor = "grey"
       , position = Top
       , lowerOnStart = True
       , commands = [ Run StdinReader
                    , Run Network "eth0" ["-L","0","-H","32","--normal","green","--high","red"] 10
                    , Run Network "eth1" ["-L","0","-H","32","--normal","green","--high","red"] 10
                    , Run Cpu ["-L","3","-H","50","--normal","green","--high","red"] 10
                    , Run Memory ["-t","Mem: <usedratio>%"] 10
                    , Run Date "%a %b %_d %Y %H:%M:%S" "date" 10
                    ]
       , sepChar = "%"
       , alignSep = "}{"
       , template = " %StdinReader% }{ %cpu% | %memory% | <fc=#ee9a00>%date%</fc>"
       }

Je l’ai encore une fois adapté d’une configuration donnée dans la documentation. En gros, la disposition de la barre ressemble à ce qui est marqué dans template, et les définitions / configurations sont celles du champ commands. La partie StdinReader lit sur l’entrée standard, c’est-à-dire les informations données par xmonad, et affiche donc, sans que je lui ai rien demandé, mes bureaux virtuels en mettant en valeur celui sur lequel je me trouve, le nom de la disposition de fenêtres courantes, et le titre de la fenêtre qui a le focus. Le reste, c’est moi qui l’ai configuré comme un grand, ce sont des stats sur l’utilisation du CPU, de la mémoire (ça sert à rien mais c’est classe), et l’heure qu’il est (très utile par contre).

Utilisation avec Gnome

À mon stage, j’utilise une machine debian avec Gnome. Je pourrais installer autre chose, mais je n’aime pas beaucoup faire de l’administration sur mes heures de travail, et j’avais envie d’essayer pour voir comment ce bureau progresse.

À l’occasion d’un changement de machine, j’ai essayé la combinaison Gnome+Xmonad : utilise Gnome comme gestionnaire de bureau, mais avec Xmonad à la place de Metacity comme gestionnaire de fenêtre. C’est très facile à mettre en place, la démarche est expliquée dans la documentation. J’ai utilisé la solution la plus simple, à savoir créer /usr/share/applications/xmonad.desktop et /usr/share/xsessions/xmonad.desktop, et choisir Xmonad depuis GDM.

Ça donne un environnement assez agréable : la barre de tâches et la plupart des utilitaires Gnome sont actifs, ce qui est pratique pour avoir par exemple l’applet NetworkManager depuis notre session Xmonad (on pourrait peut-être coder quelque chose d’équivalent pour xmobar). mod-p appelle le lanceur d’application Gnome (que je trouve en réalité moins agréable que dmenu car plus intrusif), donc l’intégration est plutôt bonne.

Pour mon portable, qui a un petit écran 13", je préfère la sobriété et la finesse d’une xmobar configurée à la spartiate, mais je pense que les amateurs de gnome seraient satisfaits de la combinaison Gnome+Xmonad.

Multi-écran

Toujours à mon stage, j’utilise deux écrans. XMonad a, de base, un bon support du multi-écran : il met un bureau virtuel sur chaque écran, et on peut facilement changer le focus d’un écran à l’autre ou déplacer des applications.


Le mot de la fin

Voilà, j’ai fait le tour de ma configuration XMonad. C’est vraiment pas très compliqué. Est-ce que ça vaut le coup ?

Ce dont j’avais peur, c’est que ces outils (les tiling window managers) soient trop élite pour moi : le truc qu’il faut comprendre en profondeur pour pouvoir utiliser, et après on est super-productif de la mort, mais il faut commencer par se farcir des kilomètres de doc. En pratique ce n’est pas du tout l’expérience que j’ai vécue avec Xmonad, j’ai juste retenu deux ou trois raccourcis indispensables et bidouillé un peu la configuration en copiant-collant lâchement ce que je trouvais sur le net, et en quelques essais j’ai quelque chose de simpliste qui me convient bien.

Je pense que je suis essentiellement aussi productif qu’avant, avec Kwin ou Xfwm, et c’est vrai que je m’embête moins à agrandir mes fenêtres, les changer de bureau ou d’écran, etc. L’avantage principal reste le sentiment satisfaisant et rassurant d’utiliser un outil simple, que l’on peut modifier si l’on veut, et adapté à l’usage qu’on en fait.

[ tag:blog.huoc.org,2009:bluestorm/12 ]
Voir les commentaires · Commenter

/\ \/

Administrativia

#. Par bluestorm dans Administrivia. Publié le 17.07 2010 à 23:40. 6 commentaires.
>. Utiliser et configurer XMonad
administration méditation

Les spécialistes d’un sujet comprennent dans l’ensemble très bien que le reste du monde n’est pas spécialisé dans leur domaine. Ce qu’ils ont souvent du mal à comprendre, et essaient de combattre, c’est que le reste du monde ne cherche pas à devenir spécialiste, que ça ne l’intéresse simplement pas.

Dans le monde des hobbyistes de l’informatique, cette stupeur est largement répandue. Je l’ai longtemps partagée moi-même : comment, mon explication claire et simple sur les bienfaits d’utiliser Mozilla Firefox plutôt qu’Internet Explorer ne vous intéresse pas ? Vous ne trouvez pas important de pouvoir étudier le code des programmes qui tournent sur votre machine ? Vous n’êtes disposés à faire aucun effort pour retenir que non, on ne dit pas « je lance Google » quand on démarre un navigateur internet ? Les non-informaticiens sont d’une ignorance profonde, et on dirait qu’ils font exprès : quand on leur donne une occasion de la combler, ils s’empressent de ne pas en profiter.

Pour nous, spécialistes bénévoles persuadés que l’informatique est indiscutablement la voie de l’Homme Moderne, cette situation est intolérable. On fait des efforts pour éduquer, intéresser, faire apparaître l’esprit critique (car après tout, les logiciels libres sont successeurs des Lumières) des pauvres malheureux qui nous entourent.

Attention, la dure révélation arrive : ce contrariant état de fait est normal. Chacun ne peut s’intéresser qu’à un nombre fini de sujets à la fois, et l’informatique (ou tout sous-domaine véritablement passionnant) ne fait pas partie des heureux élus chez les gens qui vous entourent. Ça ne les intéresse pas, ils ne vont faire aucun effort pour apprendre quelque chose dans ce domaine, et ils sont dans leur droit le plus légitime. Si vous les forcez (par exemple en leur faisant utiliser un logiciel élitiste qui les force à comprendre la notion d’arborescence de répertoires), vous êtes une nuisance pour eux. Il faut le comprendre, l’accepter, et digérer.

Et dans le fond, chacun fait comme ça. Moi par exemple, je ne m’intéresse exactement pas aux matchs de basket. Je ne suis la progression d’aucune équipe, je n’ai aucune idée des tactiques utilisées et de ce qui fait l’intérêt du jeu, des joueurs importants, de l’organisation des tournois, etc. Imaginez la déprime du passionné de basket qui essaiera de m’intéresser à ce sport !

Pourtant le sport, comme tous les domaines dans lesquels on peut s’intéresser et se perfectionner, peut fournir des centres d’intérêt sincères, prenants et efficaces. Il y a une scène dans le film récent « Dans ses yeux » où le personnage principal, venu retrouver un ami poivrot dans un bar, assiste à une longue explication d’un des piliers locaux qui est une encyclopédie vivante sur son club de foot préféré; sa passion force le respect.

Plus proche de mon domaine, les questions de matériel informatique m’ennuient mortellement. Je ne veux pas savoir que tel site a publié un comparatif entre le processeur i17 Ultra PowerSafe et le disque SeaShore ESSID 3, et jusqu’à il y a quelques semaines je n’avais aucune idée de la tête d’un cable HDMI ou même DVI. D’ailleurs, dans le domaine spécifique du tuning d’ordinateurs, je suis pire que désintéressé, je suis malveillant : je pense secrètement qu’ajouter un néon bleu à son unité centrale avant de la montrer à ses potes est une activité stupide. C’est mal, et c’est un préjugé que je regrette.

Il y a une idée maintenant bien connue (je m’excuse d’avance si elle est devenue une platitude pour vous) que j’avais trouvée très intéressante quand je l’ai découverte. c’est la « technologie invisible » : une bonne technologie est une technologie qu’on ne voit pas, dont on n’est pas conscient de la présence. Selon cette idée, l’informatique aura vraiment réussi quand les gens auront oublié la présence des ordinateurs qui les entourent, de même que la majorité des gens ignorent complètement le petit monde mécanique situé sous le capot de leur voiture, ou la provenance des aliments qu’ils consomment.


L’administration de mon ordinateur est une activité sur la tangente. En général, elle ne m’intéresse pas et fait partie des nombreuses chose que j’oublie. Mais il m’arrive de m’y intéresser, et alors c’est toujours la frustration qui m’anime. Quand une mise à jour logicielle, nécessaire à priori pour pouvoir jouer avec la dernière version de tel ou tel programme qui m’intéresse, casse mon système, je suis très agacé et j’essaie de réparer ça rapidement.

La frustration qui me pousse à faire le plus d’efforts, c’est celle qui provient des outils que j’utilise. Elle est rampante, la plupart du temps étouffée, mais qui s’échappe parfois et fait des ravages. Elle est suivie par une période de doute, de réflexion et de recherche des alternatives, et s’arrête souvent à ce stade : je perds des dizaines de minutes à chercher ce qui se fait de mieux dans le domaine, et après avoir lu quinze descriptions d’alternatives révolutionnaires, je décide que j’ai la flemme de les essayer, et je repars satisfait pour un temps.

Régulièrement, je passe temporairement à la case suivante : changer quelques trucs. Je publierai ici quelques retours d’expérience, dans l’espoir que ça serve à d’autre, ou que ça les aide à se motiver.

[ tag:blog.huoc.org,2009:bluestorm/11 ]
Voir les commentaires · Commenter

/\ \/

Macaque

#. Par bluestorm. Publié le 20.02 2010 à 13:17. 4 commentaires.
bases_de_données caml macaque recherche sql

C’est la saison des webbeux sur Ours & Hippy. Le blog fait peau neuve (qui ressemble beaucoup à l’ancienne, je vous l’accorde) et bluestorm vient nous parler (non sans aigreur et dédain pour l’aspect Web) de Macaque, sa petite création qui permet de marier en douceur OCaml et SQL… Quel rapport avec le Web, me direz-vous ? Parce que PHP/MySQL, hahaha… que je suis drôle. (Cette dernière proposition à elle seule me fait penser que je me conno-ifie…) —rz0

En ces périodes d’activité intense, il est de plus en plus difficile de prendre le temps d’écrire un bon gros billet comme on les aime. Je me permets de vous servir ici un billet un peu réchauffé : j’ai écrit un article sur Macaque, et j’en profite pour en parler ici.

Macaque a déjà été mentionné sur ce blog, mais sans vraiment d’introduction concrète. J’espère ici présenter Macaque de façon plus introductive, et surtout vous donner le lien vers l'article (18 pages) et vous inciter à le lire.

Macaque

Qu’est-ce que c’est que Macaque ? Puisque la paresse préside ce billet, je me permets de citer, verbatim, le résumé et une partie de l’introduction de l’article.

Macaque est une bibliothèque OCaml permettant d’interagir avec un serveur SQL. Elle permet de construire des requêtes vérifiées statiquement, de façon modulaire. Une extension Camlp4 apporte une syntaxe concrète inspirée des compréhensions. Des types > fantômes sont utilisés pour encoder, en utilisant les types objets d’OCaml, certaines propriétés fines des valeurs SQL, comme la nullabilité.

Les bases de données sont des méthodes solides et reconnues de stockage d’information pour les besoins d’une application. Un programmeur qui voudrait s’en servir est cependant confronté à une situation désagréable : il doit communiquer avec un programme externe (le serveur de base de données) en lui envoyant des requêtes en format texte, c’est-à-dire oublier, dans cette partie de son programme, tout le confort des données structurées et des moyens d’expression de son langage.

La méthode la plus flexible pour construire des requêtes SQL est la production d’une chaîne de caractères correspondant à la requête :

let interroge table predicat =
  "SELECT * FROM " ^ table ^ " WHERE " ^ predicat

Son inconvénient majeur et qu’elle transporte toutes les données sous forme de chaînes de caractères, qui n’apportent aucune information de typage, donc aucune vérification de correction (même syntaxique) à la compilation. En particulier, il y a facilement des problèmes de sécurité si des portions de la requête peuvent provenir d’un utilisateur malicieux du programme.

La méthode la plus sûre pour écrire des requêtes est de vérifier leur validité, au moment de la compilation, en interrogeant directement le serveur SQL, comme le fait le projet PG'OCaml :

let interroge predicat =
  PGSQL(dbh) "select * from ma_table where $predicat"

Pour que cette vérification statique soit possible, la requête doit contenir assez d’information : le serveur PostgreSQL sait vérifier le type des données mais ne dispose ni d’un moteur d’inférence sophistiqué, ni d’une forme de polymorphisme. En particulier, on a dû préciser ici la table étudiée, ma_table. On ne peut pas écrire de fonction générique interroge, qui fonctionne sur n’importe quelle table. On a donc perdu en flexibilité.

Quels sont les compromis acceptables ? Macaque est un langage de requête, embarqué dans OCaml, qui se veut à la fois sûr et flexible. C’est une extension syntaxique couplée à une bibliothèque logicielle à l’interface fortement typée, qui permettent d’écrire des requêtes modulaires en conservant la sûreté à laquelle sont habitués les programmeurs OCaml.

Macaque est un logiciel libre, disponible sur le site OCamlForge.

L’article présente Macaque à des utilisateurs potentiels (Oui, vous !), mais discute aussi de son implémentation. En particulier, la section dédiée au traitement de l’opération GROUP BY décrit les méthodes de métaprogrammation utilisées pour renforcer la sûreté statique permise par le typage.

La petite histoire

J’ai écrit Macaque au cours d’un stage de deux mois dans le laboratoire PPS. Cela ne vous dit sans doute rien, mais beaucoup les connaissent indirectement, car ce sont eux qui hébergent la principale version en ligne du livre « Développement d’applications avec Objective Caml », DA-OCAML pour les intimes, un cours OCaml pas forcément très accessible pour les débutants, mais très complet et qui met les pieds dans le plat de la programmation fonctionnelle.

Une partie des gens de ce laboratoire travaillent sur un ensemble d’outils pour le développement Web dans le langage OCaml. C’est le projet Ocsigen, dont le but est grosso-modo d’apporter la beauté de la programmation fonctionnelle aux sauvages qui codent des sites Web. Comme vous pouvez vous en douter, la diffusion de leur travail est restée plutôt restreinte, mais il y a des choses intéressantes dedans et je vous invite à jeter un coup d’oeil ; personnellement, j’ai abandonné ce terrain depuis longtemps et je ne suis pas impatient d’y remettre les pieds.

Toujours est-il que le sujet qu’ils m’ont proposé était intéressant, puisqu’il combinait beaucoup d’OCaml, du typage, de la métaprogrammation, et plus globalement le travail sur la conception d’une partie d’un langage de programmation, ce qui reste un des points qui me plaisent le plus en informatique.

J’y étais encadré par Jérôme Vouillon, un type très sympathique qui a su m’aider, malgré sa terrible timidité. Sa connaissance du langage OCaml m’a parfois impressionné ; il m’a permis en particulier de résoudre un problème de typage assez velu :

module M : sig
  type 'a t
  val of_option : 'a option -> 'a t
  val to_option : 'a t -> 'a option
end = struct
  type 'a t = 'a option
  let id x = x
  let of_option, to_option = id, id
end

Ce module a l’air tout à fait inoffensif, mais il ne permet pas de conserver le polymorphisme de la valeur None, naturellement de type 'a option :

# let pas_assez_polymorphe = M.of_option None;;
val pas_assez_polymorphe : '_a M.t = <abstr>

Une toute petite modification suffit. Voyez-vous laquelle ?

C’est d’ailleurs un problème que j’aurais dû reconnaître, puisqu’il est mentionné dans un article que j’ai déjà survolé plusieurs fois, Relaxing the value restriction (12 pages).

La rédaction de l’article a eu lieu après le stage proprement dit, et, malheureusement pour moi, sur mon temps libre. Je dirais que cela représente environ 40 heures de travail. Il n’est pas du tout exhaustif, et même pas aussi complet que je l’aurais souhaité : après la première phase de rédaction, une grande partie de l’effort de relecture/retravail avait pour but de le raccourcir pour respecter les contraintes de taille. En pratique, cela constitue à enlever les explications, certains exemples de code, une partie du contenu, et surtout à rogner le plus possible sur les espaces verticaux du document.

Chaque paragraphe a donc été soigneusement retravaillé pour être le moins compréhensible possible, tout en contenant toute l’information nécessaire pour que l’on puisse expliquer à un éventuel critique que s’il n’a pas compris quelque chose, c’est qu’il n’a pas lu assez attentivement. L’étape suivante, utilisée par les copistes avant le septième siècle, est de supprimer la ponctuation et les espaces entre les mots.

Une petite anecdote : le nom Macaque, légèrement incongru, puise sa légitimité dans la formule tout à fait naturelle « MAcros for CAml QUEries ». Je l’ai trouvé un soir, tard et fatigué, au cours d’une discussion sur IRC pendant laquelle quelques bonnes âmes ont subi et commenté mes tentatives douteuses d’obtenir une abbréviation rigolote en combinant les représentants du champ lexical du chameau. Je les en remercie (il me semble me souvenir de la participation, entre autres, de Dark-Side, Katen, lasts et Cygal).

Quel futur pour Macaque ?

Je continue à maintenir Macaque sur mon temps libre. La force de développement est donc plutôt réduite, puisque le temps que je lui accorde est faible, mais elle est pour l’instant largement supérieure à la demande : à part le projet Ocsigen, personne ne semble pour l’instant envisager d’utiliser Macaque, et je n’ai donc pas de demandes d’utilisateurs. Pas de retours, pas de développement.

J’ai encore quelques idées de choses à ajouter à Macaque. J’en ai un peu parlé sur le canal IRC #ocaml, et flux m’a fait des remarques qui ont conduit à l’ajout, par exemple, des valeurs par défaut à l’insertion.

Je pense que Macaque est une bonne solution pour le problème qu’il essaie de résoudre : générer des requêtes vers une base de données depuis le langage OCaml. Pour cette raison (et aussi peut-être un peu d’ego), je serais heureux de voir plus de gens s’en servir, profiter de ses qualités, et se plaindre de ses défauts. Je n’ai pas non plus fait beaucoup d’efforts pour populariser Macaque et pousser des gens à s’en servir : j’envisage mollement d’écrire un message consistant à la mailing list OCaml, et ce billet fait lui aussi partie d’une timide campagne de propagande.

Pour ma part, je suis content du travail que j’ai effectué pendant mon stage, et de l’expérience que j’en ai retirée, et je ne m’inquiète pas trop pour le sort des utilisateurs de SQL et OCaml. En plus du projet PG’OCaml déjà cité, d’autres personnes essaient de combiner les deux, dont par exemple quelques divagations sur eigenclass.org, ou le projet Ocaml-orm-sqlite, qui a commencé exactement en même temps que mon stage, mais prend une approche très différente.

En bref, ce n’est pas moi, de mon point de vue, qui décidera du futur (ou pas) de Macaque. Si des gens s’en servent, tant mieux, et je le ferai évoluer vers quelque chose de plus complet, sinon ce n’est pas grave, de toute façon j’ai déjà beaucoup trop de choses à faire.

[ tag:blog.huoc.org,2009:bluestorm/10 ]
Voir les commentaires · Commenter

/\ \/

Cohérence des effets de bord

#. Par bluestorm dans Réactions. Publié le 22.01 2010 à 22:59. 4 commentaires.
<. Détection automatique de bugs
effets_de_bord langages réaction

Ce week-end, je me suis promis que je rédigerai, pour le prochain article que je lirai en entier, un court résumé de deux ou trois paragraphes, pas plus, pour cesser d’écrire des réactions à la longueur démesurée. Voici le résultat.

Coherent Reaction (pdf, 8 pages) est un papier de Jonathan Edwards (pas le prédicateur américain), un type intéressant dont le blog s’appelle Alarming Development (Dispatches from the Programmer Liberation Front). C’est aussi le créateur du langage de programmation visuelle Subtext.

Problématique

En travaillant sur Subtext, il s’est rendu compte que l’ordonnancement des effets de bords pose problème dans un programme interactif (avec une interface utilisateur, comme par exemple des formulaires Web). Son exemple, dans un cadre MVC classique, est le suivant : imaginez qu’on travaille sur trois valeurs, qui pour avoir du sens doivent vérifier une contrainte de correction commune (par exemple la troisième, 5, doit être la somme des deux premières, 2 et 3). C’est le contrôleur qui vérifie ces contraintes : il dispose pour chaque valeur d’une fonction de vérification.

Maintenant, si on change deux de ces valeurs en même temps (la première à 4 et la troisième à 7), il y a risque de "fausse erreur" si les vérifications ne sont pas faites dans le bon ordre : si on modifie la première valeur à 4, puis on vérifie, on a une erreur (la contrainte n’est plus vérifiée, car 4 + 3 ne vaut pas 5) alors que si on avait modifié les deux ensemble tout se serait bien passé.

Il y a aussi des situations ou l’ordre de modification est important : fondamentalement, on voudrait que chaque modification s’effectue en parallèle, indépendamment, mais les langages impératifs effectuent les effets les uns après les autres, donc il y a toujours un risque d’interférence.

Les solutions classiques proposent que le modèle donne plus d’informations sur les contraintes liées au données : quelles sont les données liées, par exemple, pour qu’on les modifie toujours toutes en même temps au lieu d’une par une. Leur inconvénient, d’après l’article, est une perte en modularité, puisque cela révèle une partie de l’implémentation du modèle.

Coherent Reaction

Pour Edwards, le problème de fond est la difficulté à coordonner les effets de bords dans les langages impératifs classiques : plus les contraintes sont complexes, plus elles sont difficiles à gérer et donnent lieu facilement à des erreurs de programmation difficile à repérer. Il propose de libérer le programmeur des questions de coordination avec sa notion de cohérence, qui est une forme d’atomicité : le programmeur ne précise pas dans quel ordre faire les modifications, et c’est le programme qui le découvre par essais/erreurs : si des modifications produisent un état inconsistent ou introduisent des interférences, on les annule (rollback) pour les recommencer dans un ordre différent.

Il a aussi conçu, comme expérience pratique, un langage de programmation basé sur cette idée. C’est un langage dynamique centré sur les données (plutôt que les comportements, comme la POO), mettant en avant deux concepts :

dérivation

une façon de déduire des champs du reste de l’objet : on peut dire par exemple qu’un champ c dérive de la somme de deux champs a et b. Si l’un de ces champs est mis à jour, le champ c sera modifié en conséquence, pour maintenir cet invariant.

réaction

propagation de modifications dans le sens inverse de la dérivation : une réaction est une sorte de callback qui est exécuté quand le champ auquel elle se rattache est modifié

Ces deux mécanismes de transmission des modifications permettent de maintenir simplement l’état des données; pour les combiner, c’est le programme qui se charge de trouver un ordre des dérivations/réactions qui évite les interférences.

Remarques personnelles

J’ai apprécié cet article, mais peut-être un peu déçu : pour la réinvention des effets de bord, je m’attendais à quelque chose de plus profond. La discussion des travaux similaires, étendue dans la version longue du papier, est très intéressante.

Je ne suis pas tout à fait convaincu que cette idée peut être reprise dans le cadre d’un langage généraliste, mais elle pourrait être appropriée comme paradigme spécialisé des parties interactives d’un programme. La comparaison avec la programmation fonctionnelle réactive serait certainement intéressante, mais c’est un sujet que je ne connais pas (bien que j’envisage de m’y intéresser prochainement) donc je ne pourrais pas en dire plus.

Enfin, l’article se termine quasiment par un paragraphe, en réaction à une citation très classique d’Alan Kay, qui me semble tout à fait obscur. Je ne pense pas le comprendre, c’est bien trop flou/littéraire/psychologique pour moi, mais je pense qu’il pourrait intéresser certains lecteurs :

Smalltalk’s design—and existence—is due to the in- sight that everything we can describe can be repre- sented by the recursive composition of a single kind of behavioral building block that hides its combina- tion of state and process inside itself and can be dealt with only through the exchange of messages.
– Alan Kay : The early history of smalltalk

The conceptual model of Coherence is in a sense opposite to that of Object Oriented languages. As Alan Kay’s quote above indicates, the central metaphor of OO is that of mes- saging: written communication. The central metaphor of Co- herence is that of observing a structure and directly manip- ulating it. These two metaphors map directly onto the two primary mechanisms of the mind: language and vision.

[ tag:blog.huoc.org,2009:bluestorm/9 ]
Voir les commentaires · Commenter

/\

Pourquoi attacher tant d'importance au typage ?

#. Par bluestorm. Publié le 30.12 2009 à 13:34. 15 commentaires.
débutants langages méditation typage

La réponse, enfin dévoilée ! Bluestorm Origins: The Typing Mystery, maintenant dans les blogs (well, le nôtre du moins :-°). —rz0

Il est arrivé une bonne chose à mon dernier billet : des gens m’y ont répondu en commentaire. Je les remercie tous chaudement. Ce n’est pas complètement rationnel, mais je trouve ça vraiment appréciable, et ça m’a motivé pour retenter l’expérience de ces billets « en bref ». Mais pas pour cette fois : c’est intéressant quand on a différentes choses à dire, et il vaut mieux que ce ne soit pas trop souvent, sinon ils deviennent de plus en plus vides et répétitifs.

Dans les commentaires, que j’ai lus avec intérêt (j’ai même répondu à certains, c’est fou les blogs), se trouve une question qui m’a donné envie d’en faire un billet à part entière. C’est le commentaire de robocop :

Il y a quelque chose qui ressort quand même pas mal dans nombre de tes articles, c’est l’importance que tu attaches aux types. J’ai la facheuse impression d’être passé à coté de quelque chose de fondamental, pour moi, les types ce n’est qu’une vérification supplémentaire qui est fait à la compilation et ou à l’interpretation, mais à la façon dont tu en parles, le système de type semble le cœur même d’un langage de programmation.

Si comme je le pressens, je loupe vraiment quelque chose, pourrais-tu disserter rapidement là-dessus ?

En une phrase : pourquoi, parmi toutes les caractéristiques d’un langage de programmation, attacher autant d’importance à son système de typage ?

J’ai été surpris de ne pas pouvoir répondre à cette question sur le moment. J’ai pris du temps pour y réfléchir, et je pense avoir maintenant une réponse satisfaisante (mais pas rapide).

J’ai apprécié les commentaires de la carotte qui, avant l’ours, a relu ce texte et m’a permis de l’améliorer.

Rappels

Je ne compte pas expliquer dans ce billet les divers avantages et inconvénients, existants ou fantasmés, des différents systèmes de typage ; ce n’est pas le sujet. Je me contenterai ici d’un bref rappel de deux notions qui reviennent souvent quand on discute les systèmes de typage, et dont je reparlerai par la suite :

typage statique

désigne les systèmes de typage qui vérifient les types dans un programme avant de l’exécuter, typiquement au moment de la compilation. Les erreurs de typage, s’il y en a, sont signalées pendant cette phase et le programme n’est pas exécuté tant qu’il n’est pas correctement type. En général, à l’exécution, on n’a plus à se soucier des types et on peut raisonner comme s’ils n’existaient plus.

typage dynamique

désigne les systèmes de typage qui vérifient les types pendant l’exécution du programme. Avant chaque opération que le programme doit exécuter, il vérifie que les types des données sont correctes ; cela implique que le programme a accès à ces types quelque part : ils sont stockés dans la mémoire du programme pendant son exécution.

Ce sont deux méthodes très différentes qui ont un but commun : empêcher l’ordinateur d’effectuer des opérations mal typées (« mélanger des patates et des carottes »). Chaque façon de faire a ses adeptes, et les deux camps se livrent des guerres terribles, depuis plusieurs générations de langages de programmation — et, bientôt, d’informaticiens. Le point important ici est que les langages mettant en place l’une ou l’autre de ces méthodes ont un système de typage. Il sera plus ou moins bien défini, selon la précision de la définition du langage lui-même, mais dans tous les cas on peut s’y intéresser.

J’ai déjà parlé du typage dans un article publié sur le SdZ : Le typage, présentation thématique et historique. Je n’en suis pas complètement satisfait : je le trouve trop jeune et un peu naïf, et j’essaierai peut-être de le reprendre quand je saurai mieux ce que je veux dire.
Pour les débats autour du choix d’un système, j’aime bien l’article What To Know Before Debating Type Systems. Il est peut-être un peu biaisé en faveur des systèmes statiques, et un peu trop technique, mais il apporte une vue d’ensemble assez intéressante. C’est une bonne base si vous voulez vous lancer dans le débat : statique ou dynamique ?

Cependant, ce que je voudrais discuter ici, ce n’est pas le choix de telle ou telle méthode de typage, mais plutôt la place du typage au sein d’un langage de programmation, relativement aux autres aspects du langage.

Types comme information

Imaginez que vous voulez écrire un petit morceau de programme dans le langage de votre choix, qui soit réutilisable par d’autres programmeurs. Vous devez leur décrire ce que vous avez fait. Vous commencez par leur dire ce que fait ce bout de programme (« c’est une compression selon le codage de Huffman »), et vous allez décrire la manière dont ils doivent interagir avec ce bout de programme pour profiter de ses bienfaits : « on lui donne un texte, et il le renvoie compressé, avec le dictionnaire de compression ». Avec cette simple phrase, vous avez déjà transmis des informations de typage : on lui fournit un texte, donc en général une chaîne de caractère. S’il a besoin de plus d’informations (par exemple sur la nature du dictionnaire de compression), vous donnerez une description plus précise qui contiendra autant d’informations de typage supplémentaires.

Cette façon de procéder est commune à l’ensemble des programmes et langages. Choisissez un langage que vous appréciez et qui a une documentation utilisable : elle contient (plus ou moins explicitement) des informations de typage. Par exemple, j’ai choisi une page au hasard de la documentation du langage PLT Scheme, réputé « non typé », dans laquelle on peut trouver le texte (code ?) suivant :

(directory-exists? path) → boolean?
  path : path-string?

C’est tout à fait du typage : on décrit une fonction qui vérifie qu’un répertoire existe dans un système de fichier. Cette fonction accepte un chemin de type path-string, et renvoie un boolean. Les points d’interrogation soulignent un détail intéressant : les noms choisis pour décrire les types sont aussi des fonctions (prédicats) que l’on peut utiliser pour vérifier qu’une valeur est bien du type donné. Par exemple la documentation de la fonction path-string? contient :

(path-string? v) → boolean?
  v : any/c

Ces prédicats sont utilisés au sein de ces fonctions pour vérifier que l’utilisateur ne donne pas n’importe quoi, et la vérification de validité est donc effectuée à l’exécution : c’est une forme de typage dynamique.

Ce ne sont pas les seules explications concernant ces fonctions, il y a aussi du texte en langue naturelle qui explique leur usage. Certaines documentations ne mettent aucune information de typage aussi claire (je pense à Python par exemple), il faut aller la pêcher dans le texte explicatif, ou encore elle peut être implicitement contenue dans les noms des paramètres, qui respectent des conventions facilitant cette tâche.

L’idée importante c’est qu’au fond, un type c’est essentiellement une information sur une expression du langage de programmation.

Le langage qui parle de lui-même

Cette idée des types comme informations est très large, car elle doit faire cohabiter les notions de typage très différentes selon les langages. Elle est peut-être même un peu trop étendue, puisqu’elle désigne tout et n’importe quoi, et que je ne vais pas avoir de mal à vous convaincre que vous avez besoin d’informations pour coder correctement. On peut affiner un peu plus.

Quand on parle du système de typage d’un langage, on n’a pas en tête les conventions de nommage des paramètres utilisées dans sa documentation. On parle en général d’une manière de décrire ces informations qui fait partie du langage lui-même : le système de typage, c’est la partie du langage qui permet de donner des informations sur les valeurs.α Je désigne cette partie par le terme langage de types : par exemple le langage des types du C est un sous-ensemble du langage C qui contient les types de base (int, float..), des modulateurs (unsigned), enum, struct, …, des pointeurs, tableaux, et même des types fonctionnels.

En d’autres termes, le système de typage, c’est un méta-langage, qui détient le pouvoir d’expression du langage sur lui-même. Plus le système de typage est expressif, plus on peut dire de choses sur un programme au sein de ce programme, plutôt que dans la documentation ou en commentaire.

Vous comprenez sans doute pourquoi, avec ce point de vue, j’attache tellement d’importance, quand je m’intéresse à un langage de programmation, à son système de typage : c’est une partie toute fondamentale puisqu’elle détermine la capacité des programmes dans ce langage, en plus de faire quelque chose d’utile, à transmettre des informations sur ce qu’il fait.

Il faut cependant bien remarquer que, bien que très importante, elle n’est pas non plus indispensable : on peut très bien imaginer des langages qui ne permettent de formuler aucune information sur les programmes, mais qui ont d’autres qualités qui en font des langages intéressants. Je pense par exemple à Forth, un des premiers langages à pile haut niveau.

α : On pourrait croire à une circularité ici : si les types parlent du langage tout en en faisant partie, alors les types parlent des types et le monde s’écroule. En réalité, la plupart des langages de types ne sont pas suffisamment expressifs, et ne parlent que d’une partie restreinte du langage (qu’on appelle souvent les valeurs). Mais il existe effectivement des langages (par exemple Coq) dans lesquels les types ont eux aussi un type, qui a à son tourβ un type… Il peut bien sûr exister un nombre infini de types différents, bien qu’un programme donné ne les utilise pas tous en même temps.

β : « Turtles all the way down ! » ;-)

Système de typage et vérification

La définition actuelle reste assez molle : est-ce que les prédicats de PLT Scheme constituent un système de typage ? Ils font partie du langage, mais leur utilisation est laissée au bon vouloir de l’utilisateur : il peut aussi implémenter sa fonction sans faire aucune vérification. On pense en général plutôt à des systèmes dans lesquels le typage est obligatoire, et vérifié automatiquementγ par le compilateur. Ce qui est sûr c’est que les informations implicites de la documentation Python ne rentrent pas dans ce cadre : le typage en Python, c’est ce que le compilateur sait vérifier de lui-même et afficher dans un message d’erreur : l’appartenance à une classe, l’existence d’une méthode donnée, etc.

Quand on décrit le système de typage d’un langage, il faut donc aussi préciser la manière dont ces informations sont vérifiées, ou plus généralement exploitées par le reste du langage. Il y a d’autres distinctions que la vérification statique ou dynamique que j’ai évoquée. Par exemple, certains langages permettent des conversions implicite d’un type vers un autre, ou le choix dans une fonction d’un comportement différent selon le type des paramètres ; on parle dans ce dernier cas de polymorphisme ad-hoc, car il permet de gérer chaque cas particulier séparément.

Autres points à considérer pour son système de typage

L’expressivité et les conditions de vérification ne sont pas les seuls points à débattre d’un système de typage.

On peut s’intéresser à l’emplacement, au sein des programmes de ce langage, des annotations de typage. Certains langages demandent au programmeur très peu d’indications de typage, car ils peuvent les reconstruire à partir des informations déjà présentes dans le langage : c’est l’inférence de type. Plus le langage des types est expressif, plus cette reconstruction est difficile. Les annotations peuvent encombrer le programme si elles sont trop nombreuses, mais placées judicieusement elles peuvent augmenter la lisibilité. Plus généralement, les informations de typage, qu’elles soient produites par le programmeur ou ses outils, renforcent la documentation et la modularité entre les différentes partie du programme : donner le type des valeurs d’une bibliothèque logicielle facilite beaucoup son utilisation par d’autres programmeurs, surtout quand il est assez expressif pour traduire l’ensemble des contraintes d’utilisation de cette interface.

Un dernier exemple, plus sophistiqué, la propriété de séparation : dans certains langages (dont OCaml), le typage est une phase totalement séparée de l’évaluation, dans le sens où une fois qu’on a un programme bien typé, on peut oublier complètement tout ce qui concerne le typage, et cela ne change absolument rien au déroulement du programme.δ Dans d’autres langages, en particulier ceux qui possèdent le polymorphisme ad-hoc, il n’y a pas cette séparation. L’avantage de cette propriété est qu’elle donne un langage plus simple à comprendre, avec des phases plus indépendantes. L’inconvénient est qu’elle est contradictoire avec certaines fonctionnalités que les concepteurs du langage peuvent avoir envie de fournir : imposer la séparation peut réduire l’expressivité. Il n’y a pas de bon et de mauvais choix dans l’absolu, cela dépend des besoins du langage, mais c’est un choix auquel les systèmes de typage sont confrontés. On peut enfin imaginer des distinctions plus fines, avec une partie du langage qui brise la séparation, mais qui est traduit dans une première phase vers un langage plus simple, bien séparé. C’est comme cela par exemple qu’on manipule les type classes de Haskell quand on veut en donner des définitions très précises (formelles).

γ : En fait, PLT Scheme permet aussi la vérification des types ; je suis impressionné par cette implémentation de Scheme.

δ : Je parle ici de la sémantique du langage, c’est à dire d’une définition un peu abstraite du comportement (ou sens) des programmes. Ce n’est pas une question d’implémentation : si on crée un compilateur pour un langage avec cette séparation, on peut très bien choisir de faire des optimisations en raisonnant sur le type des variables, par exemple en changeant leur représentation mémoire (boxing, etc.), du moment que cela crée aucune différence de comportement du programme observable par le programmeur.

Contraintes et limitations

Si le langage des types est peu expressif, et la vérification très rigoureuse (impossible de définir des termes non typés), le typage peut vite devenir une contrainte. Par exemple, le langage des types du C ne permet pas d’exprimer le polymorphisme paramétrique (une fonction qui marche sur tous les types ayant structure donnée) ; on fait avec, en utilisant une sorte de type-à-tout-faire, void *, qui permet d’abandonner à la fois l’information de typage et la restriction qui y est associée.

Cette limitation que l’on rencontre n’est pas seulement un artefact de systèmes pratiques, mais se trouve déjà dans la théorie des langages. Par exemple, le lambda-calcul simplement typé de base ne permet pas d’écrire des programmes qui ne terminent pas : tous les programmes bien typés terminent en un temps fini. Ça a ses avantages, mais aussi un inconvénient : ce langage n’est pas Turing-complet. En gros, il y a des fonctions qu’on ne peut pas écrire avec. On peut lever cette limitation,ε soit en enrichissant le langage des types (polymorphisme plus types récursifs par exemple), soit en ajoutant des constructions au reste du langage (opérateur de point fixe ou définitions de fonctions récursives).

Les langages au typage dynamique ont en général un langage de type assez faible, mais ont du coup l’avantage de ne poser que très peu de contraintes : comme ils ne vérifient pas grand chose au niveau du typage (par exemple, ils ne vérifient pas le fait que les deux branches d’une condition renvoient le même type), ils ne posent pas non plus de contraintes gênantes. C’est un autre choix qui peut se justifier ; par exemple, certains motifs de conception de la programmation objet sont notoirement difficilesζ à typer correctement (ils demandent un langage très expressif), les langages dynamiques n’ont pas ce problème.

ε : Limitation qui n’en est pas toujours une : on peut faire des choses très intéressantes avec des langages non Turing-complets (par exemple les expressions régulières/rationnelles, quand elles le sont vraiment, ou Coq).

ζ : Jacques Guarrige, Code reuse through polymorphic variants

The idea for this example originally comes from a post by Phil Wadler on the Java-Genericity mailing list W+98, in which he proposed a solution to the Expression Problem, that is the problem of extending a variant type with new constructors without recompiling code for old ones. A similar problem and solution can be found in KFF98, but untyped. It appeared later that Wadler’s solution, which already supposed an extension of Generic Java, itself an extension of Java, could not be typed. Didier Rémy, Jérôme Vouillon and myself finally came up with a typable solution in an object-oriented extension of the 3rd order typed lambda-calculus, which was later checked correct by Haruo Hosoya in F-omega- sub-rec. On the other hand, I could provide a much shorter solution in about 15 lines of Objective Label using polymorphic variants, which were merged later in Objective Caml. The solution used only standard ML-polymorphism, which is a weakened 2nd order typed lambda-calculus, and can be inferred automatically.

Pour bien typer, il faut bien comprendre

Il arrive donc assez souvent que des idées sur de nouvelles façon de programmer soient développées dans un cadre non typé, et qu’elles trouvent seulement ensuite leur place dans un système de typage, comme par exemple le transfert du langage de programmation Oz, langage dynamique qui a popularisé certains paradigmes de concurrence, à AliceML, langage typé qui reprend une partie de ces fonctionnalités.

Cela ne veut pas dire que le cadre non typé est plus fructueux : exprimer précisément une information statique sur ces nouvelles idées permet souvent de les éclairer d’un jour nouveau. Par exemple, la recherche de sémantiques statiques pour les langages à effets de bords a donné lieu à l’idée de monade, qui est utilisée en Haskell pour représenter, entre aures, les effets de bords, et qui a pour conséquence de modifier le type des programmes en y rendant ces effets explicites. On y a gagné des outils intéressant, un point de vue nouveau, et une meilleure compréhensionη de la programmation impérative.

Malheureusement, la programmation est un sujet compliqué dont nous n’avons fait qu’égratigner la surface : il y a beaucoup de choses qu’on ne sait pas encore expliquer de manière simple et précise à la fois, par exemple la concurrence, les manipulations mémoire, la communication avec les programmes extérieurs, etc. L’explication et le typage précis de manières avancées de programmer est un sujet de recherche actif, dont on espère qu’il nous apportera des réponses ; en attendant, il faut se contenter de typages correspondant à l’état des connaissances aujourd’hui, et qui ne sont pas forcément aussi expressifs qu’on le voudrait.

η : Rz0 me signale que cette « meilleure compréhension » n’a pas du tout affecté la pratique des programmeurs qui, sur le terrain, utilisent des effets de bords, et qu’il est donc un peu « osé » de présenter les monades comme une réussite de ce point de vue. C’est en effet un peu plus compliqué que ça : notre compréhension théorique des effets de bords s’est améliorée, mais on a aussi découvert que les monades sont des outils trop généraux, qu’elles s’appliquent à beaucoup de choses qui ne sont pas considérées habituellement comme des effets de bords, et qu’elles ne sont pas toujours assez précises.

Pour la gestion de la mémoire par exemple, une monade ne peut pas raisonner, au niveau du typage, sur l’étendue des zones mémoires affectées par un programme : la monade saura dire de deux programmes qu’ils modifient la mémoire, mais pas si les zones de mémoire qu’ils utilisent sont disjointes ou s’il y a des interactions entre les deux. On a besoin de ces propriétés plus fines, et on est en train de développer des outils plus spécialisés et plus complexes, comme les systèmes d’effets et les logiques de séparation.

Enfin, même si ces idées mettent du temps à sortir de leur cadre théorique pour atterrir dans la boîte à outils des programmeurs, elles restent présentes dans l’industrie, avec par exemple les outils d’analyse statique de programmes, qui suivent d’assez près les progrès de la recherche dans ce domaine. L’arrivée du concept dans un langage de programmation grand public est la dernière étape (après l’entrée dans les langages expérimentaux comme DDC ou ATS), qui se fera plus tard, ou peut-être pas du tout, si l’on constate que les bénéfices de ces méthodes ne compensent pas l’ajout en complexité.

η´ : Et puis d’abord, si l’exemple des monades ne vous plaît pas, en voici un autre : les continuations. Le comportement dynamique de call/cc est bien connu, mais l’étude de son lien avec le typage et la logique classique est au moins aussi intéressante.

Puissance du typage et complexité du langage

Cette difficulté que rencontrent les concepteurs de langage de programmation se retrouve aussi, à plus petite échelle, quand un programmeur essaie de donner un type statique à son programme. Quand on code dans un langage dynamique, on ne se soucie essentiellement que d’une chose : il faut qu’au moment de l’exécution, notre programme marche. Donner un type très expressif à un programme, cela peut demander de rendre explicites plus d’informations : qu’est-ce que notre programme fait, comment le fait-il, pourquoi cette fonction est-elle correcte ? L’ordinateur peut en deviner une partie (c’est le principe de l’inférence des types), le programmeur en connaît une partie (par exemple ce qui est un entier, et ce qui est un booléen dans son programme), mais il y a parfois des programmes compliqués pour lesquels ce n’est pas aussi simple : donner un type très précis impose une bonne compréhension du programme.

Bien entendu, même dans les langages typés, un programmeur a toujours la possibilité d’utiliser des représentations moins expressives, et qui demandent donc moins de réflexion. Par exemple, au lieu d’avoir un type de donnée spécialisé pour décrire les dates (jours, mois, heure, etc.), on peut par exemple utiliser des chaînes de caractères. C’est généralement une mauvaise idée : un effort au départ pour décrire et utiliser un type plus précis permet ensuite des manipulations plus sûres et plus directes.

Il y a cependant un point à ne pas oublier quand on cherche des langages de types expressifs : ils rendent nécessairement les langages plus complexes. Quand des débutants en Ada doivent comprendre les différences entre les paramètres in, out, et in out, ils peuvent penser qu’un langage faisant moins de fioritures aurait été plus simple. On peut se demander s’il vaut mieux concevoir des langages simplifiés pour les débutants et des langages moins simples mais ayant d’autres qualités pour les experts, ou si une taille unique peut convenir à tout le monde. Cette question intéressante est encore ouverte, mais il est clair que la simplicité (réelle ou perçue) d’un langage joue dans son adoption par les programmeurs, et qu’on ne peut pas se permettre de concevoir des systèmes de typage arbitrairement compliqués en espérant qu’ils vont dominer le monde.

Tous les langages sont typés, parfois sans le savoir

Ma définition est suffisamment large pour qu’on puisse trouver un système de typage à tous les langages, même ceux qui se proclament traditionnellement « non typés ». Les cas les plus extrêmes sont les langages dont les types sont tellement pauvres qu’ils ne peuvent essentiellement rien dire d’intéressant. Par exemple, en Brainfuck, le système de typage est vide : on ne peut rien dire sur les programmes, et d’ailleurs il n’y a rien à dire puisqu’ils ne manipulent jamais rien d’autre qu’une bande mémoire dont une case est pointée.

Plus utiles, les assembleurs sont aussi des langages avec des typages très pauvres : il y a en général des registres, des valeurs immédiates et des adresses mémoires (et par exemple des labels à plus haut niveau), et des considérations de taille, alignement, etc., qui sont assez riches mais ne nous apprennent pas beaucoup sur le comportement à plus haut niveau du code, tant qu’on ne le munit pas d’un système de typage plus expressif.θ On peut encore citer le langage de Matlab/Octave, qui ne connaît essentiellement que les matrices de scalaires, ce qui peut parfois donner de très mauvaises surprises.

θ : Voir par exemple les travaux de Greg Morrisset ou George Necula sur les assembleurs fortement typés.

La réaction de rz0 de sujet souligne un autre aspect du problème :

À mon avis, un point intéressant, et pas du tout développé dans la plupart des visions « haut niveau » de l’assembleur, c’est les contraintes sur les adresses ou la mémoire : représentation, décalage, facteur, taille, alignement, granularité, etc. Je considère que c’est du typage aussi, et c’est du typage « utile », dans le sens où si les données fournies aux opérandes sont mal typées, soit ça n’assemble pas (p.ex. parce qu’il n’y a pas assez de bits pour représenter l’adresse entièrement), soit ça balance une exception au runtime (si on passe un registre et que sa valeur ne vérifie pas les conditions de typage). Bref, je trouve que ce n’est pas « rien à dire ».

Mélanger typage statique et typage dynamique ?

Cette idée de décrire les langages peu typés comme possédant un petit nombre de « types poubelles » ne donnant que très peu d’information peut en fait être utilisée pour mélanger au sein d’un langage le typage statique et le typage dynamique : on peut rajouter ces types poubelles à l’éventail des types du langage.

Il s’agit de permettre de « perdre » localement l’information de typage, en transformant toutes les valeurs, quelle que soit leur type, vers un même type dynamic. On peut ensuite utiliser une fonction d’extraction pour récupérer des valeurs bien typées dans les parties du programme qui n’ont pas besoin de typage dynamique. Cette idée est vieille comme le monde, il s’agit de faire un cast (conversion d’un type vers un autre) au détriment de la sûreté du typage, et elle est présente dans un grand nombre de langages de programmation.

Pour que cette méthode ne nuise pas trop à la sûreté de l’application dans son ensemble, il faut réutiliser les méthodes de vérification de typage des langages dynamiques : quand on envoie la valeur vers le type poubelle, on lui associe une autre valeur runtimeι qui décrit son type. Ensuite, quand on convertit à nouveau la valeur dynamique vers un type plus expressif du langage, on peut insérer un test pendant l’exécution qui vérifie que le type d’arrivée est bien compatible avec le type dynamique stocké avec la valeur.

Au-delà du dynamic_cast de C++, les langages fortement typés ont fait des tentatives dans cette direction. La solution la plus simple est de demander à l’utilisateur de ces fonctions d’insertion et extraction dans le type dynamique de manipuler lui-même les types dynamiques, en utilisant une bibliothèque logicielle dédiée. On peut utiliser des fonctionnalités du langage pour simplifier cette manipulation (par exemple des macros syntaxiques, des types fantômes,κ ou des type classes), et mettre en place des bibliothèques logicielles apportant ces facilités aux programmeurs.

Ça n’est cependant pas suffisant : si l’on se contente d’une bibliothèque logicielle à part, le langage ne garantit pas la cohérence entre l’information statique de typage dont il dispose, et les valeurs dynamiques construites par l’utilisateur ou sa bibliothèque spécialisée : même quand elle est fortement typée (les manipulations à l’extérieur de la bibliothèque sont sûres) il faut faire confiance à l’auteur de cette bibliothèque de valeurs dynamique pour n’avoir pas fait d’erreurs de conversion à l’intérieur.

ι : Un anglicisme qui désigne le « moment de l’exécution », à nuancer de compile-time ou parse-time, et dont j’ai beaucoup de mal à me débarrasser.

κ : C’est ce que j’ai utilisé dans Macaque ; j’avais besoin d’une méthode sûre pour traiter la perte de typage des données passant par le serveur SQL (qui n’utilise que des chaînes de caractères), et je me retrouvé, sans m’en rendre compte au départ, avec du typage dynamique fait maison.

Si l’on veut plus de simplicité et de sûreté, il faut intégrer cette fonctionnalité directement au sein du langage de programmation. La communauté CAML a fait partie des pionniers de ce domaine avec Dynamics in ML, une extension du langage proposant des valeurs dynamique même pour les types polymorphes. Les langages fonctionnels Clean et AliceML proposent aussi cette fonctionnalité.

On peut remarquer que cette approche (construction automatique, par le langage directement, de la description runtime de type) brise la propriété de séparation, que j’ai évoquée précédemment, entre la phase de typage et la phase d’exécution.

Ces méthodes permet de concevoir des langages typés statiquement mais qui acceptent de « laisser la main » gracieusement quand on atteint les limites de leur expressivité. Cela permet de résoudre les problèmes des constructions trop difficiles à typer mentionnés auparavant, sans abandonner trop de typage. Malheureusement, en pratique la mise en place de tels systèmes au sein d’un langage peut compliquer sensiblement son implémentation efficace, et elles ne sont donc pas très répandues. C’est encore une fois un sujet de recherche actif. Il y a des langages qui essaient de rendre encore plus transparente la frontière entre les mondes dynamiquement et statiquement typés. Cela demande de nouvelles méthodologies de typages, et tout une zoologie se crée autour du sujet : soft typing, gradual typing, blame typing, hybrid typing, …

En attendant, les programmeurs se contentent en général de méthodes plus indirectes, en utilisant des valeurs dans des types moins expressifs mais plus souples, comme le font sans le savoir les utilisateurs de langages dynamiques.

Les types ne font pas tout

Ce billet a beaucoup parlé des types, et de pourquoi le système de typage est un ingrédient majeur d’un langage de programmation. Il ne faut pas non plus oublier que ce n’est pas le seul, et qu’une grande partie de l’intérêt des langages ne vient pas du tout de leur système de types.λ

L’un des livres sur la programmation qui m’a beaucoup plu est le CTM : Concepts, Techniques and Models of Computer Programming. C’est un livre vraiment formidable, qui a étendu mon horizon en matière de techniques de programmation, et qui pourtant ne parle pas du tout, ou quasiment pas, de typage.

λ : Même si vous seriez sans doute surpris par la quantité de chose qu’on peut présenter sous forme d’un typage, ou en relation avec le typage : sécurité, performances, invariants de structures de données, etc.

[ tag:blog.huoc.org,2009:bluestorm/8 ]
Voir les commentaires · Commenter

>> Page : 0 1 2