Ours & Hippy — le blogOurs & Hippyourshippy@huoc.orgtag:blog.huoc.org,2009:atom2010-03-01T01:01:34+01:00tag:blog.huoc.org,2009:posts/42
De l'art de coder un blog (1/2)
2010-02-28T22:22:14+01:002010-03-01T01:01:34+01:00Nhat Minh Lê (rz0)
<p>Après deux semaines de développement (et une semaine de déploiement
qu’il serait dommage d’oublier…), le nouveau blog est là. Pour les
pauvres âmes égarées par erreur en ces lieux, permettez-moi de
rappeler que ce blog, sur lequel vous êtes, est un produit entièrement
artisanal, issu de nos belles campagnes, ahem, je me dissipe.
</p><p>Mais si je vous écris aujourd’hui, ce n’est pas pour vous parler des
nouvelles fonctionnalités trop cools du blog (vous pouvez bien voir
par vous-mêmes, vous êtes assez grands), mais pour vous faire part de
cette petite expérience, la mienne, dans le monde du développement
Web. Au programme donc, aujourd’hui : comment écrire un blog, ou
plutôt, comment j’ai écrit le mien. Original, n’est-t-il-pas ?<sup>α</sup> :]
</p><div class="Navigation"><b>Sommaire</b>
<ul><li><a href="#r42_fond">Le fond</a>
</li><li><a href="#r42_hier">Séries, articles, commentaires, et hiérarchie</a>
</li><li><a href="#r42_tags">Et les tags, dans tout ça ?</a>
</li><li><a href="#r42_atom">Rédaction, mises à jour, et Atom</a>
</li></ul></div><p>Avant de poursuivre, il faut replacer la démarche dans son contexte
(wow, je croirais entendre mes profs). Mon blog est un petit blog,
hébergé en grande partie sur mon PC de bureau reconverti en serveur
malgré lui, tout ça derrière une Freebox. Il est clair, à partir de
là, que les enjeux ne sont pas du tout comparables à un site moyen ou
gros comptant les visiteurs par milliers.
</p><div class="Notes"><p>α : Ça me fait penser à ce petit billet gentillet sur lequel j’étais
tombé par hasard, un jour, alors que je dérivais de recherches en
recherches puis de liens en liens, sur l’Internet : <a class="extern" href="http://invalidlogic.com/2008/12/22/blogging-apps-are-the-new-hello-world/">Blogging apps
are the new Hello World</a>.
</p></div><h3>Le fond <a id="r42_fond"></a>
</h3><p>Dans le fond, un blog, c’est quoi ? C’est un tas d’articles, de
commentaires (si le blog n’est pas trop perdu), et toute une taxinomie
de ce vaste petit monde en tags, catégories, séries, ou tout autre
mode de classification que l’on pourra imaginer.
</p><p>Besoins très classiques et à la fois pas si évidents à modéliser
correctement.
</p><p>Bien sûr, le but, du moins le mien, n’est pas d’implémenter tous les
outils de filtrage et de tri possibles et imaginables. J’ai toujours
pensé, par exemple, que les tags et les catégories étaient
redondants. Je ne doute pas que certains y trouvent leur compte,
à séparer en tags <em>et</em> en catégories ; moi, honnêtement, avec mon
modeste blog, je n’en vois pas l’intérêt. Il n’y a tout simplement pas
suffisamment de contenu pour nécessiter autant de sophistication.<sup>β</sup>
</p><p>J’ai donc retenu les éléments suivants :
</p><ul><li>des articles ;
</li><li>des commentaires ;
</li><li>des séries (collections d’articles, avec possibilité de lier deux
articles d’une même série par des liens <i>précédent / suivant</i>) ;<sup>γ</sup>
</li><li>et des tags (mots-clés décrivant un article).
</li></ul><div class="Notes"><p>β : Du temps des RZ-pages, mon ancien site, le système de catégorie
était autrement plus complexe, conceptuellement, que l’actuel
classification par tags : il était hiérarchique, et les articles
pouvaient en même temps être des catégories <i>(duh!)</i>. L’expérience
a montré que pour le contenu que j’avais à proposer, c’était plus
confus qu’autre chose.
</p><p>γ : Du point de vue de l’implémentation, séries et catégories sont
proches, si l’on exclut la possibilité de joindre des articles
consécutifs d’une série.
</p></div><h3>Séries, articles, commentaires, et hiérarchie <a id="r42_hier"></a>
</h3><p>Durant les dernières vacances (ou était-ce avant ?), j’ai eu une
discussion intéressante sur <a href="http://blog.huoc.org/./sdz.html">#sdz</a>, sur l’organisation
des commentaires. Certains argumentaient que les commentaires
hiérarchiques étaient plus clairs, tandis que je restais sceptique.
</p><p>Mais si je vous parle de cela, c’est qu’un tel choix n’est pas sans
conséquences sur l’implémentation. Si l’on opte pour une vue
généralisée en arbres des commentaires, il vient naturellement que
l’on pourrait faire de même pour les articles, et quitte à faire, ne
maintenir qu’un seul arbre gigantesque comprenant toutes les séries,
les articles, et les commentaires. Une telle stratégie suggère une
représentation spécialisée des données, adaptée à la consultation
d’arborescences, telle que la <a class="extern" href="http://sqlpro.developpez.com/cours/arborescence/">représentation
intervallaire</a>.
</p><p>Pour mon blog, j’ai décidé de rester sur un modèle plus simple, avec
une hiérarchie figée, à trois niveaux : séries, articles,
commentaires. Se pose tout de même la question de faire une seule ou
plusieurs tables ; j’ai décidé de n’en faire qu’une. Un article, une
série, ou un commentaire est, dans ma base de données, un
enregistrement dans la table <code>entry</code>. Il y a à cela plusieurs raisons,
qui peuvent finalement toutes se résumer à un mot :
homogénéité. J’aime pouvoir traiter les articles et les commentaires
de manière similaire… car ils sont similaires ! L’interface est
différente (voy. ci-dessous : <a href="#r42_atom">Rédaction, mises à jour, et
Atom</a>), mais les données sont analogues : des auteurs, un
contenu, une date, et quelques autres broutilles.
</p><p>Mais la conception ne s’arrête pas là : on ne va guère loin avec juste
des articles, des commentaires, et des séries… reste à les lier
entre eux ! Et c’est là où les choses se gâtent quelque peu. Résumons
les différents types de liens qui peuvent lier deux objets :
</p><ul><li>un article peut appartenir à au plus une série ;
</li><li>un article peut avoir un prédécesseur et un successeur ;
</li><li>un commentaire appartient à un et un seul article.
</li></ul><p>En SQL, cela donne ceci :
</p><pre><code>CREATE TABLE entry (
-- Un identifiant :
e_eid INTEGER PRIMARY KEY,
-- Des métadonnées :
e_atomid TEXT UNIQUE NOT NULL,
e_title TEXT NOT NULL,
e_ctime INTEGER NOT NULL,
e_mtime INTEGER NOT NULL,
e_lang TEXT,
-- Quelques liens :
e_parent INTEGER,
e_prev INTEGER,
e_next INTEGER,
-- Le contenu :
e_content TEXT,
-- D'autres métadonnées (relatives au site) :
e_url TEXT UNIQUE,
e_type CHAR(1) NOT NULL, -- Series | Article | Topic | Comment.
e_from CHAR(1) NOT NULL, -- Primary | Secondary | Foreign.
e_banned CHAR(1) NOT NULL, -- Auto-banned | Banned | -.
e_level INTEGER NOT NULL,
-- D'autres champs...
);
</code></pre><p>De plus, il est fréquent, quand on récupère les données d’un objet,
d’avoir besoin de certaines informations connexes, telles que le
nombre de commentaires d’un article, ou le titre de la série
apparentée. Avec une <a class="extern" href="http://fr.wikipedia.org/wiki/Base_de_données_relationnelle">base de données relationnelle</a>, on a des
jointures. C’est un joli concept, dirait bluestorm, mais une jointure
pour la série parente, une pour l’article précédent, une autre pour
l’article suivant, et encore une pour les commentaires associés, cela
fait… beaucoup.
</p><p>C’est là qu’intervient le cache. Enfin, <em>un</em> cache ; car il y a toute
une flopée de façons de faire. On peut mettre en cache le résultat de
certaines requêtes, ou carrément la sortie HTML produite par notre
script. On peut stocker ça en mémoire (p.ex. en utilisant
<a class="extern" href="http://memcached.org/">memcached</a>), ou dans la base de données. Sans compter les mille et
une manières de maintenir ce cache à jour.
</p><p>Dans mon cas, il s’agit tout simplement d’une petite table,<sup>δ</sup> dont les
données sont extraites à partir d’une <a class="extern" href="http://fr.wikipedia.org/wiki/Vue_(base_de_données)">vue</a> associée. C’est la méthode
du pauvre pour matérialiser une vue, disons.
</p><div class="Notes"><p>δ : Il y a également un cache au niveau de la sortie HTML (ou XML
pour Atom et les sitemaps).
</p></div><p>Et pour ceux qui aiment le code, voici :
</p><pre><code>-- La vue :
CREATE VIEW entry_cache_1 AS
SELECT
self.e_eid AS ec_eid,
parent.e_url AS ec_parent_url,
parent.e_title AS ec_parent_title,
parent.e_atomid AS ec_parent_atomid,
parent.e_authors AS ec_parent_authors,
parent.e_tags AS ec_parent_tags,
thread.et_utime AS ec_parent_utime,
prev.e_url AS ec_prev_url,
prev.e_title AS ec_prev_title,
prev.e_atomid AS ec_prev_atomid,
next.e_url AS ec_next_url,
next.e_title AS ec_next_title,
next.e_atomid AS ec_next_atomid
FROM
entry self LEFT JOIN
entry parent ON self.e_parent = parent.e_eid LEFT JOIN
entry_thread thread ON self.e_parent = thread.et_eid LEFT JOIN
entry prev ON self.e_prev = prev.e_eid LEFT JOIN
entry next ON self.e_next = next.e_eid;
-- La table :
CREATE TABLE entry_cache (
ec_eid INTEGER PRIMARY KEY,
ec_parent_url TEXT,
ec_parent_title TEXT,
ec_parent_atomid TEXT,
ec_parent_authors TEXT,
ec_parent_tags TEXT,
ec_parent_utime INTEGER,
ec_prev_url TEXT,
ec_prev_title TEXT,
ec_prev_atomid TEXT,
ec_next_url TEXT,
ec_next_title TEXT,
ec_next_atomid TEXT
);
-- Et la requête pour remplir la seconde depuis la première :
REPLACE INTO entry_cache SELECT * FROM entry_cache_1
WHERE ec_eid = ?
</code></pre><p>En vérité, il manque sur cette démonstration certaines données
capitales telles que le nombre de commentaires d’un article. Celles-ci
sont en fait maintenues à part, dans une table <code>entry_thread</code>. Cette
dernière est mise à jour par incréments, à chaque ajout d’un nouveau
commentaire. Elle peut également être rafraîchie à partir d’une
requête, à la manière d’<code>entry_cache</code>.<sup>ε</sup>
</p><p>Des approches plus complexes (principalement du fait de leurs
possibilités de passage à l’échelle) peuvent se justifier pour des
gros sites, mais compte tenu de mon hébergement quelque peu
<i>technologiquement précaire</i>, pour parler comme
<a class="extern" href="http://sdz.couch.it/gens/Poulet">Poulet</a>, la ressource limitante est clairement
la bande passante, et s’il est rigolo de jouer au petit jeu de
l’optimisation un moment, on peut dire que j’aurais déjà perdu assez
de temps comme cela. :)
</p><div class="Notes"><p>ε : C’est utile, par exemple, pour l’importation de commentaires
sous forme d’un flux Atom archivé, comme cela a été le cas lors de
la migration vers la nouvelle version du blog.
</p></div><h3>Et les tags, dans tout ça ? <a id="r42_tags"></a>
</h3><p>La question du stockage et de l’indexation des tags est l’autre gros
problème d’implémentation. Le sujet est plutôt bien présenté,
<i>benchmarks</i> à l’appui, dans <a class="extern" href="http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html">cet article sur la représentation des
tags dans une base de données</a> ; je ne vais pas épiloguer sur
les différentes manières de faire. Pour résumer, on a le choix entre
stocker les tags d’un article de manière <a class="extern" href="http://fr.wikipedia.org/wiki/Forme_normale_(bases_de_données_relationnelles)">dénormalisée</a> avec l’objet
lui-même, ou utiliser des tables d’associations entre des objets
articles et des objets tags, avec toutes les variantes possibles et
imaginables.
</p><p>L’ancienne version du blog utilisait la forme
dénormalisée… essentiellement par flemme. Le problème qui s’est posé
fut celui de faire des statistiques (p.ex. des <a href="http://blog.huoc.org/./tagcloud.html">nuages de
tags</a>) sur les tags. Cette approche est
fondamentalement asymétrique et privilégie l’accès par articles au
détriment de l’accès par tags.
</p><p>On pourrait imaginer maintenir un index orthogonal, qui recense tous
les articles associés à chaque tag. Avec des structures de données
appropriées, l’intuition me dit que l’on devrait arriver à queque
chose de pas mal. Je n’ai pas tellement étudié la question, mais il me
semble logique que les mecs qui se branlent sur les bases de données
non relationnelles, dans lesquelles la redondance est reine, soient
enclins à opter pour ce genre de solutions. Quelqu’un au courant pour
m’expliquer à quel point j’ai tort ?
</p><p>Bref, pour revenir à des choses plus modestes, l’architecture actuelle
du blog utilise également un modèle redondant : les tags sont stockés
à la fois dans la table des articles, sous forme dénormalisée, et dans
une table <code>tag</code>, à part, associée à <code>entry</code> par la table d’association
<code>tagmap</code> :
</p><pre><code>CREATE TABLE entry (
-- Champs précédemment décrits...
-- Listes d'auteurs et de tags dénormalisées :
e_authors TEXT,
e_tags TEXT,
-- D'autres champs...
);
CREATE TABLE tag (
t_tid INTEGER PRIMARY KEY,
t_name VARCHAR(255) UNIQUE NOT NULL
);
CREATE TABLE tagmap (
tm_eid INTEGER NOT NULL,
tm_tid INTEGER NOT NULL,
PRIMARY KEY (tm_eid, tm_tid)
);
</code></pre><h3>Rédaction, mises à jour, et Atom <a id="r42_atom"></a>
</h3><p>Le dernier point que j’aimerais aborder est une des particularités de
mon blog. En effet, on n’écrit pas ses articles directement sur le
site Web ! Mais ce n’est pas un <a class="extern" href="http://nanoblogger.sourceforge.net/">blog entièrement statique</a>
non plus…
</p><p>Mon blog est une espèce hybride qui se nourrit de fichiers
<a class="extern" href="http://fr.wikipedia.org/wiki/Atom">Atom</a>. En fait, quand bluestorm ou moi poste un billet, il
l’ajoute à un fil Atom interne (grâce à un jeu d’outils en ligne de
commandes développés en parallèle), et <i>ping</i> le blog pour lui
demander de rafraîchir sa mémoire.
</p><p>Cela peut paraître un peu compliqué pour pas grand chose, au premier
abord, mais l’intérêt de cette méthode est qu’elle découple la
rédaction et la présentation du contenu… Pas convaincus ? :p
Accessoirement, la généralisation de cette approche permet d’importer
du contenu d’autres blogs, le but étant au final d’obtenir une espèce
de gros annuaire de billets intéressants affiliés à la
micro-communauté #sdz.<sup>ζ</sup>
</p><div class="Notes"><p>ζ : Le concept est différent d’un <a class="extern" href="http://fr.wikipedia.org/wiki/Planet">Planet</a>, qui agrège
du contenu trié par date ; ici, le but serait de hiérarchiser,
classer les choses intéressantes dans un index global.
</p><p>Je rappelle au passage aux intéressés, qui se reconnaîtront, que la
spéc du format Atom augmenté utilisé pour cette opération de
référencement se trouve ici :
<a class="extern" href="http://git.huoc.org/?p=blog.git;a=blob;f=doc/atom.text">atom.text</a>
</p></div><h3>Conclusion <a id="r42_ccl"></a>
</h3><p>Voilà qui conclut la première partie de cet article. Il me reste pas
mal de choses à raconter ; des anecdotes plus légères, plus digestes
sans doute pour certains, plus ennuyeuses pour d’autres. J’ai fini de
parler de la machinerie derrière le blog. La prochaine fois, je
parlerai de la partie directement visible : les questions d’interface,
de présentation, de déploiement, tous ces petits ingrédients qui
contribuent à créer des petites pages mignonnes et, je l’espère,
agréables à lire, pour les visiteurs… sans oublier les robots,
moteurs de recherches, et autres joyeusetés qui accompagnent
immanquablement tout site Web dans sa vie, ou sa non-vie ! :]
</p>
tag:blog.huoc.org,2009:bluestorm/10
Macaque
2010-02-20T13:17:28+01:002010-02-20T13:17:28+01:00Gabriel Scherer (bluestorm)
<div class="Edito"><p>C’est la saison des webbeux sur <i>Ours & Hippy</i>. 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 <a href="http://blog.huoc.org/./macaque/">Macaque</a>, 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
</p></div><p><a id="introduction"></a>
</p><p>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.
</p><p>Macaque a <a href="http://blog.huoc.org/./macaque/">déjà été mentionné</a> 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 <a class="extern" href="http://bluestorm.info/textes/articles/macaque.pdf">le lien vers
l'article (18 pages)</a> et vous inciter à le lire.
</p><h3>Macaque <a id="presentation_macaque"></a>
</h3><p>Qu’est-ce que c’est que Macaque ? Puisque la paresse préside ce
billet, je me permets de citer, <i>verbatim</i>, le résumé et une partie de
l’introduction de l’article.
</p><blockquote><div><div class="Edito"><p>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 <a class="extern" href="http://ocaml.janestcapital.com/?q=node/11">types
> fantômes</a> sont utilisés pour encoder, en utilisant
les types objets d’OCaml, certaines propriétés fines des valeurs
SQL, comme la nullabilité.
</p></div><p>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.
</p><p>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 :
</p><pre><code>let interroge table predicat =
"SELECT * FROM " ^ table ^ " WHERE " ^ predicat
</code></pre><p>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.
</p><p>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 <a class="extern" href="http://pgocaml.berlios.de/">PG'OCaml</a> :
</p><pre><code>let interroge predicat =
PGSQL(dbh) "select * from ma_table where $predicat"
</code></pre><p>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, <code>ma_table</code>. On ne peut pas
écrire de fonction générique <code>interroge</code>, qui fonctionne sur
n’importe quelle table. On a donc perdu en flexibilité.
</p><p>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.
</p><p>Macaque est un logiciel libre, <a class="extern" href="http://macaque.forge.ocamlcore.org/">disponible</a> sur le site OCamlForge.
</p></div></blockquote><p>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.
</p><h3>La petite histoire <a id="publication_article"></a>
</h3><p>J’ai écrit Macaque au cours d’un stage de deux mois dans le
laboratoire <a class="extern" href="http://www.pps.jussieu.fr/">PPS</a>. 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 », <a class="extern" href="http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/index.html">DA-OCAML</a> 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.
</p><p>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 <a class="extern" href="http://ocsigen.org/">Ocsigen</a>, 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.
</p><p>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.
</p><p>J’y étais encadré par <a class="extern" href="http://www.pps.jussieu.fr/~vouillon/">Jérôme Vouillon</a>, 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 :
</p><pre><code>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
</code></pre><p>Ce module a l’air tout à fait inoffensif, mais il ne permet pas de
conserver le polymorphisme de la valeur <code>None</code>, naturellement de type
<code>'a option</code> :
</p><pre><code># let pas_assez_polymorphe = M.of_option None;;
val pas_assez_polymorphe : '_a M.t = <abstr>
</code></pre><p>Une toute petite modification suffit. Voyez-vous laquelle ?
</p><p>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,
<a class="extern" href="http://caml.inria.fr/pub/papers/garrigue-value_restriction-fiwflp04.pdf">Relaxing the value restriction (12 pages)</a>.
</p><p>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.
</p><p>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.
</p><div class="Remarque"><p>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).
</p></div><h3>Quel futur pour Macaque ? <a id="futur_macaque"></a>
</h3><p>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.
</p><p>J’ai encore quelques idées de choses à ajouter à Macaque. J’en ai un
peu parlé sur le canal IRC <code>#ocaml</code>, et flux m’a fait des remarques
qui ont conduit à l’ajout, par exemple, des valeurs par défaut
à l’insertion.
</p><p>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 <i>mailing list</i> OCaml, et ce billet fait lui aussi partie d’une
timide campagne de propagande.
</p><p>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 <a class="extern" href="http://eigenclass.org/hiki/addressing-orm-problem-typed-relational-algebra">divagations sur eigenclass.org</a>, ou le
projet <a class="extern" href="http://github.com/avsm/ocaml-orm-sqlite">Ocaml-orm-sqlite</a>, qui a commencé exactement en même temps
que mon stage, mais prend une approche très différente.
</p><p>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.
</p>