Journal Explorer des langages de programmation - édition 2020

Posté par  (site Web personnel) . Licence CC By‑SA.
Étiquettes :
43
26
avr.
2020

Cher nal,

j'avais publié il y a quelques années un journal sur différents langages de programmation peu connus. La motivation pour creuser ces langages venait, à l'époque, du ras le bol du JS. Aujourd'hui, avec le confinement, j'ai de nouveau envie d'apprendre et découvrir des langages de programmation, disons voir, exotiques.

Mais, tout d'abord, faisons un rapide tour des langages cités à l'époque et de ce qu'ils sont devenus.

  • Crystal : le langage se porte bien, même s'il n'a pas encore atteint la version 1.0 et qu'il reste encore assez confidentiel. C'est marrant de voir les 3 axes majeurs annoncés pour Ruby 3 : performance, concurrence et typage statique. Crystal coche les trois (pour la partie concurrence, ce n'est pas encore tout à fait sec, mais c'est déjà bien avancé). Honnêtement, j'ai du mal à comprendre que pas plus de rubyistes ne s'intéressent à Crystal. J'ai vraiment l'impression que Matz s'est un peu perdu ses dernières années et n'arrivent plus à proposer des évolutions intéressantes au langage, et à l'inverse, Crystal progresse bien avec des moyens pourtant bien plus limités.

  • Pony : le langage se veut très ambitieux et il paraît que ça continue d'avancer. Vu de loin, j'ai quand même l'impression que ça coince un peu et que son quart d'heure de gloire est passé.

  • Oden : RIP, le développement a été arrêté et le site web du langage ne renvoie plus qu'une 404.

  • Elm : j'ai un avis très partagé. D'un côté, le compilateur a fait de très gros progrès et ça me semble être une alternative viable au JS pour le code dans les navigateurs. De l'autre, la core team a l'air de ne pas réussir à grandir, et il y a régulièrement des polémiques, des départs, et des incompréhensions. Au final, j'ai l'impression que ces ratés feront que le langage ne pourra jamais passer à la vitesse supérieure.

  • Elixir : le langage conserve son attrait (notamment grâce au framework Phoenix), c'est stable et ça tourne.

Et, voici maintenant les langages que j'ai commencé à regarder et à me documenter un peu dessus. Le but est d'en choisir un pour l'apprendre plus en profondeur et pourquoi pas y contribuer un peu.

  • Idris2 est une sorte d'Haskell en plus poussé. La version 2 s'appuie sur la « Quantitative Type Theory ». Ça permet d'écrire des mettre des nombres dans les types, comme :
append : Vect n a -> Vect m a -> Vect (n + m) a
append xs ys
    = case xs of
           [] => ys
           (x :: xs) => x :: append xs ys

Je n'ai pas vraiment réussi à comprendre ce que ça apportait en pratique pour les développeurs. Et je n'ai pas été spécialement été attiré par ce que j'ai vu. C'est probablement très intéressant, mais je ne dois pas être la bonne personne pour ça.

  • Prolog et plus particulièrement Scryer-Prolog : Prolog est un ancien langage (grosso modo, il est sorti en même temps que le C) mais il s'appuie sur un paradigme peu répandu, la programmation logique. Je suis assez curieux de voir ce que ça peut donner quand on creuse un peu plus que les tutoriels de base (ou le cours de 2h que j'avais pu avoir dessus quand j'étais en école d'ingénieur). D'ailleurs, ça a l'air de revenir un petit peu à la mode, j'en ai même vu dans yarn 2.

  • Zig est une sorte de C moderne. Contrairement à Go, il peut servir à faire des bibliothèques pouvant être utilisés dans d'autres langages de programmation. Et contrairement à C++/Rust/D, il a gardé la simplicité du C. Il y a notamment une phrase de la documentation qui m'a marqué : « Zig competes with C instead of depending on it ». Dans les fonctionnalités que j'ai trouvé intéressantes, on retrouve :

    • la possibilité d'utiliser des bibliothèques en C sans avoir à écrire de bindings/FFI (on importe directement le fichier .h)
    • la séparation très claire entre ce qui est exécuté à la compilation et ce qui est exécuté au runtime, tout en ayant la possibilité d'utiliser le même langage pour les deux (j'imagine assez bien pouvoir initialiser des valeurs complexes à la compilation ou inclure un dictionnaire de compression)
    • les structures de données génériques sont gérées comme des fonctions qui prennent un type et renvoie la structure pour ce type.
    • et des fonctionnalités que l'on peut retrouver dans d'autres langages modernes : defer, async-await, une gestion des erreurs proche de ce que l'on peut trouver en Rust, etc.
  • Gleam : c'est un langage très jeune, qui propose du typage statique pour la machine virtuelle d'Erlang (BEAM). Je suis fasciné par OTP, mais je n'ai jamais réussi à aller très loin. Même en faisant fi de la syntaxe bizarre, Erlang (et Elixir) ne propose que du typage dynamique, et j'ai beaucoup de mal à m'intéresser à quelque chose qui vise à rendre des programmes plus résistants aux erreurs et qui, dans le même temps, augmente le risque d'erreurs à cause du typage dynamique. Gleam propose un langage simple, avec une syntaxe propre (ou, au moins, qui est plus proche de ce que j'ai l'habitude). Le projet a beau être très jeune, j'ai un déclic : il y a quelque chose qui m'accroche dans ce langage et me donne envie de l'approfondir.

