kantien a écrit 1202 commentaires

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

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

    Je n'ai toujours pas saisi la finalité de la méthode décrite dans le journal, il va falloir que je médite la vidéo et le code donné en lien à la fin pour mieux saisir.

    J'ai l'impression qu'il veut faire de la quantification universelle sur du constructeur (forall a.) ce qui s'appelle du higher-kinded polymorphism et en OCaml on a tendance à utiliser le système des modules pour cela.

    D'un autre côte, il semblerait que l'objectif soit aussi d'éviter de construire explicitement l'AST du langage cible pour éviter de l'allocation. Si l'on met de côté cette contrainte, on peut obtenir le résultat qu'il souhaite ainsi sans même passer par les modules :

    type expr = Val of int | Op of expr * expr
    let rec fold f g e =
      match e with
      | Val i -> f i
      | Op (e1,e2) -> g (fold f g e1) (fold f g e2)
    ;;
    type expr = Val of int | Op of expr * expr
    (* oh le joli type de fold ;-) *)
    val fold : (int -> 'a) -> ('a -> 'a -> 'a) -> expr -> 'a = <fun>
    
    let e = Op(Val 1, Op(Val 1, Val 2));;
    let eval1 = fold (fun i -> i) ( + );;
    let eval2 = fold (fun i -> i) ( - );;
    let eval3 = fold string_of_int (fun a b -> Printf.sprintf "(%s+%s)" a b);;
    
    (* 1 + (1+2) *)
    eval1 e;;
    - : int = 4
    
    (* 1 - (1-2) *)
    eval2 e;;
    - : int = 2
    
    eval3 e;;
    - : string = "(1+(1+2))"

    Le terme technique pour la fonction fold est bien celui d'un catamorphisme sur la structure d'AST du langage d'experession, que l'on spécialise selon différentes interprétations pour donner eval1, eval2 et eval3.

    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é à 1.

    À ce niveau, cela se tient et il me semble que c'est toujours ainsi que cela se pratique (lorsque j'étais étudiant puis chargé de TD c'est ainsi que l'on faisait). Lors de mon premier cours de sup', notre professeur nous avait dit une chose du genre : « pour la première fois de votre vie, vous allez faire des mathématiques » (c'est volontairement provocateur, mais il y a du vrai dedans : cela traduit surtout un changement complet de méthode dans le traitement de cette science).

    Cela étant, commencer cette approche dès le primaire et le secondaire ne pouvait qu'être voué à l'échec. Il ne faut pas avoir soi même d'enfant, ou avoir côtoyé des enfants pour imaginer qu'une telle méthode puisse fonctionner.

    Les actes logiques de l'entendement qui produisent les concepts selon la forme sont :

    • la comparaison c'est-à-dire la confrontation des représentations entre elles en relation avec l'unité de la conscience ;
    • la réflexion c'est-à-dire la prise en considération de la manière dont diverses représentations peuvent être saisies dans une conscience ;
    • enfin l'abstraction ou la séparation de tout ce en quoi pour le reste les représentations données se distinguent.

    Remarques. 1) Pour faire des concepts à partir de représentations, il faut donc comparer, réfléchir et abstraire, car ces trois opérations logiques de l'entendement sont les conditions générales et essentielles de production de tout concept en général. — Par exemple, je vois un pin, un saule et un tilleul. En comparant tout d'abord ces objets entre eux, je remarque qu'ils diffèrent les uns des autres au point de vue du tronc, des branches, des feuilles, etc…; mais si ensuite je réfléchis uniquement à ce qu'ils ont de commun entre eux, le tronc, les branches et les feuilles-mêmes et si je fais abstraction de leur taille, de leur taille, etc… j'obtiens un concept d'arbre.

    2) On n'emploie pas toujours correctement en logique le terme : abstraction. Nous ne devons pas dire : abstraire quelque chose (abstrahere aliquid), mais abstraire de quelque chose (abstrahere ab aliquo). Si par exemple dans un drap écarlate je pense uniquement au rouge, je fais abstraction du drap; si je fais en outre abstraction de ce dernier en mettant à penser l'écarlate comme une substance matérielle en général, je fais abstraction d'encore plus de déterminations, et mon concept est devenu par là encore plus abstrait. Car plus on écarte d'un concept de caractères distinctifs des choses, c'est-à-dire plus on en abstrait de déterminations, plus le concept est abstrait. C'est donc abstrayants (conceptus abstrahentes) qu'on devrait nommer les concepts abstraits, c'est-à-dire ceux dans lesquels d'avantage d'abstractions ont eu lieu. Ainsi par exemple, le concept de corps n'est pas à proprement parler un concept abstrait; car du corps lui-même je ne puis faire abstraction puisque dans ce cas je n'en aurais pas le concept. Mais il faut bien que je fasse abstraction de la taille, de la couleur, de la dureté ou de la fluidité, bref de tous les déterminations spéciales des corps particuliers — Le concept le plus abstrait est celui qui n'a rien de commun avec ce qui diffèrent de lui. C'est le concept de quelque chose; car le concept qui s'en distingue est celui de rien et il n'a donc rien de commun avec le quelque chose.

    Kant, Logique.

    Pour que le processus d'abstraction aboutisse, et lorsque l'on s'adresse à quelqu'un pour qu'il comprenne où l'on veut en venir, il faut déjà pouvoir comparer. Et cette comparaison, suivie de la réflexion, nécessite une grande familiarité avec les objets à partir desquels l'on cherche à produire un concept plus général les englobant tous par abstraction. Parler d'arbre à une personne qui n'en a jamais vu un seul, ni plusieurs différents pour les comparer, cela ne peut aboutir qu'à parler dans le vide et à de l'incompréhension.

    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.

    La meilleure… c'est discutable, non ? Pourquoi pas avec des objets ou avec la méthode ci-dessus utilisée ?

    J'ai l'impression que dans mon message du dessus je me suis trompé, et que tu fais référence à la méthode que tu as décrite dans ton message, c'est cela ? Il faut que j'y réfléchisse à tête reposée (pour le choix des objets, je ne les utilise jamais et je me suis toujours demandé ce que cela faisait dans le langage si ce n'est pour le défi théorique d'en comprendre le typage :-P).

    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. Dernière modification le 14 juin 2016 à 01:07.

    Je ne vois pas comment faire autrement. Et ça méthode ne marche pas, il le dit lui même dans le journal et en redonne la raison dans ce commentaire. Sa solution fait une quantification existentielle sur le type, alors qu'il cherche une quantification universelle (higher-kinded polymorphism)-> les modules et les foncteurs; et il le dit lui même dans son journal :

    En effet, une F-algèbres, pour les gens qui n'ont pas le temps de cliquer sur le lien, c'est un foncteur F (comme celui précédent), et une fonction de « calcul » de type F A -> A.

    alors pourquoi ne pas utiliser le système des modules et des foncteurs du langage ?

    J'ai regardé rapidement la vidéo, et je n'ai pas encore bien saisi l'intérêt du shallow embedding par rapport au deep embedding. D'après Alluminium cela évite l'allocation de l'AST mais on peut imaginer une implémentation en CPS qui produit et consomme l'AST de concert pour éviter des allocations. Je trouve l'approche en shallow étrange et verbeuse.

    Pour l'overhead des frist class modules cela s'inline avec flambda, il me semble.

    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. Dernière modification le 13 juin 2016 à 19:42.

    Les GADT ne me semblent pas adaptés, mais, qui sait ?

    Je ne pense pas. La meilleure solution, en OCaml, pour faire de l'abstraction sur les types (higher-kinded polymorphism) reste de passer par le système des modules. C'est la solution retenue par Jeremy Yallop, Léo White et Frédéric Bour pour implémenter du polymorphisme ad-hoc (ou surchage d'opérateur, les type class en Haskell) en OCaml (voir la partie 2.4 pour la discussion sur le choix d'utiliser le système de module).

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

  • [^] # Re: Lambda Akbar!

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

    Il y a des perles magnifiques dans cette histoire brève, incomplète et globalement erronée ! :-)

    1936 - Alonzo Church also invents every language that will ever be but does it better. His lambda calculus is ignored because it is insufficiently C-like. This criticism occurs in spite of the fact that C has not yet been invented.

    1970 - Niklaus Wirth creates Pascal, a procedural language. Critics immediately denounce Pascal because it uses "x := x + y" syntax instead of the more familiar C-like "x = x + y". This criticism happens in spite of the fact that C has not yet been invented.

    1972 - […] Lambdas are relegated to relative obscurity until Java makes them popular by not having them.

    1973 - Robin Milner creates ML, a language based on the M&M type theory. ML begets SML which has a formally specified semantics. When asked for a formal semantics of the formal semantics Milner's head explodes. Other well known languages in the ML family include OCaml, F#, and Visual Basic.

    1983 - Bjarne Stroustrup bolts everything he's ever heard of onto C to create C++. The resulting language is so complex that programs must be sent to the future to be compiled by the Skynet artificial intelligence. Build times suffer. Skynet's motives for performing the service remain unclear but spokespeople from the future say "there is nothing to be concerned about, baby," in an Austrian accented monotones. There is some speculation that Skynet is nothing more than a pretentious buffer overrun.

    Pour ceux qui ne sont pas allés lire le lien, ça vaut le coup pour se détendre et rire un bon coup.

    Néanmoins, Aluminium95 y est allé un peut fort sur ce journal et je peux comprendre la réaction de wolowizard. Je ne sais pas si ce sont mes messages de samedi dans la dépêche sur la sortie du dernier compilateur GHC Haskell qui l'ont inspiré, mais ils étaient humoristiques et dénoncés le jargon employé par les adeptes de la programmation fonctionnelle. J'y reprenais le fameux « une monade est un monoïde dans la catégorie des endofoncteurs » que je comparais à : il ne faut pas dire « la terres est ronde » mais « une équipotentielle du champ de gravitation terrestre est homéomorphe à la sphère S_2 », tout en montrant sur un exemple élémentaire le fait que ma nièce de 8 ans avait « compris » les notions d'anamorphisme, catamorphisme et hylomorphisme en appliquant l'algorithme élémentaire de la multiplication (bien qu'elle ignore tout de ce jargon qu'elle serait bien incapable de comprendre, Scratch est bien plus amusant pour elle ;-).

    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é à 1.

    Pourrais-tu développer l'analogie « anamorphosisme = lens » ?

    C'est dans le contenu du premier lien de mon message (la cuisine gloubiboulga ;-) où le titre original de l'article est : Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire. Les anamoprhismes sont des générateurs de structures (le unfold dont l'opérateur contraire fold est un catamorphisme). Compter sur ces doigts, pour reprendre un de tes exemples, voilà le premier anamorphisme que rencontre un enfant ! ;-) C'est un générateur de nombre entier sous leur forme unaire ou entier de Peano (type peano = Zero | Succ of peano en OCaml) qui ressemble fortement au type des listes (on peut les voir comme une liste dont la valeur des éléments est constante Nil | Cons of unit * peano de type unit list, et le morphisme naturel entre les deux types calcule la longueur de la liste).

    Ces trois types d'opérations sont fécondes. Dans ce journal, par exemple, l'auteur utilise les template C++ 14 pour implémenter un interpréteur d'algèbre linéaire sur des vecteurs, et plonger le langage cible dans le langage d'origine. Il utilise le pattern des visiteurs, là où en programmation fonctionnelle on passerait par un catamoprhisme (l'exemple est en F#). Mais pour obtenir le même fonctionnement que lui, ne pas allouer d'objets intermédiaires, on chercherait à produire l'hylomorphisme correspondant à la composition de l'anamorphisme qui réifie l'arbre d'évaluation et au catamorphisme qui le consomme :

    A recursive function h : 'a -> 'b whose call-tree is isomorphic to a cons-list, i.e., a linear
    recursive function, is called a hylomorphism. […]

    A hylomorphism corresponds to the composition of an anamorphism that builds the call-tree as
    an explicit data structure and a catamorphism that reduces this data object into the required
    value.

    Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire, p. 4-5

    On passerait directement par la pile d'appel sans allouer d'intermédiaire. L'idée est assez proche de ce commentaire de gasche sur stackoverflow, reprise ici pour une version en Haskell.

    En revanche, je ne suis pas certain qu'il soit pédagogique d'expliquer le concept général de bijection avant de savoir compter, tout comme les hylomorphismes peuvent rester des objets obscurs sans que cela ait un impact trop important sur le développement intellectuel :-p.

    Nicolas Bourbaki s'inscrirait en faux contre de tels propos. Il a grandement contribué à la réforme de l'enseignement des mathématiques sous la forme des mathématiques modernes. Cette réforme fut sur le plan pédagogie, et cela de manière surprenante, un véritable échec ! :-P

    Tu auras bien compris que mon propos empruntait la démarche inverse, en s'adressant à des adultes qui ont déjà une certaine maîtrise de l'abstraction pour leur montrer que ces concepts abstraits, au premier abord déroutant, ils les manipulaient depuis l'enfance sans les avoir nommé ainsi. ;-)

    Pour la monadologie cela se discute. Cela étant, Saunders Mac Lane leur a donné ce nom en référence au concept de Leibniz.

    Quand on raisonne à homéomorphisme près c'est pas super cool non ? Tu peux au moins demander que ce soit un Ck-difféormorphisme (avec k grand, par exemple 200000) pour éviter la possibilité d'avoir des trucs trop irréguliers …

    Là-dessus, je partage le point de vue de Poincaré (qu'il a exposé dans La valeur de la Science, ou La Science et l'hypothèse, je ne sais plus lequel des deux) selon lequel ce n'est pas spécialement pertinent, en physique théorique, de distinguer continue et différentiable. Mais peu importe, l'essentiel est de faire référence à l'identité topologique avec la sphère S_2. Je vais fonder le comité des adeptes de la capillotétratomie et de la diptérosodomie, et rejoindre le groupe des xyloglottes. :-D

    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é à 3.

    (lens mon amour…)
    […]Ces opérateurs font initialement peur, […]

    D'un côté les adeptes de la programmation fonctionnelle pratiquent une drôle de cuisine : des bananes aux lentilles enveloppées dans des fils barbelés. Ça fait un peu gloubiboulga à la mode Casimir, ce n'est pas très appétissant ! :-P

    Alors qu'au fond, même ma nièce de huit ans a compris le principe des anamorphismes (lens), catamorphismes (banana) et hylomorphismes (la composition des deux premiers). J'en veux pour preuve son cahier d'exercices sur les multiplications dont voici un extrait :

      142
    *   4
    -----
        8 <-   2 * 4
    + 160 <-  40 * 4
    + 400 <- 100 * 4
    -----
    = 568
    

    Pour appliquer l'algorithme de multiplication que lui a enseigné son institutrice, elle commence par prendre l'entier 142 qu'elle transforme en une liste d'entiers : [2; 4; 1] (lentilles et anamorphismes, vous voilà !). Ensuite, elle applique la multiplication sur chacun des éléments de la liste (soit un map qui est un cas de catamorphismes ou bananas) à l'aide d'un tableau associatif, autrement connu sous le nom de tables de multiplication, pour obtenir la liste [8; 160; 400] . Puis, pour terminer, elle ajoute le tout (ce qui est toujours un banana) pour obtenir son résultat: 568. \o/

    Bilan des courses : un anamorphisme puis deux catamorphismes pour obtenir un hylomorphisme, plus connu sous le nom de multiplication. :-P

    Pour la notion de monade et de binding entre monades (>>=) c'est aussi simple : une monade est un monoïde dans la catégorie des endofoncteurs ! Cette formulation paraît absconse ? Pour les lecteurs plus familiers avec le paradigme de la programmation orientée objet, la lecture de l'article Dieu a-t-il programmé le monde en Java ? sur le blog de la Société Informatique de France leur permettra sans doute d'y voir plus clair. D'autant que je trouve que le concept de monade en programmation fonctionnelle, qui a été nommé ainsi en théorie des catégories en référence à la monadologie leibnizienne, capture mieux la signification qu'avait ce terme chez Leibniz que le concept d'encapsulation en POO. Et puis, on peut aussi faire des monades en C++.

    C'était mon message a teneur humoristique du samedi midi. Je milite également pour que l'on soit plus rigoureux dans l'expression de certaine proposition, et que l'on ne dise plus : « la terre est ronde », mais plutôt : « d'un certain point vue, une équipotentielle du champ de gravitation terrestre est homéomorphe à la sphère S_2 ». :-D

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

  • [^] # Re: CozyBox

    Posté par  . En réponse à la dépêche Cozy Cloud lève 4 millions d'euros (pour faire du libre). Évalué à 5. Dernière modification le 11 juin 2016 à 10:13.

    Pour le client lambda, un cozy hebergé sur les serveurs du FAI me semble plus adapté, de même qu'à l'heure actuelle nombre d'abonnés ont un mail chez leur FAI, et certains des pages persos. Quel gain, pour l'utilisateur, à mettre cela sur sa box ? On perd en bande passante et en disponibilité, sans parler de la récupération des données en cas de remplacement de box. Une personne qui a un mail en @orange.fr ou @free.fr a-t-elle son serveur mails sur sa box ?

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

  • [^] # Re: LinuxFR est pro-Systemd

    Posté par  . En réponse à la dépêche Debian Jessie, 1 an plus tard. Évalué à 2.

    et non, je n'irai pas exhumer des preuves, ça fait longtemps que je n'en ai plus rien à foutre

    Il n'y a pas besoin de faire de l'exhumation : les argumentaires se sont zombifiés ! :-P

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 2.

    Il y a un outillage autour d'Ocaml pour mesurer ce genre de choses ?

    Apparemment c'est encore léger et ce n'en est qu'au début, il y a des efforts à faire de ce côté (c'est apparu avec le nouveau compilateur 4.03 et les efforts de Damien Doligez pour réduire le worst-case latency).

    Il y a un message (de gasche ;-) sur la mailing list : Measuring GC latencies for OCaml program dans lequel il renvoie à son article de blog plus complet Measuring GC latencies in Haskell, OCaml, Racket en réaction au billet pointé par GuieA_7 sur les problèmes de latence du GC de Haskell.

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 1.

    Dans ce cas, ce n'est peut être pas totalement hors de portée de OCaml non plus. Dans cette direction, outre sa date de sortie ainsi que le premier exemple qui peut engendrer un stack overflow si on ne fait pas attention et n'est pas très optimisé, il y a cet article qui donne des pistes de réflexions.

    As you may know, there is a subset of Javascript that compiles efficiently to assembly used as backend of various compilers including a C compiler like emscripten. We’d like to present you in the same spirit how never to allocate in OCaml. […]

    Now that we can manage almost any control flow without allocating, we need also to manipulate some values. That’s the point where we simply suggest to use the same approach as ASM.js: allocate a single large bigarray (this is some kind of malloc), consider integers as pointers and you can do anything. We won’t go into too much details here as this would require another post for that topic.

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 2.

    Ce qui est marrant c'est ta solution (en tout cas pour moi).

    N'étant pas développeur, ce n'est peut être pas très conventionnel ce que j'ai fait : mais ça marche ! :-P
    Disons que je me suis adapté au contrainte du test, normalement on évite de faire autant d'allocation dans du code réel. Selon le cas pratique qui amène ce genre de calcul, je dirais même que l'on peut totalement se passer d'allocation. C'est du code qui pourrait ressembler à un interpréteur d'un langage algébrique très très mal optimisé. En pratique, on ferai plutôt comme dans ce journal avec une solution en C++ (comparer le premier commentaire du journal avec le code de ma fonction worker ;-).

    En OCaml que ce soit avec du bytecode ou du code natif, on peut aussi gérer le fonctionnement du GC par des variables d'environnement : voir la doc à la variable OCAMLRUNPARAM (c'est ce que j'ai fait au départ avant de le mettre en dur dans le code).

    D'ailleurs comment se passe une redéfinition plus petite de la minor heap en Ocaml ? Il fait un stop the world et envoie tout ce qu'il faut dans la major heap ?

    Une redéfinition de la taille de la minor heap entraîne une collection mineur : il nettoie, envoie ce qui est encore en vie dans la major heap, et redimensionne. Documentation du module GC.

    En fait j'ai opté pour cette solution parce que l'algorithme du test consiste à allouer un arbre binaire parfait de profondeur d (la fonction make) puis à faire un checksum dessus (la fonction check) et répéter cela sur de nombreux arbres (le paramètre iter). Alors j'ai adapté la taille de la minor heap (le memory pool le plus efficace) pour qu'elle puisse contenir deux arbres les plus profonds qu'il y aura à allouer. :-)

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 2. Dernière modification le 05 juin 2016 à 21:43.

    Alors quelques précisions: […]

    C'est globalement ce que j'en avais compris des deux articles du membre de l'équipe chargée du compilateur. L'idée étant de pouvoir rajouter un smart pointer Gc<T> dont l'intention serait similaire à Rc<T>. Si la gestion automatique est basée sur du comptage de références, je comprends pourquoi la politique de gestion est drastique via le borrow checker (comme éviter des cycles dans le graphe des pointeurs qui sont gérés par des éphémérons dans le cas du GC d'OCaml).

    La différence peut être mince, comme tu le soulignes avec les unikernels, mais je dirais qu'un langage système permet de maîtriser suffisamment parfaitement le comportement du code pour pouvoir écrire du code temps-réel (au sens temps réel dur, pas au sens globalement performant).

    Mais dans ce cas ne faut-il pas une gestion totalement manuelle de la mémoire pour faire ce genre de chose ? Un langage tel que Rust peut-il s'attaquer à ce genre de problématique avec son mode de gestion automatique ?

    Mais justement le lien que j'ai donné explique bien que Haskell va donner de très bon débit, mais va avoir des problèmes de latences ; ton benchmarck au final est équivalent à tester un débit moyen, mais ne va pas du tout tester les problèmes de latences, parce que 1) c'est pénible à tester 2) ce n'est pas ton problème.

    Effectivement mon benchmark ne montre que le débit moyen, j'utilise time, mais parce que n'étant pas programmeur je ne connais pas les outils pour faire de la mesure de latence. En réalité, la modification que j'ai apporté au code OCaml de départ est plus un sketch of proof pour régler des problèmes de latence liés au GC. Ce qui fait que je suis passé de 25 s à 5.5 s est que j'ai fait disparaître une action du GC qui est responsable de la latence observée dans l'article que tu as cité sur le GC de Haskell : un parcours de la major heap.

    Le tas est composé de deux memory pool : une minor heap et une major heap. La première sert pour les objets à courte durée de vie et est très efficace pour ce qui concerne les temps d'allocation et de désallocation mémoire. Lorsqu'elle est pleine le GC nettoie et transfert les structures encore en vie vers la major heap, puis à chaque fois qu'il fait cela, il nettoie par tranche cette major heap (de manière incrémentale) : c'est ce processus en mode stop the world qui prend plus du temps et génère de la latence. Dans mon code, j'ai adapté la gestion de la mémoire pour ne pas y avoir recours. Cela se passe en trois temps :

    (* le memory pool de la minor heap est définie avec une taille
     * suffisante pour les besoins du calcul : un gros malloc une
     * bonne fois pour toute *)
    let () = set_minor_heap (8 lsl max_depth)
    
    (* c'est la partie proprement calculatoire du code *)
    let worker (niter, d) =
      let c = ref 0 in
      for i = 1 to niter do
        (* je nettoie la minor heap pour éviter des transferts
         * vers la major heap *)
        Gc.minor();
        c := !c + check(make  i d) + check(make (-i) d)
      done;
      !c
    
    (* le bout de code qui enchaîne les calculs
     * ne subit plus de latence *)
    compute ~worker ~master tasks;

    Sans cela j'avais un grand nombre de major collection, qui sont responsables des latences observées dans le fonctionnement du GC, là où maintenant je n'en ai plus aucune. ;-)

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 1. Dernière modification le 05 juin 2016 à 10:38.

    Je vois un peu mieux le principe du borrow checker, je regarderai peu être plus en détail la façon dont Rust gère les références. Cela étant, au début de Rust il y avait un GC qu'ils ont abandonné, mais l'idée de remettre de la gestion automatique est toujours présente, cela semble être néanmoins compliqué. Il y a deux articles sur le sujet sur le blog d'un des membres de l'équipe du compilateur :

    Sinon quelle différence fais-tu entre un langage qui peut faire de la programmation système et un langage système ? Il y a des personnes qui écrivent des noyaux, sous la forme d'unikernel, en OCaml :

    MirageOS is a library operating system that constructs unikernels for secure, high-performance network applications across a variety of cloud computing and mobile platforms. Code can be developed on a normal OS such as Linux or MacOS X, and then compiled into a fully-standalone, specialised unikernel that runs under the Xen hypervisor.

    MirageOs

    Il y a bien des problèmes de latence liés au GC qui peuvent apparaître (contrairement à la gestion totalement manuelle du C par exemple) mais cela se gère aussi en adaptant son fonctionnement à l'application. Je ne connais pas le fonctionnement du GC de Haskell (mais d'après l'article c'est similaire à celui de OCaml), mais pour celui de OCaml ce chapitre de Real World OCaml en détail bien le fonctionnement.

    Le premier commentaire de l'article que tu cites renvoie sur un benchmark (dont je n'ai pas estimé la pertinence) qui mesure la latence d'un serveur web qui stocke une hashtable (pour le serveur web en OCaml, il utilise d'ailleurs une bibliothèque développée par l'équipe de MirageOS) :

    benchresult

    J'ai eu une polémique récemment sur le coup de la latence du GC de OCaml en rapport à ce benchmark du benchmarkgame de chez Debian. J'avais mal estimé le rôle du GC dans les mauvais résultats : comme pour le cas de ton article, il y a des objets trop gros qui restent vivants trop longtemps. En adaptant la taille de la minor heap on obtient de meilleurs résultats (j'utilise aussi la bibliothèque functory, qui fait une sorte de MapReduce, pour le parallélisme ) :

    open Functory.Cores
    let () = set_number_of_cores 4
    
    let set_minor_heap size = Gc.set {(Gc.get()) with Gc.minor_heap_size=size}
    
    type tree = Empty | Node of tree * int * tree
    
    let rec make i d =
      if d = 0 then Node(Empty, i, Empty)
      else let i2 = 2 * i and d = d - 1 in Node(make (i2 - 1) d, i, make i2 d)
    
    let rec check = function Empty -> 0 | Node(l, i, r) -> i + check l - check r
    
    let min_depth = 4
    let max_depth = 
      let n = try int_of_string(Array.get Sys.argv 1) with _ -> 10 in
      max (min_depth + 2) n
    
    (*
    * c'est ici que j'adapte la taille de la minor heap 
    * ce qui évite de nombreuse copie entre la minor et la major heap
    * et réduit grandement la latence due au GC 
    *)
    let () = set_minor_heap (8 lsl max_depth)
    
    let stretch_depth = max_depth + 1
    
    let () =
      let c = check (make 0 stretch_depth) in
      Printf.printf "stretch tree of depth %i\t check: %i\n" stretch_depth c
    
    let long_lived_tree = make 0 max_depth
    
    let worker (niter, d) =
      let c = ref 0 in
      for i = 1 to niter do 
        Gc.minor();
        c := !c + check(make  i d) + check(make (-i) d)
      done;
      !c
    
    let () =
      flush stdout;
      let tasks =
        let l = ref [] in
        for i = ((max_depth - min_depth) / 2 + 1) - 1 downto 0 do
          let d = min_depth + i * 2 in
          let niter = 1 lsl (max_depth - d + min_depth) in
          l := ((niter, d), None) :: !l
        done;
        !l
      in
      let res = ref [] in
      let master ((niter, d), _) c =
        res := (2*niter, d, c) :: !res;
        []
      in
      (* il n'y a plus de major collection pendant ce calcul *)
      compute ~worker ~master tasks;
      let log (niter, d, c) =
        let s = Printf.sprintf "%i\t trees of depth %i\t check: %i" niter d c in
        print_endline s;
      in
      List.iter log (List.sort (fun (_, d, _) (_, d', _) -> compare d d') !res);
      Printf.printf "long lived tree of depth %i\t check: %i\n"
        max_depth (check long_lived_tree)

    avec ce code, sur ma machine à 4 cœurs, j'obtiens comme résultat standard avec un time btree 20 :

    real    0m5.411s
    user    0m16.320s
    sys     0m0.492s
    

    là où avec le code C le plus performant je suis dans cet ordre de grandeur :

    real    0m3.721s
    user    0m13.144s
    sys     0m0.204s
    

    c'est pas si mal et bien mieux que le 25.82 s du meilleur code OCaml du test. D'autant que, n'étant pas moi même programmeur (je suis mathématicien et logicien, pas informaticien), il est fort probable qu'un spécialiste du langage puisse encore améliorer cela.

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 1.

    Oui c'est ce que je disais dans mon commentaire avec "si les objets contenus sont mutable ils pourraient être modifiés de l'extérieur malgré ça"

    J'avais bien compris, mais je préférais illustrer par un exemple pour que ceux qui se demandent comment éviter ces effets de bords puissent avoir une illustration du problème. Résultat cela m'a amené à regarder s'il n'y avait pas une fonction de copie profonde d'une structure en Python, et voilà : copy.deepcopy() :-)

    >>> class Test:
    ...     def __init__(self, items=()):
    ...       from copy import deepcopy
    ...       self.items = list(deepcopy(items))
    ... 
    >>> l = [1, [2, 3], 4]
    >>> a = Test(l)
    >>> l.append(5)
    >>> l[1].append(6)
    >>> l
    [1, [2, 3, 6], 4, 5]
    >>> a.items
    [1, [2, 3], 4]

    La décorrélation est totale ! \o/

    Et le système d'emprunt de références va tout de même garantir qu'un seul endroit du code a la responsabilité de modification à un instant T.

    C'est un système de lock pour les problèmes d'accès concurrents ? Ou si plusieurs structures se partagent une référence, seule une peut la modifier jusqu'à ce qu'elle soit désallouée et une autre en prend le contrôle, et ainsi de suite ? (j'essaye de traduire les notions de propriétaire/temps de vie).

    le système de typage est moins puissant qu'en Haskell/OCaml

    Il y a des travaux pour améliorer encore celui d'OCaml pour pouvoir faire du polymorphisme ad-hoc et de la surcharge d'opérateur en utilisant son système de module et les foncteurs. Et on peut aussi manipuler des références (mais là c'est sans garde-fou) et faire de la programmation système en OCaml (la seule limite actuelle, c'est le GC qui ne gère pas les accès concurrents sur la major heap ce qui pose problème pour le parallélisme, cf multicore OCaml).

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 1.

    C'est même plus propre par rapport à ma solution et au problème initial : si on passe une liste dans l'appel au constructeur, on peut se retrouver au même désagrément sur les effets de bords.

    >>> class Test:
    ...    def __init__(self, items=None):
    ...      self.items = [] if items is None else items
    ...    
    ...    def add(self, item):
    ...      self.items.append(item)
    ... 
    >>> l = [1, 2, 3]
    >>> a = Test(l)
    >>> l.append(4)
    >>> a.items
    [1, 2, 3, 4]

    Là où avec ta solution, en plus d'accepter plus de type comme paramètre du constructeur, l'attribut est un peu mieux décorrélé (mais pas totalement) de l'argument.

    >>> class Test:
    ...     def __init__(self, items=()):
    ...         self.items = list(items)
    ... 
    >>> l = [1, [2, 3], 4]
    >>> a = Test(l)
    >>> l.append(5)
    >>> a.items
    [1, [2, 3], 4]
    >>> l[1].append(6)
    >>> a.items
    [1, [2, 3, 6], 4]
    >>>

    Je ne sais pas ce que sont les notions de propriétaires/temps de vie en Rust, mais je dirais pour ma part : comme quoi le fonctionnel pur (ou l'observationnellement immuable) ça a son intérêt ! :-)

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

  • [^] # Re: Pour bloquer la mise à jour

    Posté par  . En réponse au journal Vague d’intérêt pour GNU/Linux vs Windows 10 « imposé » ?. Évalué à 1.

    Heu, le gestionnaire d'openSUSE, construit autour de libsolv?

    Je ne connaissais pas. Quel est l'ordre de grandeur du temps de résolution ?

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 3.

    Je suis entrain de me former à Python. Comment expliquer ce comportement, et comment faudrait-il modifier le code pour avoir un bon fonctionnement ?

    Le comportement observé est du au fait que les arguments optionnels ne sont évalués qu'une seule fois lors de la définition de la fonction. S'ils sont mutables, et qu'on les affecte comme attribut aux objets de la classe, ils ne sont alors pas propre aux objets mais deviennent des variables de classe. Pour éviter cela, si ce n'est pas ce que l'on souhaite, il faut utiliser la solution de flan et utiliser la valeur None comme paramètre par défaut, puis tester dessus pour avoir des listes vides différentes pour chaque objet de la classe.

    >>> class Test:
    ...   def __init__(self, items=None):
    ...     self.items = [] if items is None else items
    ...   
    ...   def add(self, item):
    ...     self.items.append(item)
    ... 
    >>> a = Test()
    >>> a.add('foo')
    >>> b = Test()
    >>> b.add('bar')
    >>> a.items
    ['foo']
    >>> b.items
    ['bar']

    En OCaml, par exemple, les arguments optionnels sont implicitement représentés par le type polymorphe 'a option :

    let foo ?x () =
      match x with
      | None -> print_endline "pas d'argument optionnel"
      | Some i -> Printf.printf "l'entier %i\n" i
    ;;
    val foo : ?x:int -> unit -> unit = <fun>
    
    foo ~x:2 ();;
    l'entier 2
    - : unit = ()
    
    foo ();;
    pas d'argument optionnel
    - : unit = ()

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 8.

    C'est ce qui m'a séduit dans Haskell.

    Je peux comprendre, les effets de bords et les valeurs mutables ça peut avoir des comportements surprenants :

    >>> class Test:
    ...   def __init__(self, items=[]):
    ...     self.items = items
    ...   
    ...   def add(self, item):
    ...     self.items.append(item)
    ... 
    >>> a = Test()
    >>> a.add("foo")
    >>> b = Test()
    >>> b.add("bar")
    >>> a.items
    ['foo', 'bar']
    >>> b.items
    ['foo', 'bar']

    La valeur optionnelle, qui est mutable, est initialisée une fois et partagée par toutes les instances ! \o/

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

  • [^] # Re: Uber, Lebonrecel, Airbnb : pas de le l'économie collaborative

    Posté par  . En réponse au journal Le Bon Coin, Airbnb, Uber : Les prochaines poules aux œufs d'or. Évalué à -2.

    Tous les travaux universitaires existants montrent que le réel scandale de l'impôt français, c'est qu'il n'est pas assez progressif, et que les pauvres payent parfois plus que les riches.

    J'aurais plutôt dit que le réel scandale de l'impôt français (il n'est pas le seul), c'est qu'il soit progressif. Le problème c'est l'assiette pas le taux : à taux fixe, celui qui a plus, paye plus.

    Art. 13. Pour l'entretien de la force publique, et pour les dépenses d'administration, une contribution commune est indispensable : elle doit être également répartie entre tous les citoyens, en raison de leurs facultés.

    Art. 14. Tous les Citoyens ont le droit de constater, par eux-mêmes ou par leurs représentants, la nécessité de la contribution publique, de la consentir librement, d'en suivre l'emploi, et d'en déterminer la quotité, l'assiette, le recouvrement et la durée.

    Déclaration des Droits de l'Homme et du Citoyen de 1789

    J'ai tendance à penser que dans la langue du XVIIIe l'expression « en raison de » est synonyme de « au prorata ».

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 2.

    Haskell est énorme mais fait peur. Ocaml c'est sympa.

    Honnêtement, pour ce qui est de leur système de types, c'est du pareil au même entre les deux; en dehors de l'absence de typeclasses en OCaml mais plus pour longtemps (modular implicits). Leur système prend leur inspiration dans la correspondance de Curry-Howard (ou correspondance preuve-programme, correspondance formule-type), ce qui en fait un système très expressif mais très exigeant : quand le compilateur gueule, c'est comme un mathématicien qui se plaint que la preuve d'une énoncé n'est pas correcte.

    Là où Haskell peut faire peur, c'est que comme il fait du fonctionnel pur il faut recourir à des monades pour les effets de bords : ça peut effrayer au premier abord. OCaml de son côté permet de mélanger les paradigmes impératif, fonctionnel et objet; et faire de la réutilisation de code (comme pour le paradigme objet) via les variants polymorphes (je ne sais pas s'il y a un équivalent en Haskell).

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

  • [^] # Re: On s'en bat le steak

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 3.

    Pas du tout. Ça permet de se rendre compte de beaucoup d'erreurs à la compilation.

    Effectivement, quel est l'intérêt de laisser compiler un code qui de toute façon ne pourra que planter à l'exécution ?1 et cela, tout en refusant de le compiler parce qu'il est mal indenté… Il y a vraiment des choix de design que je ne comprends pas dans ce langage. :-/


    1. ce n'est pas comme s'il n'existait pas des techniques éprouvées depuis des décennies pour éviter ce genre de désagréments. 

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

  • [^] # Re: Pour bloquer la mise à jour

    Posté par  . En réponse au journal Vague d’intérêt pour GNU/Linux vs Windows 10 « imposé » ?. Évalué à 2. Dernière modification le 31 mai 2016 à 09:11.

    Jsais pas fais le test toi même.

    Par curiosité j'ai installé une debian testing hier avec juste le destkop de base et le groupe de packages multimedia + les paquets apt-cusd et aspcud. J'ai voulu dist-upgrader en SID et avec le solver aspcud il a refusé de faire quoique ce soit me signalant une liste de paquets broken. Avec le solveur de base j'ai pas compté mais il a upgradé un bon nombre de packages sans forcément interrompre tout.

    J'étais étonné alors hier soir j'ai installé une jessie 8.3-amd64 dans une VM avec Gnome. Une fois l'installation finie, j'installe apscud et apt-cudf. Je modifie mon source.list pour le faire ressembler à cela :

    deb http://ftp.fr.debian.org/debian/ jessie main contrib non-free
    deb-src http://ftp.fr.debian.org/debian/ jessie main contrib non-free
    
    deb http://security.debian.org/ jessie/updates main
    deb-src http://security.debian.org/ jessie/updates main
    
    # jessie-updates, previously known as 'volatile'
    deb http://ftp.fr.debian.org/debian/ jessie-updates main
    deb-src http://ftp.fr.debian.org/debian/ jessie-updates main
    
    # testing
    deb http://ftp.fr.debian.org/debian/ testing main
    
    # sid
    deb http://ftp.fr.debian.org/debian/ sid main
    

    Je lance un apt-get update puis apt-get dist-upgrade et apt-get dist-upgrade --solver aspcud. Les deux me proposent bien une procédure de mise à jour, la différence : le premier voulait me supprimer, entre autre, gdm3, gnome et gnome-shell. :-/

    Pour ce qui est de la stratégie de aspcud c'est celle par défaut dans le fichier /etc/apt-cudf.conf :

    solver: mccs-cbc , mccs-lpsolve
    upgrade: -lex[-new,-removed,-notuptodate]
    dist-upgrade: -lex[-notuptodate,-new]
    install: -lex[-removed,-changed]
    remove: -lex[-removed,-changed]
    trendy: -lex[-removed,-notuptodate,-unsat_recommends,-new]
    paranoid: -lex[-removed,-changed]
    
    solver: *
    upgrade: -new,-removed,-notuptodate
    dist-upgrade: -notuptodate,-new
    install: -removed,-changed
    remove: -removed,-changed
    trendy: -removed,-notuptodate,-unsat_recommends,-new
    paranoid: -removed,-changed
    

    c'est-à-dire qu'il va chercher à minimiser le nombre de nouveaux paquets -new et ceux qui ne sont pas à jour -notuptodate.

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

  • [^] # Re: A force...

    Posté par  . En réponse au journal Typage statique pour Python. Évalué à 1.

    Mais que se passe-t-il si je donne à la fonction pgcd sans annotations des objets pour lesquels sont définis les opérations mod et comparaison à 0 (une classe polynôme par exemple) ? Est ce que le systême d'inférence va me mettre des bâtons dans les roues parce qu'il a fait de mauvaise hypothèses ?

    En OCaml il n'y as pas de typeclasses comme en Haskell (il y a des travaux pour rajouter cela au langage) et on ne peut pas surcharger les opérateurs. Les opération mod et la comparaison à 0 ne fonctionnent que sur les types int et donc le système d'inférence identifie que les paramètres de la fonction sont nécessairement de type int.

    ( mod );;
    - : int -> int -> int = <fun>
    
    0;;
    - : int = 0

    Néanmoins on peut définir une module qui implémente le type polynôme avec sa propre fonction mod et sa valeur zero.

    Pour reprendre le cas du pgcd si je compare la valeur à un float comme 0.0 le système se plaindra :

    let rec pgdc a b =
      match a mod b with
      | 0. -> b
      | r -> pgcd b r
    ;;
    Error: This pattern matches values of type float 
           but a pattern was expected which matches values of type int

    Pour comparer avec python :

    >>> def f(i):
    ...   print("l'entier %i" % i)
    ... 
    >>> f(2)
    l'entier 2
    >>> def g():
    ...   f("a")
    ... 
    >>> g()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in g
      File "<stdin>", line 2, in f
    TypeError: %i format: a number is required, not str
    

    là où avec l'inférence de type l'erreur sur g est determiné statiquement à la compilation

    let f i = Printf.printf "l'entier %i" i;;
    val f : int -> unit = <fun>
    
    f 2;;
    l'entier 2- : unit = ()
    let g () = f "a";;
    Error: This expression has type string but an expression was expected of type int

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