Mais je trouve ça trivial, par contre. L'écriture du lambda… on peut considérer ça comme une fonction je suppose, je suis mitigé à ce sujet.
Je n'ai jamais dis que on ne pouvais pas le faire en C++ attention. Mais quand tu regardes le lambda, avec l’opérateur ternaire, la subtilité sur l'ordre du test pour l'ascendant / descendant (que tu as mal pris en compte, de là à conclure que c'est source d'erreur ;) et la logique à écrire pour chaîner les éléments.
Tient, j'ai une autre proposition marrante, en C++ :
Je pense que si on pouvais passer un conteneur d'un coup plutôt que de spécifier les bornes, ça allègerait déjà pas mal de code (perso, j'utilise les conteneurs entiers la plupart du temps…).
C'est en discussion pour c++17 et j’espère que cela aboutira.
Je te remercie pour ta critique et je vais me permettre d'y répondre.
Prof, c'est manifestement ni mon métier (sachant que, pour le moment, j'ai pas de taf, si ça veut dire quelque chose), ni le tien.
Et merde… 30% de mon salaire vient de mes enseignements à l'université… Mais bon, comme on dit, "Ceux qui savent font, les autres enseignent".
Je ne dis pas ça pour t'empêcher de faire de nouveaux journaux, au contraire… et puis merde, je vais balancer le reste sans politiquement correct, et faire confiance à ton sens critique, puisqu'après tout, tu postes sur linuxfr.
Pas de souci ;)
Avant de continuer je voulais mettre un truc au point. Ce n'est pas un cours d'introduction à Haskell, pour cela il y en a des dizaines écrits par des gens plus brillants que moi et avec de beaux dessins comme LHAH.
La première chose qu'il me semble importante de noter, c'est l'aspect très… matheux, du langage. C'est con à dire, mais, moi, j'ai jamais été un matheux. Ouai, ok, j'ai un bac sti "génie" électronique, mais je me tapais des 8 en maths, quand, en physique je tapais du 13+ et du 15+ en élec.
Il n'y a rien de matheux dans mon article, peut-être la notion de relation d'ordre et les monoids, mais j'ai juste donné un nom à un concept connu (l'addition) et montré que ce concept est plus général et que Haskell propose une façon standard de le manipuler. Ca ne choque personne que le + soit utilisé en python ou en C++ pour concaténer des chaines de caractères, mais si je t'explique que c'est parce que on peut voir une similarité entre le + des chaînes de caractère et celui des entiers, que cette similarité se nomme Monoid et que les développeurs d'Haskell ont choisis l'opérateur <> pour la représenter et ainsi éviter la confusion avec le +.
Soyons sérieux, un instant. Ta fonction, elle prend [a] en argument. Soit. dans ton exemple de déclaration, je vois le [a] 2 fois. Quelle occurrence est donc le paramètre, histoire que l'on puisse au moins deviner quelle autre occurrence est le retour?
Je l'admet, je ne prend personne par la main dans ce passage et la clarification qui est quelques paragraphes plus bas arrive trop tard, c'est clairement une erreur de pédagogie de ma part. a -> b, a est le paramètre, b est la valeur de retour, la flèche indiquant que a donne b.
(Remarque sur l'orthographe, rien à dire, j'ai laissé quelques coquilles. Moi c'était plutôt du genre 15+ en math et 2 en français et en physique, chacun son handicap ;)
Le coup du ordonnable (2n, au passage, me dit mon correcteur), on le remarque comment?
Grâce à la contrainte Ord a en début de ligne. Je n'ai pas voulu faire un cours de syntaxe Haskell très précis et j'ai supposé que ma phrase "On remarque que a est Ordonable" laisserait comprendre la relation entre Ord et Ordonable…
Encore une fois, tu me rejette dans mon ignorance crasse, et ça me semble mauvais en terme de pédagogie.
Oui, toutes mes excuses, tu n'imagines pas le temps que j'ai passé à rajouter des paragraphs pour rendre mon histoire plus clair. Au début je voulais juste hurler "Énorme, (->) est une instance de Monoid et cela permet de faire du tri de façon trop super"…
Ensuite, une "liste simplement chaînées" (j'ai reproduit le 's' après quelques secondes d'hésitations, parce que ça me fait chier de jouer les nazi du french) et non modifiable. Le haskell semble séduire quelques utilisateur du C++, mais je n'ai jamais compris (ni cherché) pourquoi? Ok, une liste simple, ça veut dire 1 pointeur de moins par élément, soit 8 bytes (ou octets en français, mais le terme est moins précis, niveau sémantique, un byte n'étant pas forcément constitués de 8 bits) et dans le cas de haskell c'est du const. Bien. Pourquoi pas. Mais dans ce cas, quel est l'impact en terme de perfs lorsqu'une fonction à pour simple but de construire une liste chaînée? Doit-elle être copiée-collée? J'espère bien que non.
Toi tu fais partie de ces étudiants qui n'aime pas comprendre l'ensemble et qui s'attachent aux points de détails. C'est intéressant ;) Ton "pourquoi" est vague donc je vais essayer de répondre à de nombreuses questions possibles :
Pourquoi non mutable ? C'est subjectif. Parce que on aime bien cela en fonctionnel et surtout en Haskell. Cela permet de ne pas se demander qui modifie quoi. Cela simplifie le parallélisme (personne ne va venir modifier ta donnée sans te prévenir) donc on préfère commencer non mutable et introduire de la mutabilité contrôlée quand c'est nécessaire. Cela rend aussi les tests plus simple (pas d'état caché) et le raisonnement sur le code plus simple, car il n'y a pas de modification d'état caché possible.
Doit elle être copiée collée en permanence ? Non, de temps en temps plus qu'en C++, de temps en temps moins. Exemple, soit une liste chainée list0 qui contient A, B et C, les pointeurs étant representés par des -> :
list0 -> A -> B -> C -> FIN
Si tu veux ajouter Z en tête de liste pour créer la liste list1, tu peux crée une nouvelle liste et faire pointer Z vers le A d'avant, tu auras :
list1 -> Z -> list0
Mais pas de copie, puisque Z pointe vers l'ancienne liste en mémoire. Il y a un partage de A B et C entre les deux listes. Si tu veux maintenant supprimer un élément de list0, tu vas faire pointer ta nouvelle liste vers le B.
Pourquoi pas doublement chaînée ? Parce que ce n'est pas possible en non mutable, car tu devrais modifier le nœud d'après pour lui faire pointer sur le nœud d'avant, donc en fait tu devrais crée un nouveau nœud, qui invaliderait le pointeur du nœud encore avant, et ainsi de suite…
Performance. C'est LENT, comme le diable, mais tout autant que les listes chaînées en C++ ou en python. C'est pour cela que on a aussi des vecteurs et d'autres structures et que le compilateur est généralement pas trop bête, quand tu "crée" un nouveau vecteur en changeant juste une case, il peut utiliser l'ancien si celui-ci n'est plus nécessaire. La plupart des mes algos en Haskell sont en moyenne 2x plus lent que du c++ optimisés, ce qui est intolerable dans mon domaine d'activité, mais est carrément supportable autre part ;)
Performance. Haskell est paresseux et les listes sont rarement utilisées comme conteneur, mais plutôt comme générateur de valeur. Souvent cela permet d'exprimer des algorithmes complexes simplement tout en ayant des performances très bonnes.
En résumé, c'est un paradigme. Certains algorithmes s'expriment super bien en non mutable, d'autres moins. Certains ne sont pas possibles dans l'un ou l'autre paradigme. A titre de comparaison, à l'université, j'ai fais deux groupe dans une UE, la première semaine, j'ai fais à un groupe des arbres binaires mutables et à l'autre groupe des arbres binaires non mutable. Et bien le groupe mutable galérait encore avec les pointeurs mutée pour faire une simple insertion tandis que le groupe non mutable n'avait eu aucun problème. Cela dépend des fois.
Bref, ton journal est bien trop… hum… c'est cru, mais mon sentiment, alors, voilà: imbu de ta connaissance.
Le drame de ma vie, je suis un pauvre type dépressif sans aucune confiance en moi et tout le monde me reproche d'être trop confiant. Désolé si je t'ai donné ce sentiment.
Le C, à imposé sa syntaxe comme un quasi-standard. Le pascal aussi. Les paradigmes de ces langages sont peut-être moins efficaces (ref. souhaitée) mais plus adaptés à la façon de penser des gens normaux (ref souhaitée aussi), c'est à dire pas théoriciens des langages ni matheux (ref souhaitée encore, notez comme je m'auto-troll).
Excuses moi de te dire cela, mais tu es biaisé par ton bagage et tu penses que la façon de penser des gens normaux est la tienne. Mais les gens normaux qui n'ont jamais programmé, je peux t'assurer qu'ils ne pensent ni en C ni en Haskell. On peut troller autant que on veut, mais en haskell l'ordre sur la signature d'une fonction vas de la gauche vers la droite, en premier les arguments, à la fin le résultat. En C c'est le résultat en premier, et après les arguments, sauf en C++ où on peut aussi mettre le résultat à la fin. Et une convention fait que on trouve des résultats en argument au milieu… Je rentre dans le troll, mais je n'ai jamais trouvé l'ordre des arguments en C++, python, java, … "Naturel". Et pourtant j'ai utilisé ces langages avant Haskell et la plupart sont plus jeunes que Haskell. Essaye de te rappeler tes débuts en programmation et ce sur quoi tu bloquais et tu verras que rien n'est naturel ;) Mes bêtes noires à moi c'était la post et pré incrémentation (a++ ou ++a), les constructeurs en C++ et les boucles…
Une question simple, pour clore ce déjà trop long post: pourquoi ne pas nous montrer le traditionnel hello world, en C, puis en C++ (2 langages différents pour moi, bien que très proches), puis en JS (à la mode), python (idem), et enfin haskell?
Même si le hello world ne montre quasiment rien du langage, il permets au moins aux devs de savoir comment écrire un programme qui ne fait rien. C'est, l'ai de rien, très important de savoir faire un truc qui ne fait rien avec le minimum de code. En fait, c'est la base pour savoir écrire un programme qui fait uniquement ce qu'on lui demande.
Mais je ne faisais pas un cours d'Haskell… Je me suis embêté à comparer 2 langages (python et Haskell) et j'aurais du en comparer plus en refaisant une introduction complète sur la syntaxe et les subtilités de chaque langage…
Mais si tu y tient, voila l'implementation comparée de mon histoire de tri en Haskell et en C++… Je sais lequel est le plus naturel pour moi ;)
#include <iostream>#include <algorithm>#include <string>#include <vector>// Mon typestructEtudiant{std::stringnom;floatnote;};// Et sa fonction d'affichagestd::ostream&operator<<(std::ostream&stream,constEtudiant&etu){stream<<"Etudiant{"<<etu.nom<<", "<<etu.note<<"}";returnstream;}intmain(){std::cout<<"Hello World"<<std::endl;// Création de la liste, j'ai utilisé un vecteur ici contre une liste en Haskell, c'est débatable.std::vector<Etudiant>etudiants{{"Batman",20},{"Hulk",12},{"Chuck Norris",12}};// Tri suivant les notes descendant et les noms ascendantsstd::sort(std::begin(etudiants),std::end(etudiants),[](constEtudiant&etu0,constEtudiant&etu1){if(etu0.note==etu1.note){// second critère de tri: le nom, ascendantreturnetu0.nom<etu1.nom;}else{// premier critère de tri: la note, descendant// ATTENTION, c'est normal d'avoir etu1 < etu0returnetu1.note<etu0.note;}});// affichage de la liste triéestd::cout<<"{";for(auto&&item:etudiants){std::cout<<item;std::cout<<", ";}std::cout<<"}";std::cout<<std::endl;return0;}
importData.List(sortBy)importData.Monoid((<>))importData.Ord(comparing)-- Fonctions utilitaires pour rendre le code plus lisibleascending=comparingdescending=flip.comparing-- Mon type étudiant et sa fonction d'affichage automatiquement derivéedataEtudiant=Etudiant{nom::String,note::Float}deriving(Show)-- Fonction main-- petite subtilité du haskell, il y a une distinction entre les données pures dans le let-- et les effets de bords dans le do. Ici je mens et je cache pleins de choses, mais bon ;)main=let-- ma liste d'étudiantetudiants=[Etudiant"Batman"20,Etudiant"Hulk"12,Etudiant"Chuck Norris"12]-- Que je tri par ordre descendant de note et ascendant de noms...-- Ce commentaire est il inutile ?sortedEtudiants=sortBy(descendingnote<>ascendingnom)etudiantsindo-- AffichageputStrLn"Hello World"-- Il faut appeler show pour convertir en String un étudiantputStrLn(showsortedEtudiants)
PS: je n'ai lu que le court passage que j'ai cité. Ça m'a déjà semblé trop inaccessible, trop chiant. Cool, on est dredi.
Merci d'avoir pris le temps de donner ton avis sur le début. J'espere que mes réponses t’intéresserons.
Ce qui est imonde c'est cet article, mais par contre je trouve que ce code au contraire est "lisible" et "simple à maintenir" (Et je rentre dans ton troll ;)
Pour trier une liste d'étudiant par note descendentes et par nom ascendant :
Je trouve cela très lisible et simple à maintenir. Après c'est le problème de tous les DSL, c'est qu'il y a une abstraction (ici <> et ce qui se cache sous ascending et descending), mais celle-ci n'est pas nécessaire à comprendre pour comprendre ce que fait réellement le code ni le modifier. Au pire on peut renommer <> en andThenSortBy pour rendre cela encore plus lisible.
Je ne suis pas certain que je pourrais écrire plus clair en haskell et je sais que écrire aussi clair en python ou c++ me demandera d'ajouter des fonctions non triviales qui ne sont pas dans la librairie standard.
(->) r est une instance de Monad, donc "Les fonctions sont des monades" ;) Mais je t'avoue que au niveau théorie des catégories, je suis largué et comme tu le dis, on peut vraiment dire n'importe quoi et que cela ai du sens.
Mais en effet, tu as raison, c'est simplement Reader en mode anonyme, merci de m'avoir fait observer cela.
Et les fonctions sont aussi des Monads… Et là j'ai souvent le cerveau qui fond. Je crois que je me suis servi une fois de l'instance d'Applicative pour les fonctions, pour un code golf, mais plus jamais ;) Alors l'instance de Monad ;)
L'utilisation d'une telle fonction de comparaison qui doit choisir entre "plus grand", "plus petit" ou "égal" est loin d'être aisé dans le cas général où on peut être "aucun des trois".
Tu as un exemple de cela ? Si tu veux trier, tu dois forcement pouvoir définir un ordre, au moins arbitraire. Tu peux te contenter de <= pour établir une relation d'ordre. Il y a un mapping bijectif entre key et cmp, du moins je pense.
Finalement, comme plusieurs l'ont fait remarquer, utiliser key est généralement plus efficace que cmp, puisque key est appelé une seule fois par élément (O(n) donc), puis la comparaison (O(n log n) pour ordonner) est effectuée en C si les clés sont des type de base. À l'opposé, utiliser cmp fait en sorte que ces O(n log n) comparaisons impliqueront toutes un appel d'une fonction Python.
En effet, mais l'overhead mémoire peut ne pas être négligeable. Je n'ai pas regardé l'implementation, mais j'imagine que le tri en place avec cmp peut se faire en O(1) en mémoire, si on ignore la mémoire prise par la liste initiale. Alors que le tri avec le DSU ajoute forcement un overhead en O(n) pour stocker les résultats de l'appel à key. En fonction du problème cela peut être une limitation, même si j'avoue que dans le cas d'un tableau énorme on passera surement par une autre structure, type numpy.
Pour le coté dense, oui je l'admet. Comme je le dis dans un commentaire plus haut c'est partie d'une moment "Whaou" simple mais qui demande du contexte, et puis j'ai toujours eu du mal à être concis ;( Saint Exupéry ne serait pas fier de moi.
Merci pour la remarque "marrant", je prend cela comme un compliment ;)
Par exemple, il n'est pas évident de se dire que la signature des fonctions est logique sans comprendre qu'une fonction de plusieurs paramètres peut-être écrite comme la composition de plusieurs fonctions partielles dont la dernière est à 1 paramètre, ceci expliquant la notation des types en Haskell (Curryfication) qui ne ressemble pas au C++/Python.
J'ai essayé de faire un paragraphe la dessus mais finalement je me rend compte qu'il arrive au milieu du texte alors qu'il mériterait d'être au début.
Par contre, je pense qu'il y a une question à laquelle il serait bon de répondre par un journal / dépêche : celui de l'organisation du code, ou de sa différence entre l'approche objet, l'approche déclarative, l'approche fonctionnelle pure sans DataType, avec DataType, impérative sans Objet, etc.
Tu aurais un exemple de la problématique que tu sous entend ? J'ai l'impression que je suis biaisé parce que depuis que je fais du Haskell mon style en C++ et en Python a évolué vers quelque chose qui ressemble à Haskell et forcement je suis convaincu que c'est la bonne solution ;)
J'ai tendance à penser que l'approche fonctionnelle motive la séparation donnée / comportement alors que l'approche objet couple souvent les deux, mais à mon avis ce n'est pas imposé par la technologie, c'est plus un choix de développeur. D'ailleurs la STL C++ a vraiment une approche découplée depuis quelque temps, std::begin() ou lieu de obj.begin() ou l'ensemble <algorythms>, où le discours dernier sur le fait que f.method() pourrait devenir équivalent à method(f) pour unifier les styles d'appel.
La seule grosse difference que je trouve vraiment dure entre les deux approches c'est pour le polymorphisme runtime, (i.e: les appels virtuels en nomenclature POO). En fonctionnel, du moins en Haskell, j'ai l'impression que 99% des cas de figures sont remplacés par du curying ou des types sommes.
Par contre je ferais bien un article sur l'organisation de code et le typage. Je trouve que beaucoup de monde dans la communauté POO/mutable confonds généricité et absence de type. Un example que je donne souvent c'est pour la réalisation d'une librairie de vecteur pour un moteur 3D, un développeur c++/python vas faire une classe Point avec pleins d'operations : soustraction, addition, produit scalaire. Finalement il sera heureux, sous prétexte de généricité, d'utiliser la même classe pour generer les points, directions, normales, couleurs. De mon coté, j'aurais plus tendance à définir plusieurs classes / types, au moins Point, Direction, Normal / DirectionNormalisée, Couleur avec un sous ensemble de méthodes limitées aux besoins du domaine. (Par exemple, pas d'addition entre Point, pas de produit scalaire entre Couleur…). Mais encore une fois, c'est Haskell qui m'a biaisé l'esprit de cette manière, mais ce n'est pas inapplicable aux C++…
Bon par contre je vais le re-lire à tête reposée par ce que je ne suis pas sur d'avoir tout capté :-)
En même temps c'est dense ;) N'hésites pas à discuter les points pas clairs. Je suis parti de ma révélation de hier soir de "Mon dieu, les fonctions sont aussi des Monoids" et en fait je suis arrivé à ce truc immonde ;)
Quand j'ai découvert Haskell, j'ai cru que c’était le plus beau langage du monde. Ce qui est le cas (a part quelques trucs subtiles).
Puis j'ai compris certains concepts, alors j'ai voulu les appliquer au c++. La première chose que j'ai fais c'est implémenter mon propre Maybe. (qui se nomme std::optional dans c++17 le draft).
Mais en fait, cela n'a AUCUN intérêt sans la do notation malheureusement puisque cela force a chainer des lambdas. Exemple :
maybe<float> doSomething(...)
{
return doSomethingInt(...).bind([](int ret){
return doSomethingFloat(ret);
});
}
Alors, c'est personnel, mais ce n'est pas parce que le pointfree existe et qu'il y a plein d’opérateurs marrant qu'ils faut les utiliser.
Autant je trouve que la version 1 est lisible. Mais (Je me permet de corriger l’opérateur ;) (<=< existe, mais c'est la combinaison de kleishi inverse, enfin bref ;)
push_var_on_stack = pushq =<< get_var_addr Aussi, je crois que Haskell on prefere le camelCase ;)
Parce qu'en ce moment je prototype beaucoup et que ce langage me permet d'être super efficace, faire moins d'erreur et avec des performances correctes.
Après je reecrirais en C++ parce que c'est ce que nous utilisons dans notre compagnie.
Ba oui… Globalement c'est surtout les fichiers ignoré que je supprime avec git clean. Les autres j'ai tendance à supprimer à la main car c'est soit des fichier à ajouter, soit des fichiers qui doivent sans doute avoir une raison d'être là ou d'être bougé ailleurs.
Attention au git clean dans le mauvais répertoire, cela fait désordre…
Histoire vécue. Je fais souvent des git clean (enfin moi c'est du hg purge, mais c'est pareil) pour nettoyer un dépôt de tous les trucs inutiles qui sont arrivés et pan… plus de home…
Personnellement j'ai une solution avec un sous répertoire de mon home pour les fichiers de config versionnés et je crée automatiquement les liens symboliques qui vont bien.
Si on enlève evolve, Mercurial est au même niveau que Git sur la facilité de réécrire l'historique.
Je m'auto répond. La seule difference que je vois (et qui est vraiment un problème) c'est qu'au lieu de "cacher" les commits qui disparaissent du fait d'une réecriture (comme le fait git), ils sont supprimés et stockés dans un fichier de backup. Il est vrai que c'est une limitation importante, mais je n'y pensais plus tellement evolve marche bien depuis quelques années.
C'est vrai j'avais oublié evolve. Je m'y étais intéressé l'année dernière, mais l'avais mis de côté car j'évitais les extensions qui ne sont pas fournies en standard ou qui sont expérimentales. Apparemment ce n'est toujours pas fournie en standard ou jugée suffisamment stable, je ne la trouve pas dans la liste http://mercurial.selenic.com/wiki/UsingExtensions. J'attend ça avec impatience car pouvoir réécrire l'historique sans rien détruire, et pouvoir relire ces réécritures, c'est une tuerie.
C'est à mon sens un problème de communication de la part de l'équipe de Mercurial, c'est qu'ils ont tendance à être très conservateur et le système d'extension donne cette idée que seulement un petit sous ensemble des fonctionnalités sont vraiment correctes et que le reste c'est juste des truc un peu bancales qui marchent plus ou moins. J'ai l'impression qui Git fait un peu l'inverse, c'est à dire proposer un truc en standard, alors que cela marche plus ou moins et corriger dans les version suivantes.
De ma dernière discussion avec un développeur d'evolve (je ne peux pas prouver cette affirmation cependant), je crois avoir compris que la plupart des composantes d'evolve sont déjà dans le core de mercurial et c'est maintenant plus des problèmes cosmétiques qu'autre chose pour en faire une extension officielle.
En pratique, j'utilise evolve depuis quelques temps et aucun problème et je connais quelques développeurs qui bossent sur des "petites" base de code de "petites" entreprises "familiales" (genre facebook…) qui l'utilisent quotidiennement sans soucis majeurs.
Mais ça n'enlève rien à Git qui ne propose peut-être pas cette fonctionnalité, mais qui répond quand même au besoin de beaucoup de dev sur la facilité de réécrire l'historique, et ça depuis qu'il existe.
Si on enlève evolve, Mercurial est au même niveau que Git sur la facilité de réécrire l'historique.
Hormis github, qui est un argument tout à fait valable, je ne vais aucun avantage à git comparé à mercurial.
En faisant abstraction de son horrible UI, Git permet d'être plus souple au quotidien : quand je fork un dépôt Git pour y contribuer, je peux me permettre de retravailler l'historique de ma branche comme je le souhaite sur mon dépôt perso (y'a peu de chance que quelqu'un l'ait forké à son tour donc je me permet de tout péter à coup de git rebase et git push -f avant de faire la pull request sur le projet upstream). Simple et rapide pour moi dans le cadre des petites contributions, et historique lisible pour ceux qui vont faire la revue de code avant un éventuel merge.
Et ? C'est pareil avec Mercurial. Je trouve même que c'est bien plus simple qu'avec Git. Avec mercurial, tu update (checkout) le commit qui t’intéresse, tu commit --amend, puis en utilisant evolve, tous les autres commit sont rebasés par dessus.
Tu peux toujours faire du reordonancement et de la suppression avec histedit (qui est l'équivalent à quelques détails de rebase -i).
La dessus, tu as le système de phase qui t’évite de rebaser des commits publique, donc qui t'evites de te retrouver par erreur après quelques heures de travail avec un dépôt dans un état qui va te prendre quelques heures à rétablir.
De plus, mercurial (avec evolve) garde l'historique des manipulation d'historique. Ainsi tu peux voir quel est le commit à l'origine d'un amend, ou d'un rebase. Git ne sait pas faire cela et tu dois chercher dans le reflog à la main, ce qui peut être laborieux.
Hormis quelques détails, comme l'absence d'auto !fixup ou !squash dans Mercurial et que l'ajout interactif d'éléments dans un commit (i.e, l’équivalent de git add -p) est un peu moins élaboré (il existe crecord, mais je n'ai jamais accroché), je suis très souvent frustré par les limitations et le manque de flexibilité de git comparé à mercurial.
Pour finir, Facebook a dernièrement annoncé qu'il passaient de git à mercurial pour des raison de flexibilité et de performance https://www.youtube.com/watch?v=X0VH78ye4yY et google annonce aussi collaborer avec facebook pour l'évolution de Mercurial.
Je sais que ce n'est pas le propos, mais discutons en quand même.
Personnellement j'ai deux uses case de mon clavier de téléphone, soit en mode une main avec l'index soit en mode deux mains avec les deux pouces (le plus rare).
Dans les deux cas, je trouve qu'une disposition type bépo (ou dvorak que j'utilise tous le temps sur des claviers traditionnels) n'est pas adaptée, mais la disposition "azerty" qui m'est proposé par défaut est tout aussi mauvaise.
Connaissez vous des claviers plus adaptés (qui minimise l'effort à faire avec le pouce) ? Y a il des études faites sur ce sujet ?
Pour avoir formaté la thèse de médecine et les publications de ma femme en utilisant zotero sous word il s'agit d'un très mauvais souvenir. Perte de travail, citations incohérentes ou manquantes, mise à jour qui supprime du texte du document… Seul point positif j'ai trouvé la documentation pour customiser les styles de citation très bonne contrairement a bibtex/biblatex.
Très clairement, pour les documents à faire en moins de trente minutes, j'utilise libre office.
Par contre, pour ma thèse qui contenant plusieurs centaines de figures générées (tableaux, courbes, graphiques, images issues de simulations), je n'ose même pas imaginer comment j'aurais fais cela avec Word / LOW.
Loin du troll, c'est une vraie question, ce type de production est elle possible en Word ?
Je pense qu'il parlaient de Word, le traitement de texte. Cependant je ne vois pas le rapport avec la matière première utilisée dans la conception de préservatifs…
Changer cela veut dire s'exposer au changement. Cela veut dire revalider les logiciel, cela veut dire s'assurer que ce qui fonctionnait avant fonctionne encore. Par exemple, quand tu compiles avec un vieux gcc, tu as des chances que la libc++ utilisée soit disponible sur la plupart des distributions, donc cela simplifie ton packaging. Quand tu as tout un environnement de build automatique, mettre a jour une partie de ta toolchain te force a de nombreuses mises à jour, potentiellement de nombreuses dépendances pour lesquelles tu doit maintenir ton code. En gros, c'est beaucoup de temps (et donc de cout) pour un gain qui n'est pas forcement important (hormis en qualité, mais la qualité a un cout, et n'est pas forcement la priorité numéro 1).
Autre chose, malheureuse, mais quand tu dois maintenir un logiciel sur de nombreuses plateforme, tu as tendance à niveler vers le bas.
[^] # Re: merci
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 3.
Je n'ai jamais dis que on ne pouvais pas le faire en C++ attention. Mais quand tu regardes le lambda, avec l’opérateur ternaire, la subtilité sur l'ordre du test pour l'ascendant / descendant (que tu as mal pris en compte, de là à conclure que c'est source d'erreur ;) et la logique à écrire pour chaîner les éléments.
Tient, j'ai une autre proposition marrante, en C++ :
C'est en discussion pour c++17 et j’espère que cela aboutira.
[^] # Re: Moi == pas doué, je suppose....
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 7.
Je te remercie pour ta critique et je vais me permettre d'y répondre.
Et merde… 30% de mon salaire vient de mes enseignements à l'université… Mais bon, comme on dit, "Ceux qui savent font, les autres enseignent".
Pas de souci ;)
Avant de continuer je voulais mettre un truc au point. Ce n'est pas un cours d'introduction à Haskell, pour cela il y en a des dizaines écrits par des gens plus brillants que moi et avec de beaux dessins comme LHAH.
Il n'y a rien de matheux dans mon article, peut-être la notion de relation d'ordre et les monoids, mais j'ai juste donné un nom à un concept connu (l'addition) et montré que ce concept est plus général et que Haskell propose une façon standard de le manipuler. Ca ne choque personne que le
+soit utilisé en python ou en C++ pour concaténer des chaines de caractères, mais si je t'explique que c'est parce que on peut voir une similarité entre le + des chaînes de caractère et celui des entiers, que cette similarité se nomme Monoid et que les développeurs d'Haskell ont choisis l'opérateur<>pour la représenter et ainsi éviter la confusion avec le+.Je l'admet, je ne prend personne par la main dans ce passage et la clarification qui est quelques paragraphes plus bas arrive trop tard, c'est clairement une erreur de pédagogie de ma part.
a -> b,aest le paramètre,best la valeur de retour, la flèche indiquant queadonneb.(Remarque sur l'orthographe, rien à dire, j'ai laissé quelques coquilles. Moi c'était plutôt du genre 15+ en math et 2 en français et en physique, chacun son handicap ;)
Grâce à la contrainte
Ord aen début de ligne. Je n'ai pas voulu faire un cours de syntaxe Haskell très précis et j'ai supposé que ma phrase "On remarque queaestOrdonable" laisserait comprendre la relation entreOrdetOrdonable…Oui, toutes mes excuses, tu n'imagines pas le temps que j'ai passé à rajouter des paragraphs pour rendre mon histoire plus clair. Au début je voulais juste hurler "Énorme,
(->)est une instance deMonoidet cela permet de faire du tri de façon trop super"…Toi tu fais partie de ces étudiants qui n'aime pas comprendre l'ensemble et qui s'attachent aux points de détails. C'est intéressant ;) Ton "pourquoi" est vague donc je vais essayer de répondre à de nombreuses questions possibles :
list0qui contient A, B et C, les pointeurs étant representés par des->:Si tu veux ajouter Z en tête de liste pour créer la liste
list1, tu peux crée une nouvelle liste et faire pointer Z vers le A d'avant, tu auras :Mais pas de copie, puisque Z pointe vers l'ancienne liste en mémoire. Il y a un partage de A B et C entre les deux listes. Si tu veux maintenant supprimer un élément de list0, tu vas faire pointer ta nouvelle liste vers le B.
Le drame de ma vie, je suis un pauvre type dépressif sans aucune confiance en moi et tout le monde me reproche d'être trop confiant. Désolé si je t'ai donné ce sentiment.
Excuses moi de te dire cela, mais tu es biaisé par ton bagage et tu penses que la façon de penser des gens normaux est la tienne. Mais les gens normaux qui n'ont jamais programmé, je peux t'assurer qu'ils ne pensent ni en C ni en Haskell. On peut troller autant que on veut, mais en haskell l'ordre sur la signature d'une fonction vas de la gauche vers la droite, en premier les arguments, à la fin le résultat. En C c'est le résultat en premier, et après les arguments, sauf en C++ où on peut aussi mettre le résultat à la fin. Et une convention fait que on trouve des résultats en argument au milieu… Je rentre dans le troll, mais je n'ai jamais trouvé l'ordre des arguments en C++, python, java, … "Naturel". Et pourtant j'ai utilisé ces langages avant Haskell et la plupart sont plus jeunes que Haskell. Essaye de te rappeler tes débuts en programmation et ce sur quoi tu bloquais et tu verras que rien n'est naturel ;) Mes bêtes noires à moi c'était la post et pré incrémentation (a++ ou ++a), les constructeurs en C++ et les boucles…
Mais je ne faisais pas un cours d'Haskell… Je me suis embêté à comparer 2 langages (python et Haskell) et j'aurais du en comparer plus en refaisant une introduction complète sur la syntaxe et les subtilités de chaque langage…
Mais si tu y tient, voila l'implementation comparée de mon histoire de tri en Haskell et en C++… Je sais lequel est le plus naturel pour moi ;)
Merci d'avoir pris le temps de donner ton avis sur le début. J'espere que mes réponses t’intéresserons.
[^] # Re: En ce qui concerne Python
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 2.
Au final je suis d'accord avec toi, je crois que je suis juste nostalgique du bon vieux temps ;)
[^] # Re: merci
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 3.
Ce qui est imonde c'est cet article, mais par contre je trouve que ce code au contraire est "lisible" et "simple à maintenir" (Et je rentre dans ton troll ;)
Pour trier une liste d'étudiant par note descendentes et par nom ascendant :
Je trouve cela très lisible et simple à maintenir. Après c'est le problème de tous les
DSL, c'est qu'il y a une abstraction (ici<>et ce qui se cache sousascendingetdescending), mais celle-ci n'est pas nécessaire à comprendre pour comprendre ce que fait réellement le code ni le modifier. Au pire on peut renommer<>enandThenSortBypour rendre cela encore plus lisible.Je ne suis pas certain que je pourrais écrire plus clair en haskell et je sais que écrire aussi clair en python ou c++ me demandera d'ajouter des fonctions non triviales qui ne sont pas dans la librairie standard.
[^] # Re: merci
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 2.
(->) rest une instance deMonad, donc "Les fonctions sont des monades" ;) Mais je t'avoue que au niveau théorie des catégories, je suis largué et comme tu le dis, on peut vraiment dire n'importe quoi et que cela ai du sens.Mais en effet, tu as raison, c'est simplement
Readeren mode anonyme, merci de m'avoir fait observer cela.[^] # Re: merci
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 3.
Et les fonctions sont aussi des Monads… Et là j'ai souvent le cerveau qui fond. Je crois que je me suis servi une fois de l'instance d'
Applicativepour les fonctions, pour un code golf, mais plus jamais ;) Alors l'instance de Monad ;)[^] # Re: En ce qui concerne Python
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 3.
Merci, je ne connaissais pas
cmp_to_key.Tu as un exemple de cela ? Si tu veux trier, tu dois forcement pouvoir définir un ordre, au moins arbitraire. Tu peux te contenter de <= pour établir une relation d'ordre. Il y a un mapping bijectif entre key et cmp, du moins je pense.
En effet, mais l'overhead mémoire peut ne pas être négligeable. Je n'ai pas regardé l'implementation, mais j'imagine que le tri en place avec
cmppeut se faire enO(1)en mémoire, si on ignore la mémoire prise par la liste initiale. Alors que le tri avec le DSU ajoute forcement un overhead enO(n)pour stocker les résultats de l'appel à key. En fonction du problème cela peut être une limitation, même si j'avoue que dans le cas d'un tableau énorme on passera surement par une autre structure, typenumpy.[^] # Re: Très (trop ?) dense
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 6.
Pour le coté dense, oui je l'admet. Comme je le dis dans un commentaire plus haut c'est partie d'une moment "Whaou" simple mais qui demande du contexte, et puis j'ai toujours eu du mal à être concis ;( Saint Exupéry ne serait pas fier de moi.
Merci pour la remarque "marrant", je prend cela comme un compliment ;)
J'ai essayé de faire un paragraphe la dessus mais finalement je me rend compte qu'il arrive au milieu du texte alors qu'il mériterait d'être au début.
Tu aurais un exemple de la problématique que tu sous entend ? J'ai l'impression que je suis biaisé parce que depuis que je fais du Haskell mon style en C++ et en Python a évolué vers quelque chose qui ressemble à Haskell et forcement je suis convaincu que c'est la bonne solution ;)
J'ai tendance à penser que l'approche fonctionnelle motive la séparation donnée / comportement alors que l'approche objet couple souvent les deux, mais à mon avis ce n'est pas imposé par la technologie, c'est plus un choix de développeur. D'ailleurs la STL C++ a vraiment une approche découplée depuis quelque temps,
std::begin()ou lieu deobj.begin()ou l'ensemble<algorythms>, où le discours dernier sur le fait quef.method()pourrait devenir équivalent àmethod(f)pour unifier les styles d'appel.La seule grosse difference que je trouve vraiment dure entre les deux approches c'est pour le polymorphisme runtime, (i.e: les appels virtuels en nomenclature POO). En fonctionnel, du moins en Haskell, j'ai l'impression que 99% des cas de figures sont remplacés par du curying ou des types sommes.
Par contre je ferais bien un article sur l'organisation de code et le typage. Je trouve que beaucoup de monde dans la communauté POO/mutable confonds généricité et absence de type. Un example que je donne souvent c'est pour la réalisation d'une librairie de vecteur pour un moteur 3D, un développeur c++/python vas faire une classe
Pointavec pleins d'operations : soustraction, addition, produit scalaire. Finalement il sera heureux, sous prétexte de généricité, d'utiliser la même classe pour generer les points, directions, normales, couleurs. De mon coté, j'aurais plus tendance à définir plusieurs classes / types, au moinsPoint,Direction,Normal / DirectionNormalisée,Couleuravec un sous ensemble de méthodes limitées aux besoins du domaine. (Par exemple, pas d'addition entrePoint, pas de produit scalaire entreCouleur…). Mais encore une fois, c'est Haskell qui m'a biaisé l'esprit de cette manière, mais ce n'est pas inapplicable aux C++…[^] # Re: merci
Posté par Guillaum (site web personnel) . En réponse au journal Haskell et le tri. Évalué à 2.
En même temps c'est dense ;) N'hésites pas à discuter les points pas clairs. Je suis parti de ma révélation de hier soir de "Mon dieu, les fonctions sont aussi des Monoids" et en fait je suis arrivé à ce truc immonde ;)
[^] # Re: J'ai voté autre donc je justifie
Posté par Guillaum (site web personnel) . En réponse au sondage C'est l'heure des bonnes résolutions. Pour moi, 2016 sera l'année où. Évalué à 5.
+1
Pour moi cela implique:
# Monad et do
Posté par Guillaum (site web personnel) . En réponse au journal Compilateur et Monad Reader. Évalué à 3.
Quand j'ai découvert Haskell, j'ai cru que c’était le plus beau langage du monde. Ce qui est le cas (a part quelques trucs subtiles).
Puis j'ai compris certains concepts, alors j'ai voulu les appliquer au c++. La première chose que j'ai fais c'est implémenter mon propre Maybe. (qui se nomme std::optional dans c++17 le draft).
Mais en fait, cela n'a AUCUN intérêt sans la do notation malheureusement puisque cela force a chainer des lambdas. Exemple :
maybe<float> doSomething(...)
{
return doSomethingInt(...).bind([](int ret){
return doSomethingFloat(ret);
});
}
[^] # Re: Notation do
Posté par Guillaum (site web personnel) . En réponse au journal Compilateur et Monad Reader. Évalué à 6.
Alors, c'est personnel, mais ce n'est pas parce que le pointfree existe et qu'il y a plein d’opérateurs marrant qu'ils faut les utiliser.
Autant je trouve que la version 1 est lisible. Mais (Je me permet de corriger l’opérateur ;) (<=< existe, mais c'est la combinaison de kleishi inverse, enfin bref ;)
Aussi, je crois que Haskell on prefere le camelCase ;)push_var_on_stack = pushq =<< get_var_addr
# Haskell
Posté par Guillaum (site web personnel) . En réponse au sondage Quel langage utilisez-vous le plus au quotidien ?. Évalué à 5.
Parce qu'en ce moment je prototype beaucoup et que ce langage me permet d'être super efficace, faire moins d'erreur et avec des performances correctes.
Après je reecrirais en C++ parce que c'est ce que nous utilisons dans notre compagnie.
[^] # Re: Attention, risque sympatique.
Posté par Guillaum (site web personnel) . En réponse au journal Gérer ses fichiers de config avec git. Évalué à 3.
Ba oui… Globalement c'est surtout les fichiers ignoré que je supprime avec git clean. Les autres j'ai tendance à supprimer à la main car c'est soit des fichier à ajouter, soit des fichiers qui doivent sans doute avoir une raison d'être là ou d'être bougé ailleurs.
# Attention, risque sympatique.
Posté par Guillaum (site web personnel) . En réponse au journal Gérer ses fichiers de config avec git. Évalué à 5.
Attention au git clean dans le mauvais répertoire, cela fait désordre…
Histoire vécue. Je fais souvent des git clean (enfin moi c'est du hg purge, mais c'est pareil) pour nettoyer un dépôt de tous les trucs inutiles qui sont arrivés et pan… plus de home…
Personnellement j'ai une solution avec un sous répertoire de mon home pour les fichiers de config versionnés et je crée automatiquement les liens symboliques qui vont bien.
[^] # Re: Mercurial
Posté par Guillaum (site web personnel) . En réponse au journal Git a fêté ses 10 ans hier .... Évalué à 4.
Je m'auto répond. La seule difference que je vois (et qui est vraiment un problème) c'est qu'au lieu de "cacher" les commits qui disparaissent du fait d'une réecriture (comme le fait git), ils sont supprimés et stockés dans un fichier de backup. Il est vrai que c'est une limitation importante, mais je n'y pensais plus tellement evolve marche bien depuis quelques années.
[^] # Re: Mercurial
Posté par Guillaum (site web personnel) . En réponse au journal Git a fêté ses 10 ans hier .... Évalué à 4.
C'est à mon sens un problème de communication de la part de l'équipe de Mercurial, c'est qu'ils ont tendance à être très conservateur et le système d'extension donne cette idée que seulement un petit sous ensemble des fonctionnalités sont vraiment correctes et que le reste c'est juste des truc un peu bancales qui marchent plus ou moins. J'ai l'impression qui Git fait un peu l'inverse, c'est à dire proposer un truc en standard, alors que cela marche plus ou moins et corriger dans les version suivantes.
De ma dernière discussion avec un développeur d'evolve (je ne peux pas prouver cette affirmation cependant), je crois avoir compris que la plupart des composantes d'evolve sont déjà dans le core de mercurial et c'est maintenant plus des problèmes cosmétiques qu'autre chose pour en faire une extension officielle.
En pratique, j'utilise evolve depuis quelques temps et aucun problème et je connais quelques développeurs qui bossent sur des "petites" base de code de "petites" entreprises "familiales" (genre facebook…) qui l'utilisent quotidiennement sans soucis majeurs.
Si on enlève evolve, Mercurial est au même niveau que Git sur la facilité de réécrire l'historique.
[^] # Re: Mercurial
Posté par Guillaum (site web personnel) . En réponse au journal Git a fêté ses 10 ans hier .... Évalué à 5.
Hormis github, qui est un argument tout à fait valable, je ne vais aucun avantage à git comparé à mercurial.
Et ? C'est pareil avec Mercurial. Je trouve même que c'est bien plus simple qu'avec Git. Avec mercurial, tu update (checkout) le commit qui t’intéresse, tu commit --amend, puis en utilisant evolve, tous les autres commit sont rebasés par dessus.
http://mercurial.selenic.com/wiki/ChangesetEvolution
Tu peux toujours faire du reordonancement et de la suppression avec histedit (qui est l'équivalent à quelques détails de rebase -i).
La dessus, tu as le système de phase qui t’évite de rebaser des commits publique, donc qui t'evites de te retrouver par erreur après quelques heures de travail avec un dépôt dans un état qui va te prendre quelques heures à rétablir.
De plus, mercurial (avec evolve) garde l'historique des manipulation d'historique. Ainsi tu peux voir quel est le commit à l'origine d'un amend, ou d'un rebase. Git ne sait pas faire cela et tu dois chercher dans le reflog à la main, ce qui peut être laborieux.
Hormis quelques détails, comme l'absence d'auto !fixup ou !squash dans Mercurial et que l'ajout interactif d'éléments dans un commit (i.e, l’équivalent de git add -p) est un peu moins élaboré (il existe crecord, mais je n'ai jamais accroché), je suis très souvent frustré par les limitations et le manque de flexibilité de git comparé à mercurial.
Pour finir, Facebook a dernièrement annoncé qu'il passaient de git à mercurial pour des raison de flexibilité et de performance https://www.youtube.com/watch?v=X0VH78ye4yY et google annonce aussi collaborer avec facebook pour l'évolution de Mercurial.
# Utilité du bépo sur tactile.
Posté par Guillaum (site web personnel) . En réponse au journal Appels à testeurs: bépo sur Android 1.0. Évalué à 3.
Je sais que ce n'est pas le propos, mais discutons en quand même.
Personnellement j'ai deux uses case de mon clavier de téléphone, soit en mode une main avec l'index soit en mode deux mains avec les deux pouces (le plus rare).
Dans les deux cas, je trouve qu'une disposition type bépo (ou dvorak que j'utilise tous le temps sur des claviers traditionnels) n'est pas adaptée, mais la disposition "azerty" qui m'est proposé par défaut est tout aussi mauvaise.
Connaissez vous des claviers plus adaptés (qui minimise l'effort à faire avec le pouce) ? Y a il des études faites sur ce sujet ?
Merci et bonne journée.
[^] # bibliographie
Posté par Guillaum (site web personnel) . En réponse au journal Word vs TeX. Évalué à 4.
Pour avoir formaté la thèse de médecine et les publications de ma femme en utilisant zotero sous word il s'agit d'un très mauvais souvenir. Perte de travail, citations incohérentes ou manquantes, mise à jour qui supprime du texte du document… Seul point positif j'ai trouvé la documentation pour customiser les styles de citation très bonne contrairement a bibtex/biblatex.
[^] # Re: 30 min
Posté par Guillaum (site web personnel) . En réponse au journal Word vs TeX. Évalué à 10.
Très clairement, pour les documents à faire en moins de trente minutes, j'utilise libre office.
Par contre, pour ma thèse qui contenant plusieurs centaines de figures générées (tableaux, courbes, graphiques, images issues de simulations), je n'ose même pas imaginer comment j'aurais fais cela avec Word / LOW.
Loin du troll, c'est une vraie question, ce type de production est elle possible en Word ?
[^] # Re: Pas compris
Posté par Guillaum (site web personnel) . En réponse au journal Word vs TeX. Évalué à 10.
Je pense qu'il parlaient de Word, le traitement de texte. Cependant je ne vois pas le rapport avec la matière première utilisée dans la conception de préservatifs…
[^] # Re: prix en Europe et aux USA
Posté par Guillaum (site web personnel) . En réponse au journal Et comme prévu, ça a fait... pffffuit. Évalué à 3.
marrant tient, cela veut dire que 32 Go et 41 Go c'est le même prix ;)
J'aurais tendance à dire que c'est plutôt :
8 * 0.7 + 8 * 0.5 + 16 * 0.3095 + 32 * 0.2360 == 22.104
[^] # Re: En entreprises…
Posté par Guillaum (site web personnel) . En réponse au journal C++14. Évalué à 5.
Changer cela veut dire s'exposer au changement. Cela veut dire revalider les logiciel, cela veut dire s'assurer que ce qui fonctionnait avant fonctionne encore. Par exemple, quand tu compiles avec un vieux gcc, tu as des chances que la libc++ utilisée soit disponible sur la plupart des distributions, donc cela simplifie ton packaging. Quand tu as tout un environnement de build automatique, mettre a jour une partie de ta toolchain te force a de nombreuses mises à jour, potentiellement de nombreuses dépendances pour lesquelles tu doit maintenir ton code. En gros, c'est beaucoup de temps (et donc de cout) pour un gain qui n'est pas forcement important (hormis en qualité, mais la qualité a un cout, et n'est pas forcement la priorité numéro 1).
Autre chose, malheureuse, mais quand tu dois maintenir un logiciel sur de nombreuses plateforme, tu as tendance à niveler vers le bas.
[^] # Re: En entreprises…
Posté par Guillaum (site web personnel) . En réponse au journal C++14. Évalué à 2.
pareil ;) Visual 2005, qui doit péniblement faire du C++98 et gcc 4.3…