Et vous, quels langages de programmation avez-vous creusés ces derniers temps ? Qu'ont-ils comme particularités ?

  • # Mon favoRi !

    Posté par  . Évalué à 4 (+3/-0). Dernière modification le 26/04/20 à 22:23.

    Salut,

    Merci pour ce partage de petite collec' :)

    Moi, mon langage préféré quand j'ai du temps, ça reste quand même R.

    J'aime bien parce que c'est du "vectoriel" et qu'il pousse à penser l'informatique différemment.

    Comme j'avais un peu de temps, j'en ai profité pour dérouiller un peu la mécanique et faire un bilan comptable.

    Nombre de boucles for ? Résultat : 0.

    Tout est vectorisé et c'est optimisé sous le capot.

    Alors c'est sûr, il faut utiliser les "bonnes fonctions", au "bon endroit". Donc la marche est assez haute au début, vu que c'est un langage conçu pour faire de la statistique.

    Mais je le trouve chouette, la communauté est assez ouverte, bien qu'ayant des règles assez strictes : S'il s'agit du langage c'est super ok il y a des réponses du tonnerre. Si c'est un problème de stat par contre, il y a beaucoup de chances de se manger un Va embaucher un statistcien et reviens après. :)

    • [^] # Re: Mon favoRi !

      Posté par  . Évalué à 1 (+0/-0).

      pandas c'est bien aussi pour faire ce genre de chose et c'est du python. J'aime vraiment bien le python, autant pour faire du traitement de données (scikitlearn, scipy, numpy) que pour passer tout ça à l'échelle avec dask, faire du web avec django, manipuler mon k8s, monitorer tout un tas de truc (il y a toujours une lib en python pour ceph, pour k8s pour du http avec requests) et même faire du javascript (j'ai pas encore essayé). J'ai été un mongeur pendant 10ans je suis un pythoniste depuis 5ans et je regrette pas.

      • [^] # Re: Mon favoRi !

        Posté par  . Évalué à 1 (+0/-0).

        Salut,

        pandas c'est bien aussi pour faire ce genre de chose et c'est du python.

        Tout à fait exact, j'en ai entendu parler, en bien, sans m'y mettre, les journées n'ayant que 24h :(

        J'ai été un mongeur pendant 10ans

        Alors tu connais l'adage : There is more than one way to do it. ;)

        Comme je n'ai pas réellement fait de pandas, je ne peux pas comparer. Je serais complètement hors des clous.

        Je suis allé voir une ou deux pages de la documentation en mode "au pif", et je vois des instructions for typiques de la programmation impérative. C'est ce qui m'amuse avec le vectoriel, c'est se débarrasser du for, si possible, et ce n'est pas toujours possible.

        Non pas que je sois contre le for, même pas contre le goto en fait, quand c'est nécessaire. C'est juste qu'essayer de penser différemment, ça fait travailler les méninges.

        Sincèrement, mon message était simplement destiné aux personnes qui souhaitent "déconstruire" leur paradigme de programmation, pour ensuite le "retrouver", éventuellement meilleur. Certes, pour tout déconstruire, on peut aller dans du plus barbare, comme l'APL qui est lui aussi un langage vectoriel (que je n'ai jamais pratiqué, juste vu un collègue en faire). Je crois que la marche est encore plus haute :)

        Et en R aussi on peut faire du… "web", la lib la plus avancée que je connaisse étant Shiny.

        • [^] # Re: Mon favoRi !

          Posté par  . Évalué à 1 (+0/-0).

          justement, pas de for en panda. Enfin à terme :)

          • [^] # Re: Mon favoRi !

            Posté par  . Évalué à 1 (+0/-0).

            Salut,

            Ah, chouette !

            C'est sympa de ne pas faire de for (même si on sait bien qu'il est en dessous dans la pile d'appel, hein…).

            Ça compacte le code, le rend (parfois) plus lisible.

      • [^] # Re: Mon favoRi !

        Posté par  . Évalué à 5 (+3/-0).

        Il existe Julia aussi :)

        • [^] # Re: Mon favoRi !

          Posté par  . Évalué à 1 (+0/-0).

          Salut,

          Il existe Julia aussi :)

          Oui !

          Mais idem que pour pandas, les journées n'ayant que 24h… je n'ai pas creusé de ce côté.

        • [^] # Re: Mon favoRi !

          Posté par  . Évalué à 2 (+1/-0).

          Je trouve R trop orienté statistique après peut-être est-il plus polyvalent que ce qu'il dis. Mais pour les langages du futur sont:
          1) Julia pour remplacer Python (Simple, interprété mais plus rapide et avec des possibilité pour s'attaquer à R)

          2) Rust pour remplacer C/C++ dans l'embarqué, l'OS… ce qui a besoin d'optimisation. Il apporte la sécurité et productivité manquante a C/C++. Son seul défaut est que l'apprentissage n'est pas très simple mais pour des pro, cela se conçoit.

          3) Go si on souhaite pour remplacer Java. J'aime pas Java (la guerre des brevets Google-Oracle y est pour quelque chose mais aussi la gestion mémoire et des dépendances) et je ne suis guère fan de Go. Pour autant je pense que pour l'application professionnel rapide et simple a développer/déployer avec des perf correct, Go représente un bon compromis. Ma crainte c'est que Google abandonne Go comme il l'a fais pour bien des projets…

  • # Frundis

    Posté par  . Évalué à 4 (+3/-0). Dernière modification le 26/04/20 à 23:57.

    Ton message me fait penser… mais où en est Frundis de l’ami anaseto ?
    On est pas dans le dur mais c’est intéressant… surtout pour moi qui ne suis ni dev, ni dans le milieu informatique ;-)

    • [^] # Re: Frundis

      Posté par  (site Web personnel) . Évalué à 4 (+2/-0).

      Eh bien Frundis est toujours là ! :-) Plutôt stable avec de petites améliorations de temps en temps. Dernièrement, par exemple, remplacement automatique des apostrophes dactylographiques par les typographiques, ou encore amélioration de l'affichage des poèmes dans la version EPUB (en particulier pour les vers trop longs) et quelques autres petits trucs.

  • # Typage statique et Erlang

    Posté par  . Évalué à 3 (+1/-0).

    Il existe le projet purerl qui propose de compiler du PureScript vers Erlang à la place de JavaScript.

  • # skiplang

    Posté par  . Évalué à 4 (+1/-0).

    J'ai découvert skip il n'y a pas si longtemps, c'est fait par l'équipe derrière React. J'avoue avoir été beaucoup impressionné par React (les hook !), alors que j'avais très peur de JS.

    Le principe de skip est d'utiliser de la memoïzation directement dans le langage, pour mémoriser n'importe quel étape d'un calcul pour refaire uniquement ce qui change en cas d'évènement. Par exemple, le compilateur tourne comme un service et recompile très rapidement à chaque changement de fichier.

    http://www.skiplang.com/

    "La première sécurité est la liberté"

  • # Go est lent, Rust est rouillé !

    Posté par  (site Web personnel) . Évalué à 8 (+5/-0).

    Et vous, quels langages de programmation avez-vous creusés ces derniers temps ? Qu'ont-ils comme particularités ?

    J'avais testé Go et Rust il y a un moment, Crystal plus récemment.

    L'outillage de Go est très bien (facile de générer un binaire sans dépendance) et très simple (go build), mais le langage primitif (pas de généricité) et incohérent (gestion des erreurs systématiques pour ne jamais planter, mais il y a les "panic" qui font tout planter).

    L'outillage de Rust a l'air bien aussi, mais pas assez mature (installation laborieuse). Le langage semble puissant, mais pénible à écrire et surtout à lire. Ça ne donne pas du tout envie de travailler avec.

    Crystal semble un bon compromis: outillage simple et syntaxe agréable. Malheureusement, c'est très très très jeune. Vivement une version 1.0.

    Incubez l'excellence sur https://linuxfr.org/board/

    • [^] # Re: Go est lent, Rust est rouillé !

      Posté par  (site Web personnel) . Évalué à 9 (+6/-0).

      Tu parles d'outillage, et c'est vrai que c'est un élément de plus en plus important pour qu'un langage de programmation réussisse.

      Pour Go, j'en fais pas mal. J'apprécie l'outillage et la simplicité du langage. Mais je suis de plus en plus frustré par certaines limitations, et surtout le fait qu'il n'y ait toujours pas de solution en vue. Tu as parlé de généricité, c'est quelque chose dont je comprenais bien l'absence il y a quelques années pour laisser le temps à la core team d'y réfléchir et de trouver une solution qui colle au langage, mais ces dernières années, le langage n'évolue quasiment plus et on n'a toujours rien pour écrire des fonctions map ou filter, c'est très frustrant.

      Pour la gestion des erreurs, les panic me dérangent assez peu. Par contre, je trouve très compliqué de bien gérer les erreurs remontées : on en sait jamais trop la liste des erreurs possible qu'une fonction peut remonter. Il y a des techniques pour enrober des erreurs et regrouper des erreurs de plusieurs traitements, ainsi que des paquets comme github.com/hashicorp/go-multierror, golang.org/pkg/errors et github.com/pkg/errors. Mais, dans la pratique, quand on utilise des bibliothèques externes, on se rend vite compte qu'il y a différentes stratégies pour exposer les erreurs et que les prendre correctement toutes en compte dans son application est un casse-tête.

      Dans les autres points, il y a les pointeurs nils (plutôt que des optionals), l'absence de types somme, le manque d'un équivalent d'OTP pour ne pas avoir à constamment réinventer la roue avec les goroutines et channels (surtout que je vois régulièrement des gens utiliser incorrectement ces primitives lorsque je relis du code, signe que c'est loin d'être aussi simple que ce que les concepteurs du langage peuvent dire).

      Pour Rust, j'ai l'impression que c'est un super langage pour faire des gros projets (un navigateur, une suite office, un système d'exploitation), mais c'est vraiment trop complexe et bas niveau pour mes besoins de développeurs d'applications principalement web. J'en vois pourtant passer de plus en plus : rien que dans les langages cités dans le journal, gleam et Scryer-Prolog sont codés en Rust. J'arrive généralement à lire le code, mais ça ne me donne pas envie d'en écrire.

      • [^] # Re: Go est lent, Rust est rouillé !

        Posté par  (site Web personnel) . Évalué à 5 (+3/-0).

        le langage n'évolue quasiment plus et on n'a toujours rien pour écrire des fonctions map ou filter, c'est très frustrant.

        C'est curieux, dans les trucs qu'apporterait la généricité, c'est un point qui revient souvent, alors que perso les fonctions map ou filter c'est vraiment pas le truc qui me manque, car en pratique ça apporte pas grand chose par rapport à une boucle : (à mon avis) à peine plus concis, moins flexible, deux façons d'arriver au même résultat pour un gain en clarité très débatable. Je dis clarité débatable, car un des trucs qui m'a souvent gêné dans le style filter/map/fold etc, c'est que dans ce qu'on trouve dans la nature, ça a tendance à partir dans des fonctions d'itérations lambda inline sur plusieurs lignes avec le nom de la structure qu'on itère perdu à la fin, alors que c'est la première chose qu'on veut voir en général.

        • [^] # Re: Go est lent, Rust est rouillé !

          Posté par  (site Web personnel) . Évalué à 3 (+0/-0).

          Je vais prendre un exemple dans la base de code sur laquelle je travaille :

          func containsReferencedBy(haystack []couchdb.DocReference, needle couchdb.DocReference) bool {
              for _, ref := range haystack {
                  if ref.ID == needle.ID && ref.Type == needle.Type {
                      return true
                  }
              }
              return false
          }
          
          // RemoveReferencedBy removes one or several referenced_by to the file
          func (f *FileDoc) RemoveReferencedBy(ri ...couchdb.DocReference) {
              // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
              referenced := f.ReferencedBy[:0]
              for _, ref := range f.ReferencedBy {
                  if !containsReferencedBy(ri, ref) {
                      referenced = append(referenced, ref)
                  }
              }
              f.ReferencedBy = referenced
          }

          On a une structure FileDoc, dont un des champs contient une liste de ReferencedBy, et on veut retirer de cette liste un ou plusieurs éléments.

          La même chose en Ruby, ça donne :

          class FileDoc
            def removeReferencedBy(ri)
              self.referencedBy.reject! do |ref|
                ri.any? {|r| r.ID == ref.ID && r.Type == ref.Type }
              end
            end
          end
          • [^] # Re: Go est lent, Rust est rouillé !

            Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

            Oui, en Go on n'a pas d'autre choix que de réimplémenter any? pour chaque type de liste qu'on utilise, je suis d'accord, j'ai juste pas l'impression que ça représente en pratique une partie significative ni délicate du code. C'est un manque qu'on constate forcément à un moment ou un autre, mais son impact me semble souvent plus moral qu'autre chose (comparaison inconsciente avec les langages qui ont cette fonctionnalité).

            Il y a des fonctions qui me semblent plus délicates donc problématiques à recoder pour lesquelles la généricité serait plus bienvenue (par exemple des fonctions génériques utilisant les channels).

            • [^] # Re: Go est lent, Rust est rouillé !

              Posté par  (site Web personnel) . Évalué à 4 (+1/-0).

              Je ne sais pas exactement ce qu'il faudrait faire, mais Go oblige à gérer les erreurs pour faire des programmes fiables (bien), mais te plante violemment à la tronche si tu te loupes dans l'accès à un tableau (pas bien).

              En Java, je sais que chaque ligne de code peut lancer une exception (pas bien), donc je me méfie :-)

              Incubez l'excellence sur https://linuxfr.org/board/

              • [^] # Re: Go est lent, Rust est rouillé !

                Posté par  (site Web personnel) . Évalué à 2 (+0/-0).

                Hehe, vu qu'aucun langage non exotique (donc j'exclu idris et compagnie) ne peut prévoir les accès tableaux incorrects dès la compilation, et que la solution C ou assembleur conduit souvent à un segfault (ou pire), il ne reste plus que la solution Perl : renvoyer un undef sans protester :-)

              • [^] # Re: Go est lent, Rust est rouillé !

                Posté par  . Évalué à 2 (+0/-0).

                En Java, je sais que chaque ligne de code peut lancer une exception (pas bien), donc je me méfie :-)

                Tu pourrais développer ? C'est au contraire un des aspects les plus appréciables de Java selon moi 9et Dieu sait que je n'en suis pas fan).
                Une barrière de sécurité supplémentaire qui rend explicite où ton code peut péter.

              • [^] # Re: Go est lent, Rust est rouillé !

                Posté par  (site Web personnel) . Évalué à 5 (+2/-0).

                Après se tromper dans l'accès à un tableau signifie que tu n'as au choix :

                • Pas vérifié la taille du tableau avant l'accès ;
                • Pas utilisé d'itérateur.

                Ce n'est pas bien.

                En plus je suis toujours dubitatif de recevoir une erreur pour ça. Tu en fais quoi, de cette erreur au runtime ? Étant donné que c'est surtout un problème de bonne pratique plus que d'une erreur légitime.

                Or, être capable de détecter cette erreur au runtime a un coût non nul. Ça peut se comprendre qu'ils décident de ne pas le considérer comme une erreur valide.

                Je précise que la plupart des langages modernes agissent ainsi malgré une meilleure préoccupation de la gestion des erreurs qu'à l'époque du C. Je pense à Rust notamment où l'accès direct hors du tableau est possible et génère une belle erreur de segmentation à l'ancienne.

                • [^] # Re: Go est lent, Rust est rouillé !

                  Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

                  Rust notamment où l'accès direct hors du tableau est possible et génère une belle erreur de segmentation à l'ancienne.

                  Je crois que c'est plutôt un panic() (le programme quitte de lui-même) plutôt qu'une erreur de segmentation (l'OS tue éventuellement le programme plus tard, quand il détecte des accès mémoires foireux).

                  On doit quand même pouvoir éviter les vérifications de bornes dans un bloc unsafe (et donc avec cette fois-ci des erreurs de segmentation possibles).

            • [^] # Re: Go est lent, Rust est rouillé !

              Posté par  . Évalué à 4 (+1/-0).

              Je ne connais pas any, mais dans le cas d'un comportement identique entre objet pourquoi ne pas utiliser une interface en Go ?

              "La première sécurité est la liberté"

              • [^] # Re: Go est lent, Rust est rouillé !

                Posté par  (site Web personnel) . Évalué à 4 (+1/-0).

                Si tu veux implémenter any ou filter en go, il y a en gros deux possibilités : passer par interface{} ou utiliser de la génération de code.

                Pour la première approche, tu as des bibliothèques comme https://github.com/chrislusf/gleam, mais tu perds le typage statique et les casts rendent la lecture du code assez moche, je trouve.

                Pour la seconde approche, https://github.com/elliotchance/pie est un exemple. Mais la génération de code a aussi ses mauvais côtés (par exemple, ça allonge le temps de compilation). Et dans le cas de pie, ça oblige à déclarer un type spécifique sur lequel attacher la génération de code, ce qui rend compliqué à utiliser avec des bibliothèques tierces.

                Pour en revenir à la question initiale, on ne peut pas utiliser d'interface en Go pour un comportement identique entre objets car il n'y a pas de généricité, et même si le comportement est le même, les types sont différents et les types apparaissent dans la déclaration de l'interface.

                • [^] # Re: Go est lent, Rust est rouillé !

                  Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

                  tu perds le typage statique et les casts rendent la lecture du code assez moche, je trouve.

                  Une vision plus positive de la même chose serait : si on veut vraiment filter ou any en Go, on peut du moment qu'on accepte que cette partie du code ait les mêmes garanties qu'en Ruby :-) Quand à la mocheté des casts explicites obligatoires, elle a un avantage : on a plus de chances de repérer les erreurs que dans un langage dynamique aux casts implicites.

                  Après, il y a quand même un soucis en vrai : on ne peut pas facilement écrire Any avec la signature func(interface{}, func(interface{}) bool) bool, le mieux qu'on puisse faire c'est func([]interface{}, func(interface{}) bool) bool, ce qui force à transformer tout tableau en []interface{} ce qui n'est pas possible avec un cast : il faut céeer un nouveau tableau ou se limiter à utiliser Any pour les tableaux contenant des interfaces.

                  L'alternative, c'est d'utiliser reflect et d'écrire les fonctions spécialisées (générées automatiquement ou pas) et les utiliser dans la fonction générique, mais du coup, si on a déjà les fonctions spécialisées, le gain à utiliser la fonction générique à base de reflect est très discutable (on perd typage statique et performances pour au final possiblement aucune factorisation de code).

                  • [^] # Re: Go est lent, Rust est rouillé !

                    Posté par  . Évalué à 7 (+5/-0). Dernière modification le 29/04/20 à 11:10.

                    Une vision plus positive de la même chose serait : si on veut vraiment filter ou any en Go, on peut du moment qu'on accepte que cette partie du code ait les mêmes garanties qu'en Ruby :-)

                    Oui mais :

                    1. c'est très frustrant pour le développeur de perdre l'information de type juste à cause du système de type. Une fonction foo(interface{}) interface{} qui renvoie systématiquement le même type que ce qu'elle a reçu, tu es obligé de gérer des cas que tu sais inutiles juste parce que le typage n'est pas propagé
                    2. Go n'est pas Ruby. Un langage globalement typé dynamiquement te pousse à tout le temps vivre avec ce genre de choses. C'est prévu par le langage1 et tu as une homogénéité sur l'ensemble de ton code. Là tu crée une section de ton code qui n'a plus le même typage. C'est un piège plus vicieux.

                    1. entre autre l'obligation de passer par un cast par rapport au fait d'utiliser un langage dynamique te pousse à pousser plus de contraintes sur le type que ce dont tu as besoin. Si tu a une interface qui défini 2 méthodes foo() et bar() tu va avoir tendance à la réutiliser même si tu a besoin que de l'une des 2. Un autre point peut être que les langages dynamiques possèdent aussi beaucoup plus d'informations de type au runtime. Ça donne des erreurs plus compréhensible. 

                    • [^] # Re: Go est lent, Rust est rouillé !

                      Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

                      c'est très frustrant pour le développeur de perdre l'information de type juste à cause du système de type.

                      Information que tu n'as pas statiquement à la base dans un langage à typage dynamique, donc impossible à perdre. Tu n'es pas plus obligé de gérer les “cas inutiles” que dans un langage dynamique : si tu es confiant, tu peux traiter que le cas qui est censé arriver, avec panic sinon (comme dans un langage dynamique). Tu as le choix d'écrire ton code de façon plus défensive, mais c'est pas une obligation (ni même toujours recommandé).

                      Dans les langages dynamiques il y a des casts aussi, ils sont juste implicites et partout, ce qui apporte plus d'ergonomie et de flexibilité (moins de contraintes comme tu dis), mais aussi justement moins d'info quand on se plante, voire des erreurs silencieuses (cast inattendu mais valide). Go a toute l'information qu'il faut sur ses types “dynamiques” au runtime. Quand je dis que Go a une partie dynamique, c'est pas vraiment d'un point de vue du typage : techniquement parlant Go est 100% statique pour ce qui est du typage en soi, car les casts explicites sont typés (tant la source que le retour), de la même façon qu'une fonction StringToInt dans un langage quelconque renvoyant Int ou une erreur peut être typée statiquement. C'est pas comme un cast en C qui doit impérativement être correct sous risque de faire des trucs unsafe : en Go un cast est safe (d'un point de vue typage), encore plus qu'au sens d'un accès tableau en Rust/OCaml/etc, car on a le choix de gérer le cas d'erreur.

                      J'achète pas trop non plus l'argument manichéen qu'il faudrait être 100% statique ou 100% dynamique (au sens large). Aucun langage n'est totalement ni l'un (à part l'assembleur peut-être) ni l'autre (outre Coq et compagnie) : les langages à typage statique ont généralement des tests dynamiques pour les accès tableau (avec panic), pour certaines fonctions de marshalling/sérialisation, parfois via réflection (Java ou Go, donc typé statiquement même si dynamique) ou carrément type unsafe (OCaml), exceptions qui échappent au système de types (OCaml et plein d'autres), etc. ; les langages dynamiques offrent parfois un minimum de typage statique pour différencier certaines choses (une fonction d'une variable, voire dans certains langages comme Perl, un tableau d'un scalaire). À mon avis, avec Go, il est prévu par le langage d'utiliser des checks dynamiques pour les cas nécessitant un système de types plus complexe, au même titre qu'en Ruby il est prévu que tout soit dynamique.

                      Entre dynamique et statique il y a tout un monde : comme mentionné plus haut, même Rust évite un système de types plus complexe qui permettrait d'éviter des checks runtime pour les accès tableau. C'est une question de juste milieu : on sait que les extrêmes sont pas vraiment pratiques pour les usages courants (du genre Coq vs Assembleur), mais par contre il n'y a pas de réponse définitive pour dire où le juste milieu doit se trouver pour un langage généraliste.

                      • [^] # Re: Go est lent, Rust est rouillé !

                        Posté par  . Évalué à 3 (+1/-0).

                        Information que tu n'as pas statiquement à la base dans un langage à typage dynamique, donc impossible à perdre.

                        C'est mon point : il est bien plus frustrant de perdre quelque chose que l'on a que de ne pas l'avoir.

                        Dans les langages dynamiques il y a des casts aussi, ils sont juste implicites et partout, ce qui apporte plus d'ergonomie et de flexibilité (moins de contraintes comme tu dis), mais aussi justement moins d'info quand on se plante, voire des erreurs silencieuses (cast inattendu mais valide).

                        Et bien non. Si je prends ce code là :

                        class LinuxFr
                            def foo()
                                return "linux"
                            end
                            def bar()
                                return "bar"
                            end
                        end
                        class AppleFr
                            def foo()
                                return "MacOS"
                            end
                        end
                        
                        def MyFnc(site)
                            puts site.foo()
                        end
                        
                        MyFnc(LinuxFr.new)
                        MyFnc(AppleFr.new)

                        MyFnc() ne cast pas, elle n'a pas besoin de typage nominal. Il faut juste que l'objet qu'on lui passe en paramètre se comporte comme elle l'attends. En go il va falloir que tu explicite cet attendu dans une interface. Quand c'est simple et limité pas de problème quand ça devient plus complexe ça devient plus pratique de réutiliser l'interface initial mais tu a perdu l'intérêt de ton typage dynamique.

                        J'achète pas trop non plus l'argument manichéen qu'il faudrait être 100% statique ou 100% dynamique

                        Ce n'est pas mon point. Mon point c'est que le champs où dans les langages statiquement typés (globalement), on se place dans un contexte où l'on pète les informations de types (interface{} en go, Object en java, void* en C et C++,…) on se trouve dans un cadre où le langage que tu as choisi perds une partie de ses fonctionnalités et tu entre pas forcément clairement dans un paradigme différent. C'est un cas où on fait bien plus d'erreur car on ne manipule plus le langage de la même façon où on a l'habitude.

                        Il n'y a pas de manichéisme là dedans. Une approche différente, que je suis entrain de faire d'ailleurs c'est les intégrations de langage. Si tu met du groovy dans du java, du lua dans du C++ ou du C++ dans du python, tu aussi un changement de paradigme, mais il est bien plus évident à appréhender.

                        • [^] # Re: Go est lent, Rust est rouillé !

                          Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

                          C'est mon point : il est bien plus frustrant de perdre quelque chose que l'on a que de ne pas l'avoir.

                          C'est un argument qui se défend, plutôt du côté ressenti humain subjectif (frustration) que technique. Je pense que cette source de frustration est en effet assez répandue et a tendance à polariser les langages : le langage dynamique a peur de perdre en flexibilité/pureté en introduisant du statique, le langage statique a peur de perdre en garanties/performances/pureté en introduisant du dynamique.

                          MyFnc() ne cast pas

                          En Go ton exemple ne cast pas, vu que c'est exactement un cas d'application d'interface, comme tu le précises, et que l'appartenance ou non à une interface est résolut statiquement. L'interface doit, effectivement, être écrite manuellement explicitement (ici une interface contenant une seule méthode foo() string) contrairement à Ruby où tout est implicite.

                          Par contre, sauf erreur, en Ruby ton exemple fait un cast que tu n'as pas vu : site.foo() donnerait une erreur runtime si tu passes un objet sans cette méthode. Du moins, ce serait le cas en Perl ou Tcl, je ne sais pas si Ruby fait du typage statique pour valider les appels méthode de classes, a priori non si Ruby résout comme Perl les appels méthode au runtime de façon dynamique, mais je connais pas bien Ruby.

                          • [^] # Re: Go est lent, Rust est rouillé !

                            Posté par  (site Web personnel) . Évalué à 3 (+0/-0).

                            Par contre, sauf erreur, en Ruby ton exemple fait un cast que tu n'as pas vu : site.foo() donnerait une erreur runtime si tu passes un objet sans cette méthode.

                            En Ruby, ça lève bien une exception au runtime si tu passes un objet sans cette méthode. Mais ça ne fait pas pour autant un cast.

                            En Go quand on appelle une méthode sur une interface comme en Ruby quand on appelle une méthode, le runtime va regarder une table de méthodes virtuelles pour trouver le code à appeler (ou plusieurs tables s'il y a des question d'héritages).

                            • [^] # Re: Go est lent, Rust est rouillé !

                              Posté par  (site Web personnel) . Évalué à 2 (+0/-0).

                              Mais ça ne fait pas pour autant un cast.

                              C'est parce que tu penses à l'objet contenu directement (d'ailleurs je parle avant d'objet aussi quand je devrais pas), plutôt que penser variable (le type fourre-tout, à comparer à interface{}). Il n'y a pas de cast explicite, mais dans l'abstrait (peu importe l'implémentation) on peut voir ça comme un cast d'un type implicite qui contiendrait tous les objets (d'un point de vue statique on ne sait en général rien sur le type de l'objet contenu dans une variable, donc interface{}), vers le sous-type des objets qui ont une méthode foo (implicitement défini en Ruby) afin d'appliquer la méthode, avec exception runtime si ce cast échoue (le contenu de la variable, c'est-à-dire l'objet caché derrière interface{}, n'est pas d'un type compatible).

                              En Go quand on appelle une méthode sur une interface comme en Ruby quand on appelle une méthode, le runtime va regarder une table de méthodes virtuelles pour trouver le code à appeler (ou plusieurs tables s'il y a des question d'héritages).

                              Ça c'est l'implémentation de l'appel, oui. Mais l'interprétation en terme de typage nous dit qu'en Go un appel méthode n'a pas besoin de faire de cast (on sait statiquement que le type de la variable est compatible avec cet appel méthode), alors qu'en Ruby on part toujours de l'équivalent interface{}, l'unique type statique de toutes les variables en Ruby, qu'on a besoin de caster vers un type (implicitement défini, pas vraiment dans le langage) à chaque appel.

                              • [^] # Re: Go est lent, Rust est rouillé !

                                Posté par  . Évalué à 1 (+0/-1).

                                Il n'y a pas de cast explicite, mais dans l'abstrait (peu importe l'implémentation) on peut voir ça comme un cast d'un type implicite qui contiendrait tous les objets (d'un point de vue statique on ne sait en général rien sur le type de l'objet contenu dans une variable, donc interface{}), vers le sous-type des objets qui ont une méthode foo (implicitement défini en Ruby) afin d'appliquer la méthode, avec exception runtime si ce cast échoue (le contenu de la variable, c'est-à-dire l'objet caché derrière interface{}, n'est pas d'un type compatible).

                                Tu m'a faits douter, mais non le code équivalent en go de ce que j'ai posté plus haut :

                                package main
                                
                                import (
                                    "fmt"
                                )
                                
                                type LinuxFr struct {
                                }
                                
                                func (c *LinuxFr) foo() string {
                                  return "linux"
                                }
                                
                                func (c *LinuxFr) bar() string {
                                  return "bar"
                                }
                                
                                type AppleFr struct {
                                }
                                
                                func (c *AppleFr) foo() string {
                                  return "ios"
                                }
                                
                                func hello(a interface{}) string {
                                  return a.foo()
                                }
                                
                                func main() {
                                    fmt.Println(hello(LinuxFr{}))
                                    fmt.Println(hello(AppleFr{}))
                                }

                                Ça ne compile pas.

                                ./prog.go:26:11: a.foo undefined (type interface {} is interface with no methods)
                                

                                Go demande à ce que tu indique le type. Tu dois donc définir une interface avec tout ce dont tu as besoin de dans. C'est tout à fait possible et facile dans mon exemple, mais bien plus complexe dans des cas réels. Donc généralement tu va utiliser un type prédéfini qui possède potentiellement des trucs dont tu n'a pas besoin.

                                C'est loin d'être identique dans la logique comme dans le comportement.

                                • [^] # Re: Go est lent, Rust est rouillé !

                                  Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

                                  On s'est mal compris : bien sûr qu'en Go tu dois définir une interface statiquement dans ce cas pour éviter un cast.

                                  Ce que je veux dire, pour résumer, c'est qu'il y a deux cas : soit le système de type de Go permet de gérer le cas (comme dans ton exemple), soit le système de type n'est pas suffisant et il faut faire façon Ruby. Dans le deuxième cas on a un cast (explicite en Go avec gestion d'erreur possible, implicite en Ruby avec exception si échec), dans le premier cas on peut éviter le cast (en Go, jamais en Ruby) avec un peu de code en plus (définir l'interface).

                                  En d'autres termes, Ruby c'est conceptuellement proche d'un équivalent de Go avec comme unique type statique interface{} et plein de sucre syntaxique. Pour regarder à l'intérieur d'une variable Ruby, il y a toujours une tentative de cast implicite pour toute action qui ne soit pas totalement indépendante du type.

                                  Donc généralement tu va utiliser un type prédéfini qui possède potentiellement des trucs dont tu n'a pas besoin.

                                  Je vois pas trop à quoi tu fais référence ici.

                                  • [^] # Re: Go est lent, Rust est rouillé !

                                    Posté par  . Évalué à 2 (+0/-0).

                                    Donc généralement tu va utiliser un type prédéfini qui possède potentiellement des trucs dont tu n'a pas besoin.

                                    Je vois pas trop à quoi tu fais référence ici.

                                    Si tu prends mon exemple et que historiquement tu fais les choses ainsi :

                                    1. Tu crée LinuxFr
                                    2. Tu crée hello()
                                    3. Tu crée AppleFr

                                    Le paramètre que tu aura défini pour hello() sera probablement plutôt LinuxFr que l'interface minimal dont tu as besoin car c'est fastidieux de n'exprimer son typage que par les préconditions minimales aux quelles tu dépend. C'est la force des langages dynamiques, c'est totalement gratuit. Et c'est l'une des forces des langages dynamiques.

                          • [^] # Re: Go est lent, Rust est rouillé !

                            Posté par  . Évalué à 2 (+0/-0).

                            C'est un argument qui se défend, plutôt du côté ressenti humain subjectif (frustration) que technique.

                            J'ai parlé de frustration, mais aussi d'homogénéité par exemple. Ce qui est moins subjectif.

                        • [^] # Re: Go est lent, Rust est rouillé !

                          Posté par  . Évalué à 1 (+1/-1).

                          (tu dis souvent, il me semble, "mon point", est-ce que tu ne pourrais pas traduire cela en français, disons plus français ? "mon propos" par exemple . c'est juste une suggestion, hein ;-))

                          • [^] # Re: Go est lent, Rust est rouillé !

                            Posté par  (site Web personnel) . Évalué à 3 (+0/-0).

                            Qu'est-ce que tu reproches à « mon point » ? Ça me paraît être tout à fait français. https://www.cnrtl.fr/definition/point/1 donne comme définition possible :

                            Partie d'une démonstration intellectuelle, élément d'un ensemble de propositions.

                            • [^] # Re: Go est lent, Rust est rouillé !

                              Posté par  . Évalué à 2 (+0/-0).

                              Merci d'avoir fais la recherche :) Je vais tout de même essayer de varier un peu plus mon vocabulaire.

                            • [^] # Re: Go est lent, Rust est rouillé !

                              Posté par  . Évalué à 4 (+3/-0). Dernière modification le 30/04/20 à 13:53.

                              Il me semble que "mon point" est un anglicisme. Je ne suis pas convaincu par le lien de bruno: qu'on puisse utiliser "point" comme partie ou élément, bien sûr, pas de problème : point 1, point 2, le point essentiel, fondamental etc, le point de truc (point de doctrine, point de vue par exemple), ne me gêne absolument pas, c'est juste "mon point" tout court (ou pire: faire ou gagner son point) qui me semble un anglicisme dont on peut très bien se passer.
                              Ceci dit, je reconnais que c'est un détail, et que ça n'empêche nullement la compréhension, mais "mon point" est : est-ce que c'est du "bon" français ? il ne semble pas, mais je peux me tromper;-)

                              • [^] # Re: Go est lent, Rust est rouillé !

                                Posté par  (site Web personnel) . Évalué à 8 (+6/-0).

                                En français, ça s’utilise aussi pour un argument définitif qui clos une discussion.

                                Exemple d’utilisation : « Et mon point dans ta gueule ? ».

                                • [^] # Re: Go est lent, Rust est rouillé !

                                  Posté par  . Évalué à 3 (+2/-0).

                                  Encore faut-il ne pas se tromper de point : après un point d'interrogation, des points de suspension, ou même un osé point-virgule dans ta gueule, il faut s'attendre à ce que le débat reprenne de plus belle… le mieux est de sortir le définitif point final.

                                  • [^] # Re: Go est lent, Rust est rouillé !

                                    Posté par  . Évalué à 5 (+3/-0). Dernière modification le 30/04/20 à 19:12.

                                    Bof, je voudrais pas etre pointilleux mais le meileur reste quand même les points de suspension : un combo de 3 points. LA tu peux être sûr que le point y est !!!

              • [^] # Re: Go est lent, Rust est rouillé !

                Posté par  (site Web personnel) . Évalué à 4 (+2/-0).

                any? ici est une fonction qui renvoie vrai si un des éléments de la liste/tableau renvoie vrai pour un certain prédicat. Sans généricité, c'est impossible d'écrire un telle fonction qui marche pour des listes de n'importe quel type et les interfaces ne couvrent pas ce genre de cas. Le plus proche de any? qu'on puisse faire, c'est quelque chose comme (pas testé bien sûr) :

                func anyInHayStack(haystack []couchdb.DocReference, pred func (couchdb.DocReference) bool) bool {
                    for _, ref := range haystack {
                        if pred(ref) {
                            return true
                        }
                    }
                    return false
                }

                C'est proche de la fonction de Bruno en un peu plus général. Note en passant, cette généralité a peu de chances d'apporter grand chose si c'est utilisé toujours avec le même prédicat et, en un certain sens, la version Go de Bruno est plus lisible que la version Ruby justement du fait que le prédicat plus technique se retrouve dans une autre fonction plutôt qu'inline dans le corps de l'autre fonction. On pourrait voir ça comme un argument en faveur du fait que les limitations de Go nous encouragent parfois à écrire du code plus accessible.

                Pour en revenir à nos moutons, on n'a pas moyen de rendre la signature plus générale et les interfaces vont pas pouvoir nous aider ici. Tu ne peux même pas définir l'interface dont la méthode serait Any(func (type de l'élément) bool) bool pour le type []couchdb.DocReference, car type de l'élément devrait être paramétré. Même si l'interface était possible à définir (généricité partielle juste pour les types des méthodes des interfaces), la méthode à définir pour chaque type satisfaisant l'interface serait la fonction que justement on voudrait écrire qu'une seule fois à la base.

        • [^] # Re: Go est lent, Rust est rouillé !

          Posté par  (site Web personnel) . Évalué à 2 (+0/-1).

          La généricité apporterait des structures de données faciles à utiliser. En Go on n'a que map et array (avec de jolis panics dès que tu te goures, mais ça c'est une autre erreur de conception).

          Incubez l'excellence sur https://linuxfr.org/board/

          • [^] # Re: Go est lent, Rust est rouillé !

            Posté par  (site Web personnel) . Évalué à 4 (+2/-0).

            Pour les structures de données, je suis d'accord que ça serait parfois sympa et ergonomique et éviterait quelques répétitions. Après, ça a un certain charme de se dire que quand on voit quelque chose qui ressemble à un map ou un array, c'est exactement ça, pas d'abstractions surprise : ce manque de structures a un côté positif, même si pas évident à mesurer.

            Par contre, je vois pas trop ce que tu entends par les panics avec map et array : que je sache, même en Rust/OCaml/etc., si on fait un accès hors du tableau, on a un panic. Le seul truc améliorable qui me vient à l'esprit avec un système de types plus complet (sans compter les vraiment complets avec types dépendants) et qui m'est déjà arrivé, c'est un map non initialisé, mais c'est le genre de truc qu'on détecte et corrige plutôt vite (contrairement à un accès hors tableau).

        • [^] # Re: Go est lent, Rust est rouillé !

          Posté par  . Évalué à 3 (+1/-0).

          les fonctions map ou filter c'est vraiment pas le truc qui me manque, car en pratique ça apporte pas grand chose par rapport à une boucle (à mon avis) à peine plus concis, moins flexible, deux façons d'arriver au même résultat pour un gain en clarité très débatable.

          Dans un programme développé dans un langage fonctionnel, ça ne me gène pas plus que ça. Par contre dans un langage multi-paradigme (python, java), je trouve que ça casse la fluidité de lecture car le cerveau doit switcher de mode de programmation. C'est pour ça que si je dois utiliser ge genre de paradigme aquand je fais du python, par exemple, je m'assure de l'abstraire dans une fonction/méthode spécifique, pour ne pas casser la fluidité de lecture. Sinon pour certains cas, le map ou filter est très pratique. Tput dépend du problème qu'on a à régler et de la façon dont c'est implémenté dans le langage.

      • [^] # Re: Go est lent, Rust est rouillé !

        Posté par  (site Web personnel) . Évalué à 4 (+3/-0).

        En ce qui concerne Go, j'aime pas mal de leurs choix techniques:

        • remonter les erreurs par valeur de retour c'est top et force a bien gérer les erreurs au lieu de laisser une exception remonter le tout et faire n'importe quoi. On peut regretter que ce ne soit pas un type générique comme Result<> en rust qui wrap soit une erreur soit une valeur, mais c'est déjà très bien

        • forcer a réimplémenter toutes les boucles, je trouve ça bien aussi car cela force le programmeur à penser à son algorithme et lui évite de faire appel a plein defonction différentes qui vont itérer sur une collection plusieurs fois alors qu'une seule itération avec plusieurs traitements à l'intérieur serait plus efficace.

        Ce que je regrette c'est le runtime un peu trop lourd, l'absence d'accès au bas niveau (pas d'exec en Go par exemple), le duck typing et le type interface{} un peu trop présent.

      • [^] # Re: Go est lent, Rust est rouillé !

        Posté par  . Évalué à 1 (+0/-0).

        Il y a quelques jours, Rob Pike a annoncé lors d'une interview qu'une solution élégante semble avoir été trouvée pour proposer la généricité dans Go. Cela devrait apparaître dans 1 à 2 ans. Bref, ça arrive, mais pas pour tout de suite.
        Pour les erreurs, personnellement je m'y suis fait assez vite. Les dernières évolutions (en standard et non plus en third party si j'ai bien compris) pour gérer les erreurs semblent intéressantes mais je n'ai pas eu le temps/besoin de mettre en pratique.

      • [^] # Re: Go est lent, Rust est rouillé !

        Posté par  . Évalué à 2 (+1/-0).

        Tu as parlé de généricité, c'est quelque chose dont je comprenais bien l'absence il y a quelques années pour laisser le temps à la core team d'y réfléchir et de trouver une solution qui colle au langage, mais ces dernières années, le langage n'évolue quasiment plus et on n'a toujours rien pour écrire des fonctions map ou filter, c'est très frustrant.

        Ian Lance Taylor doit être à sa 6ème+ implémentation de generics pour le Go.
        C'est juste qu'il n'a pas trouvé encore le couple parfait performance runtime+lisibilité+performance compilation+taille du binaire.
        Sur le dernier podcast GoTime #98, il en parle, et on peut espérer avoir ça cette année ou l'année prochaine je pense.

        Et perso, c'est plus pour des fonctions/building block de concurrence que ça m'intéresse, ou un package math qui ne soit pas à appeler avec 20000 cast que j'y vois de l'intérêt.

        • [^] # Re: Go est lent, Rust est rouillé !

          Posté par  (site Web personnel) . Évalué à 4 (+1/-0).

          Et perso, c'est plus pour des fonctions/building block de concurrence que ça m'intéresse, ou un package math qui ne soit pas à appeler avec 20000 cast que j'y vois de l'intérêt.

          Tout à fait, je parlais des boucles parce que c'est un exemple facile à expliquer, mais il y a d'autres endroits où les generics sont plus intéressants.

          • [^] # Re: Go est lent, Rust est rouillé !

            Posté par  (site Web personnel) . Évalué à 5 (+2/-0).

            Ce qui manque à Go c'est aussi une bibliothèque pour faire des interfaces graphiques. Aujourd'hui il n'y a que des bindings, ce qui oblige à avoir une chaîne de compilation C et toute la complexitude que ça apporte (release de binaire 100% statique impossible, installation compliquai, dll hell).

            Incubez l'excellence sur https://linuxfr.org/board/

            • [^] # Re: Go est lent, Rust est rouillé !

              Posté par  . Évalué à 2 (+1/-0). Dernière modification le 06/06/20 à 23:34.

              Il y a un des maintainers de gomobile qui a lancé un projet il y a quelques mois pour amener les GUI native avec Go.
              Comme il utilise l'accélération matérielle, cela passe par OpenGL et donc, link avec C (sauf sur windows, ou la magie des DLL fait qu'en vérité ça ne link à rien). Il a en tête un rendu purement CPU, ce qui permettrait de s'abstenir de CGO.
              Mais ça fonctionne en webassembly, et il a même fait un unikernel avec une appli Go graphique pure lancée directement depuis qemu.

              https://gioui.org/
              le premier talk en parlant : https://www.youtube.com/watch?v=9D6eWP4peYM

              Et ça commence a être pris sérieusement pour de la prod, car il est pris en freelance par tailscale VPN pour faire l'appli mobile (en beta pour le moment)

  • # A propos d'Erlang

    Posté par  . Évalué à 5 (+3/-0).

    OTP, mais je n'ai jamais réussi à aller très loin. Même en faisant fi de la syntaxe bizarre, Erlang (et Elixir) ne propose que du typage dynamique, et j'ai beaucoup de mal à m'intéresser à quelque chose qui vise à rendre des programmes plus résistants aux erreurs et qui, dans le même temps, augmente le risque d'erreurs à cause du typage dynamique.

    De tous les langages que j'ai étudié jusqu'aujourd'hui, Erlang reste, avec Ruby, dans le top des langages que je préfère (Rust est en train de les rejoindre). Ce n'est pas le langage en lui-même (qui est inspiré de Prolog que tu cites plus haut), mais c'est tout le contexte (erlang/OTP). Bienb qu'étant fan de ruby, je n'aime pas Elixir qui casse un certain nombre de sécurités qu'Erlang utilise (variables non mutables par exemple) : je trouve que le mix ruby/erlang est pas du tout réussi.

    L'absence de typage statique s'excplique tout simplement par le fait qu'Erlang permet un redémarrage à chaud des applications. Cette contrainte était une des exigences de base du langage et de son environnement (il s'agissait de pouvoir mettre à jour des équipements télécom sans avoir à les arrêter). Des recherches ont été faites sur ce sujet, des articles (assez intéressants d'ailleurs) sur le sujet ont été écrits. La conclusion du créateur du langage a été que ce n'est pas possible d'implémenter simplement un langage typé qui permettrait un démarrage à chaud. Je t'invite à faire quelques recherches dessus : j'avais lu quelques papiers à l'époquie sur le sujet. Les explications étaient très bas niveau et assez détaillées. Je n'ai pas tout compris parce que je ne maitrisais pas à cette époque toutes les subtilités du langage (c'était au début de mon appentissage), mais le document était suffisamment clair pour que je puisse comprendre dans les grandes lignes la raison pour laquelle il n'y a pas de typage statique dans ce langage ( mais trop complexer pour mon niveau pour que je puisse te l'expliquer aujourd'hui). J'essaierai de vois si j'ai pas encore ça quelque part, ou si je trouve un lien qui pourrait expliquer ça.

    Maintenant, vu que cette fonctionnalité est loin d'être utilisée partout et par tout le monde aujourd'hui, peut-être serait-il possible d'introduire le typage statique avec une option à la compilation à activer lorsqu'une appli n'utilise pas le redémarrage à chaud au niveau d'Erlang, mais des méthodes autres pour ne pas couper le service lorsqu'elles démarrent ? (je pense notamment aux applis cloud).

    • [^] # Re: A propos d'Erlang

      Posté par  (site Web personnel) . Évalué à 6 (+3/-0).

      Tout à fait. Le typage dynamique d'Erlang n'était pas une volonté des créateurs du langage, mais juste le fait qu'un certain nombre de personnes se sont cassées les dents sur ce problème. Il y a deux aspects compliqués : le redémarrage à chaud et les communications inter-process. Pour le redémarrage à chaud, cela semble encore inatteignable aujourd'hui et le choix fait par Gleam est de ne pas prendre en charge le redémarrage à chaud. Pour les communications inter-process, j'ai cru comprendre qu'il n'y avait pas de solution toute prête mais que ça paraissait jouable de s'y attaquer. Pour Gleam, il y a quelques expérimentations autour de otp_process, otp_agent et otp_supervisor. Je suis curieux de voir ce que ça va donner.

      • [^] # Re: A propos d'Erlang

        Posté par  . Évalué à 2 (+0/-0).

        Il y a deux aspects compliqués : le redémarrage à chaud et les communications inter-process.

        Je n'ai rien lu à ce sujet (je chercherais plus tard), mais je suis surpris que le typage structural ne puisse pas fournir de solution. Je présume que le problème vient du fait qu'ils ne sont pas en mesure que le type d'une référence pointe vers un type toujours valide, mais si c'était vraiment ça le problème remplacer un typage nominal par un typage structural devrait être possible.

        Du coup ça me rend curieux :)

        • [^] # Re: A propos d'Erlang

          Posté par  (site Web personnel) . Évalué à 4 (+1/-0).

          Learn you some Erlang a un petit passage sur le sujet :

          Through the years, there were some attempts to build type systems on top of Erlang. One such attempt happened back in 1997, conducted by Simon Marlow, one of the lead developers of the Glasgow Haskell Compiler, and Philip Wadler, who worked on Haskell's design and has contributed to the theory behind monads (Read the paper on said type system). Joe Armstrong later commented on the paper:

          One day Phil phoned me up and announced that a) Erlang needed a type system, b) he had written a small prototype of a type system and c) he had a one year’s sabbatical and was going to write a type system for Erlang and “were we interested?” Answer —“Yes.”

          Phil Wadler and Simon Marlow worked on a type system for over a year and the results were published in [20]. The results of the project were somewhat disappointing. To start with, only a subset of the language was type-checkable, the major omission being the lack of process types and of type checking inter-process messages.

      • [^] # Re: A propos d'Erlang

        Posté par  . Évalué à 2 (+0/-0).

        En dehors du typage, ce que j'aime bien avec Erlang, c'est que quelque part, il a su faire du micro service avant tout le monde (c'est pas de l'API REST, mais ça reste quand même du microservice).

    • [^] # Re: A propos d'Erlang

      Posté par  . Évalué à 2 (+0/-0).

      Au passage, désolé pour les fautes de frappe. Je ne sais pas ce qui m'arrive en ce moment mais j'en fais beaucoup plus que d'habitude, et cette fois-ci, j'ai malhereusement passé les 5 mn qui me sont accordées pour pouvoir les corriger (je fais trop de choses en même temps …). Je vais m'efforcer de m'améliorer sur ce point en me relisant un peu plus, promis.

  • # Prolog

    Posté par  . Évalué à 2 (+1/-0).

    Suis content de voir que prolog existe toujours, je l'avais utilisé il y a très longtemps, et j'aimais bien… mais ça fait longtemps que je n'en avais plus entendu parler

    • [^] # Re: Prolog

      Posté par  . Évalué à 4 (+3/-0). Dernière modification le 27/04/20 à 12:17.

      Salut,

      Il a fallu que je farfouille dans de vieux dossiers, mais si tu aime le prolog, en voilà un petit bout :

      %%%%%%%%%%%%%%%%%%%%%%%%
      % Reines.pl
      %
      % Comment placer N reines sur un echiquier NxN
      
      
      % necessaire pour between/3
      
      :-lib(util).
      
      caseLibre(_,[],_).
      caseLibre(R,[T|L],N):- pasPrise(R,T,N),
                             M is N+1, caseLibre(R,L,M).
      
      pasPrise(R,T,N):- R =\= T,
                        S is T + N, S =\= R,
                        U is T - N, U =\= R.
      
      solution([],_).
      solution([T|L],N):- solution(L,N),
                          between(1,N,T),
                          caseLibre(T,L,1).
      
      solGraph(L,N):- length(L,N), genSol(L,N) .
      
      genSol(L,N):-   solution(L,N), !,
                      afficheReine(L,N).
      
      afficheReine([],_):-nl.
      afficheReine([T|L],N):- M is N, printLine(T,M),
                              afficheReine(L,N).
      
      printLine(1,N):- write('R '), M is N-1, printLine(0,M).
      printLine(0,N):- N < 1, nl.
      printLine(0,N):- write('x '), M is N-1, printLine(0,M).
      printLine(T,N):- write('x '), U is T-1, M is N-1, printLine(U,M).

      C'est bien vieux, et probablement pas du tout optimal. C'est pour résoudre le Problème des huit dames

      Je ne sais même pas si ça passe avec du prolog moderne.

  • # Nombre dans les types

    Posté par  (site Web personnel) . Évalué à 8 (+6/-0).

    Idris2 est une sorte d'Haskell en plus poussé. La version 2 s'appuie sur la « Quantitative Type Theory ». Ça permet d'écrire des mettre des nombres dans les types, comme …
    Je n'ai pas vraiment réussi à comprendre ce que ça apportait en pratique pour les développeurs. Et je n'ai pas été spécialement été attiré par ce que j'ai vu. C'est probablement très intéressant, mais je ne dois pas être la bonne personne pour ça.

    "Ecrire des nombres dans les types" ce n'est pas nouveau, cela se fait depuis des années dans de nombreux langages. C++ par exemple te permet d'avoir des nombres au niveau du type. Haskell fait cela très bien aussi. Idris apporte cependant plus de confort sur des cas plus avancés, que je ne détaillerais pas ici.

    Ecrire des nombres dans des types peut permettre de rendre des interfaces plus robustes avec des vérifications à la compilation. L'exemple canon c'est celui des vecteurs que tu as donné, qui permet par exemple d’empêcher (à la compilation) les accès à des cases qui n'existe pas. Un autre exemple serait la multiplication de matrice : celle-ci ne fonctionne normalement qu'avec des matrices ayant des tailles en correspondance. En gros, multiplier une matrice de taille "N * M" avec une matrice de taille "N' * M'" donne une matrice de taille "N * M'". À partir du moment ou tu es sensibilisé à ce type de pratique, tu commences à voir des cas d'application partout. En réseau, tu peux imaginer avoir un tag dans ton type de socket pour connaitre son état actuel, et ainsi empêcher des cas impossibles. Tu peux imaginer ajouter l'unité d'une grandeur physique afin de refuser d'ajouter des mètres et des grammes. Tu peux imaginer une librairie de gestion de chemin de fichier qui sait (à la compilation) si le chemin est absolu ou relatif. Une librairie de gestion de couleur qui n’autorise que les traitements dans des espaces colorimétriques compatibles. Une librairie de géométrie qui interdit des opérations mal définie (addition de deux positions. Translation d'une position en deux dimension par un vecteur 3 dimensions). Si tu as une libraire de dessin (type cairo) avec plusieurs backend supportant chacun différentes fonctionnalités, tu peux imaginer que le type final de ton dessin "liste" les fonctionnalités nécessaires et refuse (à la compilation) de générer une sortie si le backend n'en est pas capable. Si tu fais du web, tu peux imaginer un langage au dessus de CSS / HTML qui s'assure (à la compilation) que tu n'a pas d'incohérence dans les noms de tes classes CSS. Tu peux aussi vérifier (à la compilation) que tous tes liens internes de ton site web pointent vers des ressources qui existent.

    La liste est infinie, le but est encore une fois d’empêcher de compiler des programmes qui ne sont pas correctes. Tout ce que tu peux exprimer au niveau du type c'est des erreurs en moins à l’exécution et des tests en moins à faire. C'est un compromis à évaluer en fonction de trop nombreux paramètres : comme ton aisance avec le langage, la simplicité de celui-ci, l'impact sur le temps de développement, l'impact d'une erreur runtime en production, l'impact sur l'évolution future du code. Ce n'est pas facile à évaluer ;)

    • [^] # Re: Nombre dans les types

      Posté par  (site Web personnel) . Évalué à 3 (+0/-0).

      Merci pour l'explication.

    • [^] # Re: Nombre dans les types

      Posté par  . Évalué à 3 (+0/-0).

      Est-ce que ton système va au-delà des "tags" ? Il existe une méthode en Ocaml pour simuler un peu cela. J'ai voulu le faire pour gérer les unités dans les calculs, c'est juste infernal, car on passe son temps à multiplier des nombres de dimensions différentes. Si le système ne gère pas la multiplication/division des dimensions, c'est vraiment pénible à l'usage.

      Pour gérer les problème de dimensions, les outils utilisé vont bien au dela (https://www.opengroup.org/face), ils définissent les API en terme d'unité, de dimension, mais aussi de référentiel (altitude vs le sol ou le point zéro, référentiel géodésique, etc…), de codage (nombre à virgule fixe 16.16 dans un entier 32 bits, par exemple)

      Tous les cas que tu cites ne sont pas des cas d'erreurs fréquents ou des difficultés particulières à détecter rapidement au runtime.

      Si vous pouviez trouver un truc pour les API REST, cela serait top, par contre; ou encore, la gestion de la compatibilité ascendante lors d'un changement de version ("est-ce que le futur $ go get ./.. va tout péter ou pas ?").

      "La première sécurité est la liberté"

      • [^] # Re: Nombre dans les types

        Posté par  (site Web personnel) . Évalué à 2 (+0/-0).

        Les tags c'est ni plus ni moins que des types phantom, qui sont tout aussi disponible en OCaml. Cela commence à devenir amusant quand tu fais des opérations non triviales entre les tags, voir que tu transformes des valeurs connue seulement à l’exécution en type. Je crois que ce qui t'as posé problème en OCaml c'est le manque de surcharge d’opérateur (si je ne me trompe pas), mais si tu as de la surcharge, cela peut être très confortable à utiliser.

        https://www.opengroup.org/face

        Merci, je vais jeter un œil, je ne connaissais pas, pourtant les trucs qui volent c'est un peu mon truc ;)

        Tous les cas que tu cites ne sont pas des cas d'erreurs fréquents ou des difficultés particulières à détecter rapidement au runtime.

        Pas fréquent, cela dépend. Pas difficile à détecter au runtime, c'est bien possible. C'est un compromis et je ne prétend pas avoir la réponse. En plus c'est un exercice difficile puisque il n'est pas aisé de quantifier l'impact dans un projet de ces méthodes. Si tu as un bug, tu peux juste imaginer ce que cela t'aurait coûté de ne pas l'avoir, mais si tu n'as pas de bug, c'est dur de savoir combien tu as économisé grâce à ta solution actuelle.

        Si vous pouviez trouver un truc pour les API REST,

        En haskell il y a https://www.servant.dev/, tu définis un type assez trapu et à partir de ce moment là tu as énormément de choses qui se font automatiquement, et tu ne peux pas compiler ton API REST tant qu'elle n'est pas implémentée correctement (pour une certaine définition de correctement). Qu'est ce que tu aimerais ?

        ou encore, la gestion de la compatibilité ascendante lors d'un changement de version ("est-ce que le futur $ go get ./.. va tout péter ou pas ?").

        Alors ça c'est un super problème, mais je pense que c'est indépendant du problème de typeage décrit dans ce journal.

        • [^] # Re: Nombre dans les types

          Posté par  . Évalué à 3 (+0/-0).

          Si tu as un bug, tu peux juste imaginer ce que cela t'aurait coûté de ne pas l'avoir, mais si tu n'as pas de bug, c'est dur de savoir combien tu as économisé grâce à ta solution actuelle.

          Je parle à l'inverse : quelle sont les bugs qui, actuellement, dans les applications actuelles, ont couté hyper chère, et que l'on aurait détecté plus tôt.

          Qu'est ce que tu aimerais ?

          C'est assez rare de maitriser le client et le serveur en même temps. L'un évolue et pas l'autre, ou ne dépende pas de la même équipe ou société. On peut aussi avoir le problème de la gestion de version.

          Alors ça c'est un super problème, mais je pense que c'est indépendant du problème de typeage décrit dans ce journal.

          Oui, j'englobe le typage dans toutes les techniques statiques pour trouver les bugs. Et la gestion des versions de dépendances est vraiment un problème chiant. (dépendance trop vieille et tu as des failles de sécurité, trop jeune, tu as un risque de non compatibilité avec ton code)

          D'ailleurs, je me demandais à quel point des projets de fuzzer comme https://github.com/rohanpadhye/jqf pourrait aider. Imaginons le refactoring d'une méthode : l'outil irait chercher l'ancienne version et la nouvelle pour lancer des tests fuzzing pour détecter des modifications de comportements. On pourrait imaginer comparer l'interface REST ./V1/ entre le vieux et le nouveau code qui aura aussi une interface ./V2/. Des tests unitaires peuvent aider, mais encore faut-il qu'ils existent.

          "La première sécurité est la liberté"

  • # Et encore un petit autre

    Posté par  . Évalué à 3 (+2/-0). Dernière modification le 27/04/20 à 13:55.

    Salut,

    C'est pour ceux qui aiment les "casse-tête".

    En général, quand je commence un nouveau langage, j'essaye des problèmes simples. Mon exercice préféré étant les Tours de Hanoï.

    Problème simple, règles claires, super facile, je sais le résoudre à la main en version optimale… Bref, simple, donc quoi de mieux ?

    Enters Befunge.

    Alors là, je n'ai jamais réussi. Il faut coder en 2D avec des data qui peuvent modifier le code exécuté… c'est super dur.

    "Le" site de référence que j'ai sur ce sujet est (spoiler alert) : Hanoimania!

    Vous y trouverez plein d'implémentations, dans plein de langages, assez divers et pas toujours simples à lire.

    Si je ne me trompe pas, par contre, aucune version en Befunge là bas (même s'il en traine probablement ailleurs).

    • [^] # Re: Et encore un petit autre

      Posté par  . Évalué à 2 (+1/-0). Dernière modification le 27/04/20 à 17:13.

      Salut,

      Comme je suis dans mon grenier des horreurs, je donne un petit coup de pouce à si quelqu'un souhaite essayer/continuer (ce bout de code est sûr pas fini/fonctionnel).

      >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>v
      v"Nombre de plateaux du jeu de Hanoi (1 à 9) : "0 <
      v>>>>>>>>>>>>>>>>>>>>>>>>>>>>0"! ruerrE">:#,_25*,>^
      >:#,_$&>:\:0\91+`|     ^
                       >\`   |
      v"début du jeu à "0<<<<<      
      >:#,_$:.0".xuaetalp">:#,_$25*,1\3\1>>>>>>>>>>>>>>>>>>>v
      v<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      :                                                |`\1<<
      1                        v<<<"déplacement de "0<<<
      \                        >:#,_$.v
      `                     v<<<"de "<<<
      v                     >:#,_$.v
      v               v<<<"vers "0<<<
      v               >:#,_$.25*,>>>>>>>>>>>>>>>>>>>>>>>>>>>^
      v
      _>\:v                 v<<<"déplacement de (1) de "0<<<
          2                 >:#,_$$\.v
          \          v<<<"vers "0<<<<<
          `          >:#,_$.25*,>>>>>>>>>>>>                ^
       v\1_$\1\`v
       `       v_>> 2\1\1-:1\ 1+ 2\3\:0\ 1\3\1-1            ^
       v       v
       v       >>>> 1\2\1-:1\ 1+ 1\3\:0\ 2\3\1-1            ^
       v
      v_>\2\`v
      v     v_>>> 3\1\1-:1 1+ 3\2\:0\ 1\2\1-1               ^
      v     v 
      \     >>>>> 1\3\1-:1\ 1+ 1\2\:0\ 3\2\1-1              ^
      2
      \
      `
      _>>>>>>>>>>>>>>>>>>>>>> 3\2\1-:1\ 1+ 3\1\:0\ 2\1\1-1 v 
      v                       1-1\1\3 \0:\1\2 +1\1:-1\3\2  <
      >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>^
      
      
      de;num;vers;3;vers
      
  • # Eiffel ?

    Posté par  . Évalué à 5 (+3/-0).

    Quid de Eiffel ?

    J'ai eu plusieurs fois l'occasion de rencontrer son (futur) concepteur quand il travaillait chez EDF aussi je me demandais si ce langage était encore utilisé.

    • [^] # Re: Eiffel ?

      Posté par  . Évalué à 3 (+1/-0).

      Je crois que Bertrand Meyer a pris sa retraite, pour le reste pas la moindre idée.

  • # Ras le bol de JS => Brython

    Posté par  (site Web personnel) . Évalué à 3 (+2/-1).

    Voila un truc d'avenir : https://brython.info/

    Du python partout … dans les serveurs … dans les navigateurs … enfin de la simplicité,
    de la logique et de la lisibilité

  • # Un langage stable, rapide et avec un bon REPL

    Posté par  . Évalué à 10 (+10/-0).

    https://common-lisp.net/ Et si si, y'a des librairies :) https://github.com/CodyReichert/awesome-cl

    Je viens de déployer un site pour un client: lecture d'une base de données, templates à la Django (Djula), un panier client, envoi de mail, et voilà, c'est simple, rapide et efficace.

    Ce qui me facilite la vie c'est qu'une fois lancé en prod (sbcl --load run.lisp, qui charge mon projet et lance le serveur web dans une thread), j'ai toujours accès au REPL de l'application qui tourne. Ça me sert pour facilement modifier des variables de configuration (quand le client change plusieurs fois par semaine d'horaire ou d'adresse de contact). Je m'en suis bien sûr servi également pour recharger toute l'application à chaud, y compris en ayant besoin d'installer de nouvelles dépendances, et bien que ça ai très bien marché je serais prudent sur cette étape !

    Et donc, le REPL. Y'a des langages évolués qui émergent, mais aucun n'a un si bon REPL, voir aucun REPL du tout. Le temps écriture -> test -> validation -> je recommence est bien plus rapide et fun avec un REPL.

    De plus, Common Lisp (SBCL) renvoie pas mal d'erreurs ou de warnings de typage à la compilation (d'une fonction, vu que pendant le dév on compile fonction par fonction). Il y a même une librairie en version gamma qui apporte un typag statique complet à la ML/Haskell (Coalton).

    https://lispcookbook.github.io/cl-cookbook/

    (ps: Atom avec SLIMA a tout ce qu'il faut)

    • [^] # Re: Un langage stable, rapide et avec un bon REPL

      Posté par  (site Web personnel) . Évalué à 2 (+1/-0).

      Lisp la force de la simplicité <3

      Il faudrait que je m'y remette

    • [^] # Re: Un langage stable, rapide et avec un bon REPL

      Posté par  . Évalué à 3 (+1/-0).

      Et donc, le REPL. Y'a des langages évolués qui émergent, mais aucun n'a un si bon REPL, voir aucun REPL du tout. Le temps écriture -> test -> validation -> je recommence est bien plus rapide et fun avec un REPL.

      C'est joueur de dire ça quand il y a ipython en face :)

      J'ai dans ma todo list à aller voir du coté de clojure. « Bouh c'est pas bien c'est sur la jvm ! » Moi je m'en sert au quotidien et j'en suis bien content de cette jvm. Clojure a d'intéressant pour moi qu'il a une alternative que je trouve très cool aux fluent API : les transducers. On garde la composabilité, mais on inverse l'application. Le traitement composite est une élément de première classe qui peut être nommé et même composé pour enfin s'appliquer sur de la donnée. Ça évite les compositions fluent qui deviennent lourdingue à cause de leur taille

  • # Swift

    Posté par  (site Web personnel) . Évalué à 2 (+2/-1).

    J'ai (volontairement) laissé entrer la pomme empoisonnée (mais bon les produits sont vraiment bons et suivis contrairement à beaucoup d'autres marques « plus libres », bref, pas le débat) et je me demandais si certains avaient déjà utilisé swift. Apple semble dire que ça permet bien plus que de faire des applis iOS mais en réalité, ce langage est-il utilisé pour autre chose ?

    J'ai vu qu'ils avaient une sorte d'environnement de programmation pour les jeunes et les débutants (voire les jeunes débutants), je vais aller tenter l'expérience :)

    • [^] # Re: Swift

      Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

      Le peu que j'en ai vu me semble vraiment intéressant.
      Ils essayent de le pousser un peu vers les serveurs (c'est faisable de le faire tourner sur Ubuntu, par exemple), mais pas assez à mon goût.
      L'écosystème est encore réduit également.

      Actuellement, je fais du Python pour l'intégralité de mes besoins (à part un tout petit de JS), et je ne me vois pas apprendre un langage qui ne permette pas ça.

  • # Nim

    Posté par  (site Web personnel) . Évalué à 2 (+1/-0).

    Salut,

    Je ne vois pas Nim mentionné. C'est un langage bien sympa que j'ai découvert avec le projet GNUNet/SecuShare. Il regroupe bon nombre de caractéristiques sympathiques :

    • compilé, typage statique
    • macros et templates permettant de faire beaucoup de choses
    • compile vers du JavaScript natif (et pas Wasm) permettant une interaction avec le DOM
    • rapide (car avec peu d'abstractions, un peu comme Rust)
    • ramasse miettes précis, configurable et désactivable si besoin
    • pas de compilation statique, mais les binaires générés ne dépendent que de la libc en général. cela permet une taille d'exécutable bien plus légère comparée à du Go par exemple.
    • contrôle fin des aspects bas-niveau si besoin (pas non plus comme en Go ou la programmation système est limitée par le runtime Go et ses abstractions. Pas d'exec en Go par exemple)
    • facile à apprendre

    En gros c'est du C, mais en mille fois mieux. Une alternative à Rust si on a pas envie de se contraindre à son typage restrictif.

    • [^] # Re: Nim

      Posté par  (site Web personnel) . Évalué à 3 (+0/-0).

      Il me semble que Nim n'a toujours pas d'IDE complet (avec un debugger notamment). Snif.

      Incubez l'excellence sur https://linuxfr.org/board/

      • [^] # Re: Nim

        Posté par  (site Web personnel) . Évalué à 2 (+1/-0).

        vim + gdb ?

        Sinon, il y a peut être VSCode, mais j'avoue ne pas trop me pencher sur la question des IDE, j'aime pas les outils qui font tout à la fois.

        Sinon, j'ai aussi découvert Zig fait par un ancien de Nim semble-t-il. Un langage plus bas niveau qui vise a remplacer le C avec un set de fonctionnalités similaires mais modernisé sur plein de points: https://ziglang.org/

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.