kantien a écrit 1131 commentaires

  • [^] # Re: Représentations intermédiaires du compilateur OCaml

    Posté par  . En réponse au journal Malfunction: réutiliser la représentation intermédiaire du compilateur OCaml. Évalué à 1. Dernière modification le 26 juin 2016 à 22:25.

    Merci ! Ça répond exactement à ma question. J'aurais du regarder directement là

    Ma réponse du dessous tombe à l'eau, maintenant. :-P

    Cependant on peut quand même se poser quelques questions parce que ce n'est pas vraiment rassurant de lire ensuite :

    So, I conjecture that OCaml will not miscompile any Malfunction program, or at least that when it does, it will also miscompile a sufficiently contrived OCaml program.

    Cela montre surtout qu'il reste encore du travail à faire dessus, mais tout comme lui, sa conjecture me semble plus que vraisemblable. Et il ajoute qu'en cas de problème cela révélerai surtout un bug dans le compilateur OCaml : ce qui est fort possible, il n'a pas été certifié.

    Cela étant, il me semble que dans la famille des compilateurs C, seul CompCert est certifié et a résisté aux tests Csmith.

    Pour ce qui est de la famille des compilateurs de la famille ML, j'ai cru comprendre que Jacques Garrigue avait certifié certaines implémentations.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Représentations intermédiaires du compilateur OCaml

    Posté par  . En réponse au journal Malfunction: réutiliser la représentation intermédiaire du compilateur OCaml. Évalué à 3. Dernière modification le 26 juin 2016 à 22:07.

    La question était : pourquoi ocaml ? […] Juste pourquoi le mec qui fait malfunction utilise ocaml. C'est tout.

    En même temps, si tu ne lis pas les raisons données par le principal intéressé :

    Why re-use OCaml's back-end specifically, when there are plenty of other compilers available? The central issues are efficiency and garbage collection.

    Extrait de son abstract de présentation pour le ML Workshop, seconde partie : Why OCaml ?. ;-)

    Il aborde même la solution de Scheme à laquelle il objecte :

    Dynamic languages, such as Scheme, Smalltalk or Javascript, are easy to compile to and have reasonably fast implementations. However, when running statically typed functional programs, time is wasted on runtime type checks.

    Malfunction s'adresse aux personnes qui veulent obtenir rapidement un compilateur performant pour des langages fonctionnels statiquement typé (ce pourquoi ce langage est essentiellement du lambda-calcul non typé). Les systèmes de typage statique semble être le sujet d'étude de l'auteur, et l'exemple qu'il fournit pour Idris (écrit en Haskell) correspond à un langage avec types dépendants. Le développeur du langage peut alors se concentrer uniquement sur son type checker et sa compilation vers Malfunction qui se chargera alors de passer la main au back-end OCaml.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Représentations intermédiaires du compilateur OCaml

    Posté par  . En réponse au journal Malfunction: réutiliser la représentation intermédiaire du compilateur OCaml. Évalué à 1. Dernière modification le 26 juin 2016 à 18:39.

    Oui, donc il faut encore plus une syntaxe spécifiée, c'est à dire, avec une spécification. Parce que bon, dépendre d'un truc qui peut casser à tout moment je trouve que c'est pas super. Ou alors il faut que ocaml puisse garantir une certaine stabilité de cette représentation intermédiaire, ce qui apparement n'est pas à l'ordre du jour.

    La syntaxe (et la sémantique !) de Malfunction est spécifiée.

    Pour le reste de tes interrogations, la lecture de sa présentation pour le ML Workshop t'éclairera peut être.

    Malfunction is an untyped program representation intended as a compilation target for functional languages, consisting of a thin wrapper around OCaml's Lambda intermediate representation.
    Compilers targeting Malfunction convert programs to a simple s-expression-based syntax with clear semantics, which is then compiled to native code using OCaml's back-end, enjoying both the optimisations of OCaml's new flambda pass, and its battle-tested runtime and garbage collector.

    J'ai l'impression que Malfunction, dans l'esprit de son auteur, est dans la lignée d'un LLVM mais pour des langages fonctionnels statiquement typés avec gestion automatique de la mémoire. Scheme comme langage cible, par exemple, est exclus à cause de ses vérifications dynamiques de typage ce qui est une perte de temps pour des langages dont le bon typage est déjà vérifié à la compilation.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: 18 ans de DLFP

    Posté par  . En réponse au journal Quake. Vingt ans déjà. Évalué à 1.

    Je suis allé faire un tour sur le site du jeu, cela me semble prometteur ! :-) Une bonne pastiche d'une série genre X-Files.

    J'avais suivi les jeux Lucas Arts jusqu'à Grim Fandago, on y retrouvait l'humour et les délires propres de cet éditeur mais j'ai été déçu du choix d'abandonner le principe du pur point&click (dans ce type de jeu, on s'en fout de pouvoir se déplacer comme on le souhaite dans un univers 3D à la manière d'un FPS).

    Dans le genre aventure en mode point&click avec une bonne dose d'humour, j'ai également bien aimé la série des Chevaliers de Baphomet1 ou celle des Runaway. Et dans le genre casse-tête point&click en vue à la première personne, mais sans la touche humoristique, il y a l'inégalée — et inégalable ! — série des Myst.


    1. Par exemple, et de mémoire, dans le deuxième volet il y a une énigme où il faut compter en base… 20 :-D 

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: 18 ans de DLFP

    Posté par  . En réponse au journal Quake. Vingt ans déjà. Évalué à 1.

    En fouillant bien dans mon bordel, je devrais pouvoir retrouver les disquettes 3''1/2 de certains d'entre eux. Sinon, je suppose qu'ils ne sont pas passés en abandonware ?

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: 18 ans de DLFP

    Posté par  . En réponse au journal Quake. Vingt ans déjà. Évalué à 3.

    Day of the tentacle

    Lucas Arts Games a sorti une série des jeux splendides sur SCUMM : les monkey island, maniac mansion, dott, sam & max… j'ai passé des heures sur ces jeux, ils étaient à mourir de rire entre leur scénario foldingue et les combinaisons improbables d'objets à effectuer. Ce type de jeu manque aujourd'hui.

    Ils ont sorti une version remasterisée de Day of the tentacle cette année. Elle est sur steam mais seulement pour Windows, et la configuration matérielle minimale a bien évoluée :

    Minimum:

    Système d'exploitation : Windows 7
    Processeur : 1.7 GHz Dual Core
    Mémoire vive : 2 GB de mémoire
    Graphiques : NVIDIA GeForce GTX 260, ATI Radeon 4870 HD, Intel HD 3000, or equivalent card with at least 512 MB VRAM

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: 18 ans de DLFP

    Posté par  . En réponse au journal Quake. Vingt ans déjà. Évalué à 3.

    Mon premier jeu sur PC : castle adventure, et la machine : IBM PC et ses disquettes molles 5''1/4. Putain, j'suis vieux !

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Mais moi aussi je veux jouer !

    Posté par  . En réponse au journal Coup de boost sur le pilote graphique Intel. Évalué à 3.

    $ apt-cache search teapot
    libglu1-mesa - Mesa OpenGL utility library (GLU)
    
    $ apt-cache show libglu1-mesa
    [...]
    Description-en: Mesa OpenGL utility library (GLU)
     GLU offers simple interfaces for building mipmaps; checking for the
     presence of extensions in the OpenGL (or other libraries which follow
     the same conventions for advertising extensions); drawing
     piecewise-linear curves, NURBS, quadrics and other primitives
     (including, but not limited to, teapots); tesselating surfaces; setting
     up projection matrices and unprojecting screen coordinates to world
     coordinates.
     .
     On Linux, this library is also known as libGLU or libGLU.so.1.
     .
     This package provides the SGI implementation of GLU provided by the
     Mesa project (ergo the "-mesa" suffix).
    

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: C'est bien la peine !

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    En réalité, j'ai mal nommé mes modules : dans tous les cas on a un AST, c'est juste la structure de données pour le représenter qui change. L'encodage de Boeham-Berarducci permet de les représenter dans un langage qui ne possède pas de types inductifs.

    Sinon, la différence de performance semble également dépendre de la complexité des fonctions d'interprétations impliquées. Dans les tests précédents les fonctions étaient simples : l'identité pour les litéraux et l'addition pour les nœuds. En prenant la fonction de pretty printing cela change un peu la donne :

      Name                    Time/Run    mWd/Run   mjWd/Run   Prom/Run   Percentage  
     ----------------------- ---------- ---------- ---------- ---------- ------------ 
      Trav/Ast-Perf:22           9.66s   581.74Mw   308.01Mw   543.01kw      100.00%  
      Trav/Boehmdic-Perf:22      8.57s   581.74Mw   308.01Mw   544.31kw       88.69%  
    

    Là l'arbre a environ 8 millions de nœuds. Pour des arbres parfaits de 2000 à 500_000 nœuds, cela donne :

      Name                    Time/Run       mWd/Run      mjWd/Run     Prom/Run   Percentage  
     ----------------------- ---------- ------------- ------------- ------------ ------------ 
      Trav/Ast-Perf:10          1.07ms      141.98kw       16.83kw      138.88w        0.25%  
      Trav/Ast-Perf:12          4.91ms      568.06kw      106.25kw      556.69w        1.15%  
      Trav/Ast-Perf:14         22.33ms    2_272.39kw      580.68kw    2_255.60w        5.22%  
      Trav/Ast-Perf:16         99.55ms    9_089.71kw    2_945.12kw    8_802.56w       23.26%  
      Trav/Ast-Perf:18        428.06ms   36_359.03kw   14_270.78kw   35_135.02w      100.00%  
      Trav/Boehmdic-Perf:10     1.05ms      141.98kw       16.83kw      141.82w        0.24%  
      Trav/Boehmdic-Perf:12     4.79ms      568.06kw      106.25kw      561.63w        1.12%  
      Trav/Boehmdic-Perf:14    21.06ms    2_272.40kw      580.67kw    2_246.01w        4.92%  
      Trav/Boehmdic-Perf:16    91.97ms    9_089.74kw    2_945.06kw    8_749.15w       21.48%  
      Trav/Boehmdic-Perf:18   415.97ms   36_359.08kw   14_270.81kw   35_170.54w       97.18%  
    

    Et encore, ici il s'agit d'une structure simple : une seule opération binaire. Sur des langages plus riches, si les transformations sur l'arbre sont un peu plus complexes qu'une simple addition, il se peut bien que cette représentation soit plus performante comme le disait Perthmâd pour l'usage qui en est fait dans Coq.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: la langue de bois !!!

    Posté par  . En réponse à la dépêche Jerry Do-It-Together assembler un ordinateur dans un bidon de 20 litres. Évalué à 1.

    Le séquoia ? :-D Cette espèce de conifère est issue de Californie, je ne suis pas certain qu'elle supporte une implantation sur le sol africain.

    Je ne vois pas d'autre explication, bien que le rapport entre des « ateliers créatifs qui renforcent le lien social » et une langue de séquoia me semble toujours obscur (peut être une histoire de Pinocchio  ?). :-P

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Javascript

    Posté par  . En réponse au journal Lettre à mon copain Errol. Évalué à 1.

    Dans ce cas, rien ne vaut un navigateur quantiquement intriqué avec le serveur ! \o/

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Mise à jour d l'article sur Clubic

    Posté par  . En réponse au journal UBUNTU vs OVH : ça vous choque ?. Évalué à 2.

    je suis entrain de me demander si je ne ferai pas mieux de changer de crémerie pour aller là où l'on respecte la communauté.

    Mais là pour éviter toute possibilité de trolll à rallonge, il va falloir que tu trouves une distribution qui ne change pas de système d'init sans obtenir un consensus à l'unanimité. :-P

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: C'est bien la peine !

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    L'écart de performance diminue quand on augmente la taille de la structure, et la version avec type inductif devient plus performante sur la traversée également. Voilà un bench uniquement sur la traversée de la structure avec des arbres parfaits dont la profondeur varie de 10 à 20 avec un pas de 2 :

     Name                       Time/Run   mWd/Run   Percentage  
     ----------------------- ------------- --------- ------------ 
      Trav/Ast-Perf:10            20.46us                  0.08%  
      Trav/Ast-Perf:12            82.03us                  0.31%  
      Trav/Ast-Perf:14           326.56us                  1.23%  
      Trav/Ast-Perf:16         1_436.95us                  5.42%  
      Trav/Ast-Perf:18         5_867.21us                 22.12%  
      Trav/Ast-Perf:20        23_295.92us                 87.81%  
      Trav/Boehmdic-Perf:10       18.40us     3.00w        0.07%  
      Trav/Boehmdic-Perf:12       78.27us     3.00w        0.30%  
      Trav/Boehmdic-Perf:14      326.51us     3.00w        1.23%  
      Trav/Boehmdic-Perf:16    1_669.57us     3.00w        6.29%  
      Trav/Boehmdic-Perf:18    6_665.36us     3.00w       25.13%  
      Trav/Boehmdic-Perf:20   26_528.52us     3.00w      100.00% 
    

    Pour les arbres en forme de peigne (qui s'apparente à des listes, ceux que je construisais dans mes premières mesures), que le peigne parte vers la droite ou vers la gauche, la version « classique » est toujours plus performante que l'encodage de Boehm-Berarducci. Comme c'est une traversée en profondeur qui prend le sous-arbre gauche en premier, c'est plus lent sur des peignes qui partent à gauche. Mais dans les deux cas, la version « classique » avec type inductif est la plus performante. Avec des peignes vers la gauche, par exemple :

      Name                      Time/Run   mWd/Run   Percentage  
     ------------------------- ---------- --------- ------------ 
      Trav/Ast-Left:500           5.28us                  6.76%  
      Trav/Ast-Left:1000         11.54us                 14.78%  
      Trav/Ast-Left:1500         17.67us                 22.64%  
      Trav/Ast-Left:2000         23.99us                 30.73%  
      Trav/Ast-Left:2500         30.99us                 39.70%  
      Trav/Ast-Left:3000         36.98us                 47.38%  
      Trav/Ast-Left:3500         43.60us                 55.85%  
      Trav/Ast-Left:4000         51.75us                 66.30%  
      Trav/Ast-Left:4500         58.19us                 74.56%  
      Trav/Ast-Left:5000         64.97us                 83.23%  
      Trav/Boehmdic-Left:500      5.68us     3.00w        7.27%  
      Trav/Boehmdic-Left:1000    12.59us     3.00w       16.13%  
      Trav/Boehmdic-Left:1500    19.81us     3.00w       25.38%  
      Trav/Boehmdic-Left:2000    27.60us     3.00w       35.36%  
      Trav/Boehmdic-Left:2500    34.10us     3.00w       43.69%  
      Trav/Boehmdic-Left:3000    41.66us     3.00w       53.37%  
      Trav/Boehmdic-Left:3500    51.37us     3.00w       65.81%  
      Trav/Boehmdic-Left:4000    59.99us     3.00w       76.86%  
      Trav/Boehmdic-Left:4500    68.38us     3.00w       87.61%  
      Trav/Boehmdic-Left:5000    78.05us     3.00w      100.00% 
    

    et vers la droite pour de petites tailles :

      Name                        Time/Run   mWd/Run   Percentage  
     ------------------------- ------------ --------- ------------ 
      Trav/Ast-Right:10           104.64ns                  5.82%  
      Trav/Ast-Right:30           310.75ns                 17.28%  
      Trav/Ast-Right:50           490.14ns                 27.26%  
      Trav/Ast-Right:70           655.54ns                 36.46%  
      Trav/Ast-Right:90           828.10ns                 46.05%  
      Trav/Ast-Right:110          996.24ns                 55.40%  
      Trav/Ast-Right:130        1_229.26ns                 68.36%  
      Trav/Ast-Right:150        1_340.23ns                 74.53%  
      Trav/Ast-Right:170        1_515.82ns                 84.30%  
      Trav/Ast-Right:190        1_717.04ns                 95.49%  
      Trav/Boehmdic-Right:10      125.31ns     3.00w        6.97%  
      Trav/Boehmdic-Right:30      323.75ns     3.00w       18.00%  
      Trav/Boehmdic-Right:50      508.61ns     3.00w       28.29%  
      Trav/Boehmdic-Right:70      696.69ns     3.00w       38.74%  
      Trav/Boehmdic-Right:90      879.47ns     3.00w       48.91%  
      Trav/Boehmdic-Right:110   1_069.22ns     3.00w       59.46%  
      Trav/Boehmdic-Right:130   1_245.08ns     3.00w       69.24%  
      Trav/Boehmdic-Right:150   1_442.02ns     3.00w       80.19%  
      Trav/Boehmdic-Right:170   1_620.29ns     3.00w       90.11%  
      Trav/Boehmdic-Right:190   1_798.15ns     3.00w      100.00%  
    

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: C'est bien la peine !

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 2.

    J'ai fait quelques tests supplémentaires en utilisant la bibliothèque Core_bench (ce qui au passage m'a permis de me familiariser avec, elle est plutôt bien faite même si je n'ai pas encore fouillé assez pour voir comment générer des graphes avec les mesures).

    Après études du comportement des différentes implémentations, il s'est avéré que l'encodage de Boehm-Berrarduci ne produit pas de fold « recursif terminal » (quoi qu'à lire le code, j'aurais du le voir) : le seuil qui déclenche un stack overflow est juste un peu plus élevé.

    Néanmoins, dans le cas des semi-groupes implémentés, contrairement à ce qu'avait dit Perthmâd, cet encodage peut s'avérer plus performant que la version avec type inductif pour l'AST dans le cas où l'on parcours l'arbre plusieurs fois. En effet, la construction est plus lente mais le parcours plus rapide dans certain cas.

    De plus, pour avoir un encodage plus performant il s'est avéré qu'il valait mieux utiliser la version avec dictionnaire (comme celle du code d'Oleg Kyseliov donné dans le journal) que la version currifiée que j'ai donnée pus haut. La version avec dictionnaire ressemble à cela :

    module Boehmdic = struct
      type 'a algebra = {
        lit : int -> 'a;
        op : 'a -> 'a -> 'a;
        }
    
      type exp = {expi:'a. 'a algebra -> 'a}
    
      let lit n = {expi = fun alg -> alg.lit n}
    
      let op {expi=e} {expi=e'} = {expi= fun alg -> alg.op (e alg) (e' alg)}
    
      let plus {expi=e} =
        e {lit = (fun n -> n);
           op = ( + );}
    end

    Pour des résultats partiels de benchs, j'obtiens ce qui suit. Les résultats sont donnés en microseconde (us), les arbres sont des arbres binaires parfaits dont la profondeur est indiquée sur chaque ligne (de 7 à 10) et les test sont : construction+parcours (One) et parcours uniquement (Trav). Pour le temps de chauffe : faire la soustraction ;-). Les autres colonnes indiquent le nombre de collections mineures, de collections majeures et de promotions par exécution de la fonction (ici, je teste l'interprétation comme addition), ainsi que le pourcentage par rapport au temps le plus long.

     Name                   Time/Run   mWd/Run    mjWd/Run    Prom/Run   Percentage  
     ---------------------- ---------- --------- ----------- ----------- ------------ 
      One/Ast-Perf:7           5.45us    1.28kw       3.16w       3.16w        5.14%  
      One/Ast-Perf:8          11.30us    2.56kw      12.54w      12.54w       10.65%  
      One/Ast-Perf:9          23.91us    5.12kw      50.40w      50.40w       22.54%  
      One/Ast-Perf:10         51.03us   10.24kw     203.75w     203.75w       48.12%  
      One/Astk-Perf:7          9.55us    4.60kw      12.72w      12.72w        9.00%  
      One/Astk-Perf:8         19.72us    9.21kw      46.70w      46.70w       18.59%  
      One/Astk-Perf:9         42.56us   18.42kw     184.04w     184.04w       40.13%  
      One/Astk-Perf:10        96.38us   36.86kw     734.24w     734.24w       90.87%  
      One/Boehm-Perf:7         8.91us    3.83kw      20.76w      20.76w        8.40%  
      One/Boehm-Perf:8        19.13us    7.67kw      85.63w      85.63w       18.04%  
      One/Boehm-Perf:9        42.86us   15.35kw     334.80w     334.80w       40.41%  
      One/Boehm-Perf:10      106.06us   30.71kw   1_354.19w   1_354.19w      100.00%  
      One/Boehmdic-Perf:7      7.33us    3.32kw      14.65w      14.65w        6.91%  
      One/Boehmdic-Perf:8     16.33us    6.65kw      58.92w      58.92w       15.40%  
      One/Boehmdic-Perf:9     35.82us   13.31kw     235.10w     235.10w       33.78%  
      One/Boehmdic-Perf:10    86.27us   26.62kw     939.71w     939.71w       81.34%  
    
    Name                    Time/Run      mWd/Run   mjWd/Run   Prom/Run   Percentage  
     ----------------------- ---------- ------------ ---------- ---------- ------------ 
      Trav/Ast-Perf:7           2.44us                                           4.79%  
      Trav/Ast-Perf:8           4.88us                                           9.58%  
      Trav/Ast-Perf:9           9.78us                                          19.20%  
      Trav/Ast-Perf:10         19.56us                                          38.40%  
      Trav/Astk-Perf:7          6.08us    3_321.17w      0.61w      0.61w       11.94%  
      Trav/Astk-Perf:8         12.30us    6_649.33w      1.37w      1.37w       24.15%  
      Trav/Astk-Perf:9         24.82us   13_305.66w      3.11w      3.11w       48.74%  
      Trav/Astk-Perf:10        50.93us   26_618.33w      6.88w      6.88w      100.00%  
      Trav/Boehm-Perf:7         2.89us                                           5.67%  
      Trav/Boehm-Perf:8         5.99us                                          11.76%  
      Trav/Boehm-Perf:9        12.40us                                          24.34%  
      Trav/Boehm-Perf:10       24.98us                                          49.04%  
      Trav/Boehmdic-Perf:7      2.22us        3.00w                              4.36%  
      Trav/Boehmdic-Perf:8      4.50us        3.00w                              8.84%  
      Trav/Boehmdic-Perf:9      9.07us        3.00w                             17.81%  
      Trav/Boehmdic-Perf:10    18.43us        3.00w                             36.20%  
    

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Mise à jour d l'article sur Clubic

    Posté par  . En réponse au journal UBUNTU vs OVH : ça vous choque ?. Évalué à 8.

    Canonical aurait du passer un contrat etherum avec la communauté. :-P

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Non

    Posté par  . En réponse à la dépêche Pour sauver AbulÉdu, nous lançons une campagne de financement participatif. Évalué à 1.

    Cela étant, il n'est pas nécessaire que l'accès au code soit gratuit pour que le logiciel soit libre, de même que la gratuité de l'accès aux sources ne fait pas nécessairement du logiciel un logiciel libre. Même si je t'accorde que c'est la norme (l'accès aux sources d'un logiciel libre est généralement gratuite), il est tout à fait possible de faire payer l'accès aux sources tout en faisant du logiciel libre (vendre des logiciels libres par RMS). Le modèle économique par la vente de services étant bien sûr le plus répandu.

    Autrement dit : l'accès gratuit aux sources d'un logiciel n'est ni une condition nécessaire, et encore moins une condition suffisante, pour en faire un logiciel libre.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Dans l'art voluptueuse de ne rien comprendre

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    Le principe même de mon langage était de pousser le principe du typage le plus loin possible, car si il est impossible de "prouver" un code dans le cas général, tu peux prouver beaucoup de choses structurellement. Donc, l'idée était de voir jusqu'où on pouvait aller dans le typage statique.

    On peut aller très loin dans le typage statique : Coq !. Mais là, c'est du lourd et c'est violent. :-) En Coq, l'égalité n'est même pas une primitive du langage mais est définie… par le principe des indiscernables de Leibniz.

    type 'a leibniz = L of 'a
    
    let i = L  1 and j = L 1;;
    
    i = j;;
    - : bool = true
    
    i == j;;
    - : bool = false

    Le problème de l'égalité structurelle en OCaml c'est qu'elle peut être indécidable, et partir dans un calcul sans fin :

    let rec l = 1 :: l;;
    
    let rec l = 1 :: l;;
    val l : int list = [1; <cycle>]
    
    (* là on attend la fin des temps :-P *)
    l = l;;

    Le principe du typage et de l'inférence de types dans les langages fonctionnels viennent de la théorie de la démonstration, et l'inférence de types c'est de la preuve automatique. Selon le système de types (sa « puissance » expressive) l'existence d'une preuve d'une assertion peut devenir indécidable (c'est déjà le cas avec les GADT où il faut parfois rajouter des annotations pour que le système d'inférence fonctionne). En Coq, le système est si expressif qu'il faut faire les preuves à la main, le cas général étant indécidable.

    Comme on est quand même trolldi et que cette question du principe d'idendité résonne chez moi, je ne peux m'empêcher de citer mon maître :

    Unité et diversité. Quand un objet se présente à nous plusieurs fois, mais à chaque fois avec les mêmes déterminations intérieures (qualitas et quantitas), il est, si on le considère comme un objet de l'entendement pur, le même, toujours le même, non pas plusieurs, mais une seul chose (numerica identitas); si au contraire il est phénomène, il ne s'agit plus de comparer des concepts, mais quelque identique que tout puise être à ce point de vue, la diversité des lieux qu'occupe ce phénomène dans un même temps est un principe suffisant de la diversité numérique de l'objet même (des sens). Ainsi, dans deux gouttes d'eau, peut-on faire complétement abstraction de toute diversité intérieure (de qualité et de quantité), et il suffit de les intuitionner en même temps dans des lieux différents pour les regarder comme numériquement diverses. Leibniz prenait les phénomènes pour des choses en soi, par conséquent pour des intelligiblia, c'est-à-dire pour des objets de l'entendement pur (bien qu'il leur donnât le nom de phénomènes, à cause du caractère confus de leur représentation), et alors son principe des indiscernables (principium identitatis indescernabilium) était certainement inattaquable; mais comme ce sont des objets de la sensibilité, et comme l'entendement par rapport à eux n'a pas d'usage pur, mais un usage simplement empirique, la pluralité et la diversité numériques sont déjà indiqué par l'espace même comme condition des phénomènes extérieurs. En effet, un partie de l'espace, quoique parfaitement semblable et égale à une autre, est cependant en dehors d'elle, et elle est précisément par là, par rapport à la première partie, une partie diverse, qui s'ajoute à la précédente pour constituer un espace plus grand, et il doit en être de même, par suite, pour tout ce qui est en même temps en différents de l'espace, quelque semblable et quelque égale que cela puisse être par ailleurs.

    Kant, Critique de la raison pure.

    D'où la distinction entre les deux prédicats d'égalité en OCaml : égalité structurelle = et égalité physique ==, où la première peut être longue voire ne pas répondre tandis que la seconde répond toujours en temps constant. Cette même distinction, qui a grandement son importance, pourrait même être utile pour fournir une sémantique plus « propre » à la physique quantique de telle sorte que l'on ne dise plus qu'un objet peut être en deux lieux au même instant (soit disant en vertu du principe de superposition).

    j'y ai pensé après avoir lu logicomix

    J'avais dans l'idée de faire un journal un jour sur cette BD, faudrait peut être que je m'y colle. Si tu l'as lu, il y a un passage dans un restaurant à Paris où Poincaré se moque de Hilbert qui veut fabriquer une machine à faire des théorèmes parce qu'il « aime trop les saucisses » :-P (ce passage vaut bien les gros troll de DLFP ;-). Leibniz en avait eu l'idée et Frege, Russel, Hilbert voulaient mener le programme à bout. Mais Kant, à son époque, avait déjà saisi l'impossibilité de la chose, et Poincaré qui l'avait bien lu était fort sceptique sur la réussite du projet… jusqu'à ce que Gödel mette tout le monde d'accord et donne raison à Kant sur Leibniz ! \o/

    Cela étant toutes ces recherches ne furent pas vaines, et c'est pour cela que l'on a l'ordinateur, les langages de programmations, et les théories des types.

    Pour revenir à ton problème particulier et ton code, je jetterais peut être un œil dessus si j'ai le temps pour comprendre un peu mieux ce que tu cherches à faire.

    c'est plus un "Sont du même type" genre un "~", c'est un blanc dans ma mini syntaxe

    Cet article sur le blog de Jane Street te donnera peut être des idées.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Dans l'art voluptueuse de ne rien comprendre

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    Pour ta première question la première idée qui me vient à l'esprit c'est les variants polymorphes :

    type _ term = Or: [`A] term * [`B] term -> [`A|`B] term

    Pour la deuxième, tu commences à vouloir pousser le système des types bien loin (j'aime bien l'idée ,c'est amusant :-). Il faudrait sans doute jeter un œil du côté de l'encodage du principe des indiscernables de Leibniz : à voir si tu trouves ton bonheur dans cette liste de liens.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Dans l'art voluptueuse de ne rien comprendre

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 2.

    Où sinon, plutôt que de mettre un constructeur en plus dans ton langage (qui ne correspond sans doute à rien dans ta grammaire), il y a peut être moyen de mixer les GADT avec un usage plus « standard » des types fantômes. Comme dans cet exemple :

    module GADT = struct
      type (_,_) term =
        | Lit : int -> (int,_) term
        | Add : (int,_) term * (int,_) term -> (int,_) term
        | Mult : (int,_) term * (int,_) term -> (int,_) term
        | Div : (int,_) term * (int,[`NZ]) term -> (int,_) term
    
      let rec eval:type a b. (a,b) term -> int = function
        | Lit n -> n
        | Add (t,t') -> (eval t) + (eval t')
        | Mult (t,t') -> (eval t) * (eval t')
        | Div (t,t') -> (eval t ) / (eval t')
    
      let lit n = Lit n
      let add t t' = Add (t,t')
      let mult t t' = Mult (t,t')
      let div t t' = Div (t,t')
      let square_add_one t:(int,[`NZ]) term = (add (mult t t) (lit 1))
    end
    
    open GADT;;
    
    let t1 = square_add_one (lit (-3));;
    val t1 : (int, [ `NZ ]) term = Add (Mult (Lit (-3), Lit (-3)), Lit 1)
    
    let t2 = lit 23;;
    val t2 : (int, '_a) term = Lit 23
    
    let t3 = div t2 t1;;
    val t3 : (int, '_a) term = Div (Lit 23, Add (Mult (Lit (-3), Lit (-3)), Lit 1))
    
    eval t1, eval t2, eval t3;;
    - : int * int * int = (10, 23, 2)
    
    (* et si tu veux convertir au bon type dans la REPL, tu peux faire *)
    ((lit 4):>(int, [`NZ]) term);;
    - : (int, [ `NZ ]) term = Lit 4

    Ainsi tu n'obtiendras des termes non nuls qu'à partir de fonctions dont tu es certain que c'est ce qu'elles produisent.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Dans l'art voluptueuse de ne rien comprendre

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    D'ailleurs, est-ce que dans les gadt, on peut mettre no propre type fantome ?

    Je ne vois pas trop ce que tu veux, les GADT sont des types fantômes : on les appelle aussi first-class phantom type.

    Tu veux un truc dans le genre ?

    module GADT = struct
      type _ term =
        | Lit : int -> int term
        | Add : int term * int term -> int term
        | Div : 'a term * [`NZ] term -> 'b term
        | Nz : 'a term -> [`NZ] term
    
      let rec eval:type a. a term -> int = function
        | Lit n -> n
        | Nz t -> eval t
        | Add (t,t') -> (eval t) + (eval t')
        | Div (t,t') -> (eval t ) / (eval t')
    
      let lit n = Lit n
      let add t t' = Add (t,t')
      let div t t' = Div (t,t')
      let nz t = assert (eval t <> 0); Nz t
    end
    
    open GADT;;
    
    let t1 = add (lit 3) (lit 2);;
    val t1 : int GADT.term = Add (Lit 3, Lit 2)
    
    let t2 = lit 3;;
    val t2 : int GADT.term = Lit 3
    
    let t3 = div t2 t1;;
    Error: This expression has type int GADT.term
           but an expression was expected of type [ `NZ ] GADT.term
           Type int is not compatible with type [ `NZ ]
    
    let t3 = div t2 (nz t1);;
    val t3 : '_a GADT.term = Div (Lit 3, Nz (Add (Lit 3, Lit 2)))
    
    eval t1, eval t2, eval t3;;
    - : int * int * int = (5, 3, 0)
    
    nz t3;;
    Exception: Assert_failure ("gadt.ml", 17, 13).

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Dans l'art voluptueuse de ne rien comprendre

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    pour info, je bosse dans le domaine, autour des objet EMF d'eclipse, et des stéréotypes de sysml

    Je sais que c'est le domaine dans lequel tu travailles; mais je voulais surtout insisté sur le côté embedded du DSL : à la fin on code dans le langage hôte (Haskell, ici OCaml, où le langage que tu veux peu importe) et non dans le DSL.

    Jeremy Gibbons (qui n'est pas né de la dernière pluie non plus) dans son exposé (c'est la vidéo) commence par cette classification :

    Programming Language
     |
     |-- General Purpose
     |
     |-- Domain Specific
           |
           |-- standalone
           |
           |-- embedded
                 |
                 |-- deep
                 |
                 |-- shallow
    

    La technique avec construction de l'AST puis interprétation via un fold sur celui-ci c'est du deep, et la technique qu'il présente et qui est le sujet du journal c'est du shallow.

    En regardant à nouveau la vidéo, j'ai vu le passage où il aborde la manière de gérer un type checker : en passant d'une interprétation conctextualisée (avec environnement) à une interprétation non-contextualisée via un flip (dans la vidéo, il ne l'illustre pas sur un type checker mais sur une fonction de pretty printing avec paranthésage minimal) ce qui ramène à la situation d'un fold.

    à propos des type fantômes j'ai retrouvé ça : https://linuxfr.org/users/montaigne/journaux/les-types-fantomes.

    Si tu réutilises ton système de typage de ton langage hôte, tu ne peux pas faire "autre chose", ce qui est très limitant.

    L'idée des types fantômes puis des GADT est de réutiliser le type checker du langage hôte pour s'assure que les termes de l'EDSL sont bien typés. Le principe général de faire de l'embedding est que cela évite d'avoir à coder un lexer et un type checker : on réutilise les outils fournis par la langage hôte. Comme dans l'exemple suivant :

    type _ term =
    | Lit : int -> int term
    | Bool : bool -> bool term
    | Add : int term * int term -> int term
    | Neg : int term -> int term
    | If : bool term * 'a term * 'a term -> 'a term
    
    let rec eval : type a. a term -> a = function
    | Lit n -> n
    | Bool b -> b
    | Add (t,t') -> (eval t) + (eval t')
    | Neg t -> -(eval t)
    | If (b,t,t') -> if (eval b) then (eval t) else (eval t')
    
    let lit n = Lit n
    let true_ = Bool true and false_ = Bool false
    let add t t' = Add (t,t')
    let neg t = Neg t
    let if_ b t t' = If (b,t,t');;
    
    let t1 = if_ true_ (lit 1) (add (lit 2) (lit 3));;
    val t1 : int term = If (Bool true, Lit 1, Add (Lit 2, Lit 3))
    
    let t2 = if_ false_ (lit 1) (add (lit 2) (lit 3));;
    val t2 : int term = If (Bool false, Lit 1, Add (Lit 2, Lit 3))
    
    eval t1, eval t2;;
    - : int * int = (1, 5)
    
    if_ (lit 2) (lit 1) (lit 3);;
    Error: This expression has type int term but an expression was expected of type
           bool term
           Type int is not compatible with type bool

    Enfin, il ne présente pas la méthode shallow comme un remplacement mais comme un approche complémentaire de la méthode deep. Il donne deux exemples intéressants à la fin de la conf', dont un où le shallow language a pour sémantique des termes de l'AST d'un langage plus petit pour, par exemple, compiler vers une architecture very Risc où l'addition add x y est interprétée par Sub x (Sub (Lit 0) y).

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: C'est bien la peine !

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1. Dernière modification le 15 juin 2016 à 23:48.

    Sauf erreur de ma part lors d'une évaluation avec dans ASTk il y a deux fois plus de fermetures créée que dans Bohem (c'est Boehm à propos ;-))
    et dans AST il n'y en a pas, le tout pour autant de calculs utiles.

    Oui j'ai vu après pour le nom, j'espère qu'il ne m'en voudra pas trop. Dans la vidéo, il est même orthographié « Böhm ». C'est parce que j'écoute trop de jazz manouche, alors quand je lis « boehm », je fais inconsciemment la transformation en « bohem » (et puis sur une structure syntaxique donnée, on pose l'interprétation que l'on veut, non mais ! :-P); en plus musicalement c'est d'une qualité nettement supérieur au banana split1 de l'abominable homme des neiges (qui, soit dit en passant, abuse de la candeur d'une jeune adolescente). :-D

    Pour le nombre de fermetures, je n'ai pas l'impression qu'il y en a plus dans le module Astk; c'est juste que l'on construit l'arbre avant de construire les fermetures, alors que dans le module Bohem ce sont les smart constructors qui construisent les fermetures. Tu devrais lire le lien que j'ai donné où gasche présente la transformation en CPS. Les enregistrements du premier module forme un réification sous forme de listes chaînées de fermetures du code en CPS.

    Tout ces codes sont des façons différentes, au fond, d'implémenter un parcours en profondeur (depth-first search) de l'AST en commençant toujours par le sous-arbre gauche.


    1. c'est une technique introduite dans la vidéo pour multiplier les interprétations avant de présenter l'encodage de Böhm-Berarducci, qui consiste à dire que si f:'a -> 'b et g: 'a -> 'c sont des fold alors h: 'a -> 'b * 'c = (f,g) est un fold

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: C'est bien la peine !

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 3.

    Je ne peux qu'appuyer ce commentaire. On trouve sur le blog d'un des membres de l'équipe de F# chez Microsoft toute une série d'articles sur les fold (dont le terme technique est catamorphisme) dont le troisième est consacré à l'application sur l'interprétation d'un AST, ce qui en POO serait fait via un visitor pattern.

    Il y a même eu un journal ici récemment où l'auteur explique comment faire un EDSL d'algèbre linéaire via les visitor pattern et les expressions template C++14.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: Dans l'art voluptueuse de ne rien comprendre

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 1.

    Il faudrait que je regarde à nouveau la vidéo de l'article, mais il me semble bien que la technique présentée concerne les Embedded Domain Specific Languages (EDSL). Là, tu prends l'exemple d'un compilateur C.

    Néanmoins, je n'ai pas souvenir (je l'ai juste survolée) de l'avoir vu aborder le problème du bon typage du DSL en utilisant le système de type du langage hôte. Historiquement, c'est ainsi qu'ont étés introduis les types fantômes qui sont devenus par la suite les first-class phantom types ou GADT. Comment on gère le typage avec la méthode du shallow embedding ?

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

  • [^] # Re: C'est bien la peine !

    Posté par  . En réponse au journal EDSL et F-algèbres. Évalué à 2.

    J'ai joué un peu avec le code, surtout l'encodage de Bohem-Berarducci, pour comprendre un peu ce qu'il faisait et comment cela marchait. Je vais l'illustrer sur un langage simple : un opérateur binaire sur les entiers (un semi-groupe pour les adeptes de l'algèbre ;-).

    On commence de manière classique avec un type exp pour l'AST du langage :

    type exp = Lit of int | Op of exp * exp

    on se donne des smart constructors pour notre langage et une fonction fold générique sur son AST :

    let lit n = Lit n
    
    let op e e' = Op (e,e')
    
    let rec fold f g = function
      | Lit n -> f n
      | Op (e, e') -> g (fold f g e) (fold f g e')

    à partir de là, on peut définir tout un tas d'interprétations différentes de l'AST et on colle le tout dans un module Ast :

    module Ast = struct
      (* l'AST du langage à un opérateur binaire sur les entiers *)
      type exp = 
        | Lit of int
        | Op of exp * exp
    
      (* smart constructors *)
      let lit n = Lit n
    
      let op e e' = Op (e,e')
    
      (* fold générique sur l'AST *)
      let rec fold f g = function
        | Lit n -> f n
        | Op (e, e') -> g (fold f g e) (fold f g e')
    
      (* interprétation en tant qu'addition *)
      let plus = fold (fun i -> i) ( + )
    
      (* interprétation en tant que soustraction *)
      let moins = fold (fun i -> i) ( - )
    
      (* profondeur de l'arbre *)
      let depth = fold (fun i -> 0) (fun d d' -> 1 + max d d')
    
      (* conversions en chaîne de caractères *)
      let show = fold string_of_int (fun s s' -> Printf.sprintf "(op %s %s)" s s')
      let show_p= fold string_of_int (fun s s' -> Printf.sprintf "(%s + %s)" s s')
      let show_m= fold string_of_int (fun s s' -> Printf.sprintf "(%s - %s)" s s')
    end

    Il s'utilise simplement dans une boucle REPL :

    open Ast;;
    let t = op (lit 1) (op (lit 2) (lit 3));;
    val t : exp = Op (Lit 1, Op (Lit 2, Lit 3))
    
    plus t, show_p t;;
    - : int * string = (6, "(1 + (2 + 3))")
    
    moins t, show_m t;;
    - : int * string = (2, "(1 - (2 - 3))")
    
    show t;;
    - : string = "(op 1 (op 2 3))"
    
    depth t;;
    - : int = 2

    Maintenant, on passe à l'encodage de Bohem-Berarducci. L'idée est de faire du type exp une « linéarisation » de l'arbre d'éxecution du fold de l'AST précédent. La fonction fold avait pour type (int -> 'a) -> ('a -> 'a -> 'a) -> exp -> 'a, le nouveau type sera donc :

    type exp = {expi : 'a. (int -> 'a) -> ('a -> 'a -> 'a) -> 'a}

    Le champ expi prend deux fonctions f et g et renvoie un objet de type 'a qui constitue l'interprétation de l'expression pour les fonctions f et g, comme le faisait le fold pour l'AST.

    On retrouve ensuite nos smart constructors qui mime les deux branches du fold :

    let lit n = {expi = (fun f g -> f n)}
    
    let op {expi=e} {expi=e'} = {expi = fun f g -> g (e f g) (e' f g)}

    La seule différence notable est dans le cas de op ou l'expression fold f g e devient e f g, étant donné que e est son « propre » fold et n'a pas besoin d'être rementionné comme argument.

    Pour les différentes interprétations c'est identique, en remplaçant fold par le champ expi du type des expressions; et on obtient le module :

    module Bohem = struct
      (* le type des expressions est une linéarisation de son propre fold *)
      type exp = {expi : 'a. (int -> 'a) -> ('a -> 'a -> 'a) -> 'a}
    
      (* smart constructors *)
      let lit n = {expi = (fun f g -> f n)}
    
      let op {expi=e} {expi=e'} = {expi = fun f g -> g (e f g) (e' f g)}
    
      (* interprétation en tant qu'addition *)
      let plus {expi = e} = e (fun i -> i) ( + )
    
      (* interprétation comme soustraction *)
      let moins {expi = e} = e (fun i -> i) ( - )
    
      (* profondeur de l'arbre *)
      let depth {expi = e} = e (fun i -> 0) (fun d d' -> 1 + max d d')
    
      (* conversions en chaîne de caractères *)
      let show {expi = e} =
        e string_of_int (fun s s' -> Printf.sprintf "(op %s %s)" s s')
      let show_p {expi = e} =
        e string_of_int (fun s s' -> Printf.sprintf "(%s + %s)" s s')
      let show_m {expi = e} = 
        e string_of_int (fun s s' -> Printf.sprintf "(%s - %s)" s s')
    end

    Il s'utilise comme le précédent :

    open Bohem;;
    
    let t = op (lit 1) (op (lit 2) (lit 3));;
    val t : Bohem.exp = {expi = <fun>}
    
    plus t, show_p t;;
    - : int * string = (6, "(1 + (2 + 3))")
    
    moins t, show_m t;;
    - : int * string = (2, "(1 - (2 - 3))")
    
    show t;;
    - : string = "(op 1 (op 2 3))"
    
    depth t;;
    - : int = 2

    L'intérêt que je vois de prime abord et le côté récursif terminal des évaluations dans cette encodage ce qui permet d'éviter des stackoverflow sur des arbres grands ou fortement déséquilibrés. Pour ce qui est des performances, j'ai fait un benchmark du pauvre en le comparant à l'approche par AST et la méthode AST mais avec un fold récursif terminal en appliquant la transformation CPS décrite ici par gasche, ce qui donne ce module :

    module Astk = struct
      (* l'AST du langage à un opérateur binaire sur les entiers *)
      type expr =
        | Lit of int
        | Op of expr * expr
    
      (* smart constructors *)
      let lit n = Lit n
    
      let op e e' = Op (e,e')
    
      (* fold en CPS via CPS conversion trick *)
      let fold f g e =
        let rec loop e k = match e with
         | Lit n -> k (f n)
         | Op (e, e') -> 
             loop e (fun ie -> loop e'(fun ie' -> k (g ie ie')))
        in loop e (fun e -> e)
    
      (* interprétation en tant qu'addition *)
      let plus = fold (fun n -> n) ( + )
    
      (* interprétation en tant que soustraction *)
      let moins = fold (fun n -> n) ( - )
    
      (* profondeur de l'arbre *)
      let depth = fold (fun n -> 0) (fun d d' -> 1 + max d d')
    
      (* conversions en chaîne de caractères *)
      let show = fold string_of_int (fun s s' -> Printf.sprintf "(op %s %s)" s s')
      let show_p= fold string_of_int (fun s s' -> Printf.sprintf "(%s + %s)" s s')
      let show_m= fold string_of_int (fun s s' -> Printf.sprintf "(%s - %s)" s s')
    end

    Pour le pseudo-bench cela donne :

    (* la liste des entiers [1; ...; n] *)
    let range n =
      let rec loop acc n = if n=0 then acc else loop (n::acc) (pred n)
      in loop [] n;;
    
    (* op (lit 0) (op (lit 1) op(... (lit n)) *)
    let ast n = let open Ast in List.fold_left (fun e i -> op e (lit i)) (lit 0) (range n);;
    
    let bohem n = let open Bohem in List.fold_left (fun e i -> op e (lit i)) (lit 0) (range n);;
    
    let astk n = let open Astk in List.fold_left (fun e i -> op e (lit i)) (lit 0) (range n);;
    
    (* la fonction de mesure approximative du temps de calcul *)
    let time f = fun () ->
    let before = Unix.gettimeofday() in
    for i = 1 to 100 do f () done;
    let after = Unix.gettimeofday() in
    after -. before;;
    
    (* trois mesures pour se faire une idée *)
    time (fun () -> Ast.plus (ast 100_000)) ();;
    - : float = 3.21051192283630371
    
    time (fun () -> Bohem.plus (bohem 100_000)) ();;
    - : float = 4.11348295211792
    
    time (fun () -> Astk.plus (astk 100_000)) ();;
    - : float = 5.08448696136474609

    Il reste encore à investiguer sur les cas où cet encodage offre un avantage sur la méthode usuelle.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.