kantien a écrit 1279 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 à 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 Baphomet 1 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.

  • [^] # Re: Unicode et Haskell

    Posté par . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 2. Dernière modification le 14 juin 2016 à 21:18.

    Cela ne doit tout de même pas représenter une grande proportion de cette génération. Si l'on suit le principe de Poincaré selon lequel « on ne devient pas mathématicien, on naît mathématicien », ils devaient être de cela. Pour les autres, j'ai toujours entendu dire que ce fût un calvaire. N'ayant pas vécu moi même cette époque, je ne peux que me baser sur des ouïe dires, mais de ce que l'on m'en a dit je ne pense pas que cela m'aurait dérangé non plus comme approche.

    Néanmoins, lorsque j'étais étudiant je faisais du soutien scolaire pour des collégiens et des lycéens afin de financer mes études, et pour nombre d'entre eux l'abstraction était une peine. Pour prendre un exemple, j'ai eu plus d'un élève de troisième qui comprenait difficilement que l'on puisse mettre x à la place de prix du kilo de bananes dans un système d'équations, et ils préféraient recopier l'expression complète à chaque ligne de calcul. Après, mon expérience m'a apprise que je ne suis pas un grand pédagogue, même si je m'en sors mieux sur des cours particuliers que des cours de groupes.

    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.

    Merci pour l'exemple d'utilisation. Pour le côté tail-recursive cela vient de son côté CPS, ce qui permet sans doute d'éviter le stackoverflow sur de grands arbres (là où mon fold, qui ne l'est pas, risque d' « exploser ») ?

    J'ai néanmoins une question sur la source de l'efficacité de la construction dans le cas où l'on ne parcours l'arbre qu'une seule fois. L'AST semble être représenté par des fermetures sur ses « constructeurs », ce qui en fait une sorte de construction paresseuse, et quand on l'évalue on le construit pour le consommer de concert, c'est cela ? Si je vois à peu près ce que l'on gagne en temps (ça évite de construire l'arbre d'abord, puis de le parcourir ensuite pour l'interpréter), quel est le coût en espace de toutes ces fermetures ?

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