David Delassus a écrit 761 commentaires

  • [^] # Re: Kamoulox !

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 2. Dernière modification le 17 mars 2022 à 23:32.

    Premièrement, c'est pas le sujet, le concept de checked exception existe, qu'elles soient utilisées ou non en pratique est un autre débat.

    Deuxièmement, c'est principalement les RuntimeException (et classes filles) qui sont unchecked.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Conteneurs

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 3.

    Sauf que je peux déjà plus ou moins le faire sans Generics ça.

    type Comparable interface {
      Compare(other Comparable) (int, error)
    }
    
    type Foo struct {}
    
    func (Foo) Compare(other Comparable) (int, error) {
      switch other.(type) {
      case Foo:
        // do stuff
        return 0, nil
    
      default:
        return 0, errors.New("wrong type")
    }
    
    func Sort(items []Comparable) ([]Comparable, error) {
      // do stuff
    }

    Alors oui, ça fait un peu de boilerplate. Mais c'est possible.

    Donc je persiste et signe, les generics servent surtout a faire de la composition d'interface justement pour retirer le boilerplate de ces fonctions.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Kamoulox !

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 3.

    C'est justement pour ça qu'il précise juste après l'existance des exceptions "unchecked".

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Deepfake, pas fake

    Posté par  (site web personnel) . En réponse au lien First ever war deepfake. Congratulations. Évalué à 6.

    Ah le porno… Moteur de l'innovation depuis toujours.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Kamoulox !

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 5.

    Pas sûr de comprendre.

    Si t'as rien qui catch ton exception, ça remonte aussi en haut de la stack et fini par quitter le programme anormalement.

    Je disais que le panic/recovery était plus moche car au moins avec de vrai exceptions tu peux contrôler précisément ou et quand le recovery code s'exécute.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Kamoulox !

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 4. Dernière modification le 17 mars 2022 à 15:05.

    Sauf que tu as recover() en Go pour "catch" le panic.

    Ce qui veut dire que panic/recover c'est le try/catch de Go, mais en plus moche.

    https://go.dev/blog/defer-panic-and-recover

    EDIT: A moins que c'est justement ce qui tu voulais dire, auquel cas, ignore mon message.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Kamoulox !

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 5.

    Quand tu lis la documentation d'une fonction, si elle retourne un Result, tu peux tout de suite savoir les cas d'échec possibles. Et le système de type du langage peut vérifier que tout les cas sont gérés.

    C'est pas le cas avec les exceptions. Les throws que tu fais ne font pas partie de la signature de la fonction.

    Les exceptions sont une forme de early return, souvent comparé à goto. Pour ma part je trouve les 2 concepts complémentaire, cf Elixir ou on utilise {:ok, val} ou {:error, reason} pour ce qui est prévisible et raise / rescue pour ce qui ne l'est pas.

    Dans certains cas je peux considérer qu'une requête HTTP qui retourne une 404 est prévisible mais qu'un network unreachable ne l'est pas : est-ce que la ressource existe ?

    Dans d'autres cas, je considère que toute erreur est imprévisible : j'ai besoin de tel document

    Dans le premier cas, si je reçois un {:error, 404} je peux retourner false et laisser l'exception network unreachable se propager.

    Dans le second cas je transforme le {:error, reason} en exception (unwrap).

    Les Result et les exceptions permettent tout deux de séparer le code et la gestion d'erreur, mais la sémantique, les performances, et l'intégration au système de type sont radicalement différent.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Go ou golang ?

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 8.

    Le nom c'est Go.

    Mais quand je fais des recherches google, les résultats sont plus pertinents en utilisant le terme "golang". Du coup je mélange un peu les deux.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Kamoulox !

    Posté par  (site web personnel) . En réponse au journal Golang, oops you did it again. Évalué à 10.

    L'avantage du type Result<Data, Error> c'est les opérations que tu peux chaîner.

    Result<T, E> -> Result<U, E> -> Result<V, E> -> ...

    Tu peux donc découpler la gestion d'erreur de ton algorithme et ce sans avoir besoin d'exception et de "jump" dans le code.

    Un exemple en Elixir avec ma lib rustic_result :

    K8s.Client.get("example.com/v1", :example, selector)
      |> then(&K8s.Client.run(conn, &1))
      |> Result.or_else(fn _ -> Result.err(:not_found) end)
      |> Result.and_then(fn resource ->
        case resource["status"]["phase"] do
          "Accepted" -> Result.ok(resource)
          "Rejected" -> Result.err(:rejected)
          nil -> Result.err(:rejected)
        end
      end)

    En gros :

    • map : Result<T, E> -> Result<U, E> avec f : T -> U
    • map_err : Result<T, E> -> Result<T, E2> avec f : E -> E2
    • and_then : Result<T, E> -> Result<U, E2> avec f : T -> Result<U, E2>
    • or_else : Result<T, E> -> Result<U, E2> avec f : E -> Result<U, E2>

    Le principe est de te permettre, grâce à ce type et ses opérations, de composer des "computations" et d'en récupérer un type final cohérent que tu peux traiter correctement.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: ouf

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust (partie 2). Évalué à 2.

    Miam, merci de ne pas avoir fait un journal pour ça :D

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Première partie

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust (partie 2). Évalué à 2.

    Ah je pensais que tu parlais du lien dans la partie 1 vers la partie 2.

    Le lien dans la partie 2 vers la partie 1 et cité tout en haut de l'article :)

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Mastermind

    Posté par  (site web personnel) . En réponse au lien Clone de Wordle pour les ouvertures d'Échecs. Évalué à 4.

    Wordle c'est aussi un mastermind du dictionnaire anglais.

    De temps en temps on tombe sur un farfelu qui croit qu'il a inventé l'eau chaude mais le plus souvent, c'est une adaptation d'un modèle existant.

    -- Venec, Kaamelott.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Première partie

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust (partie 2). Évalué à 3.

    Merci :) j'oublierai pas de lier la partie 3 ici ^

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: J'ai pas écouté le podcast, mais mon grain de sel

    Posté par  (site web personnel) . En réponse au lien Is functional programming the hipster programming paradigm?. Évalué à 4.

    Tu ne peux pas copier une fonction avec sa scope, juste la passer par référence.

    Et tu n'as jamais pu le faire. Les Worker sont l'exemple parfait du fait que tu ne puisse pas copier une fonction.

    C'est comme en C, tu as des pointeurs de fonctions, que tu peux copier, mais pas la fonction en elle même.

    En Erlang / Elixir, tu as 2 types de fonctions :

    • les fonctions anonymes, qui peuvent être copiée et stockée dans un ETS/DETS/Mnesia, envoyé a un autre node du cluster pour être exécutée la bas, AVEC sa scope
    • les fonctions définies dans un module, et la tu les "copie" en passant le tuple {Module, Function, Arguments} ou Module et Function sont des atoms.

    Alors oui, assigner une fonction (sa référence) à une variable te permet d'avoir des fonctions d'ordre supérieur et de faire de la programmation fonctionnelle, mais non ça ne fait pas de ton langage un langage fonctionnel.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: J'ai pas écouté le podcast, mais mon grain de sel

    Posté par  (site web personnel) . En réponse au lien Is functional programming the hipster programming paradigm?. Évalué à 3.

    Parce que je fais la différence entre :

    • langage objet et programmation orientée objet
    • langage fonctionnel et programmation fonctionnelle

    Dans un langage objet, tout est objet, tu n'as pas de type primitifs (1 et true et "hello" sont des objets, pas des valeurs primitives).

    Dans un langage fonctionnel, les données sont immutables et les fonctions sont des données.

    En Javascript, tu ne peux pas passer une fonction en tant que valeur à un Worker (thread/process) par exemple. Ce qui veut dire que la fonction n'est pas une valeur. Ce n'est donc pas un langage fonctionnel.

    Toujours en Javascript, 1 n'est pas un objet, ce n'est donc pas un langage objet.

    Cela n'empêche pas d'appliquer du mieux qu'on peut les concepts de ces 2 paradigmes.

    A ma connaissance, seul Smalltalk est un vrai langage objet (le code en lui même est un objet que tu peux manipuler).

    Concernant les langages fonctionnels, on va identifier 2 types :

    • purement fonctionnel (les effets de bords sont abstrait par le runtime) : Haskell / Purescript
    • les autres : Erlang / Elixir

    Et donc par cette définition, non ni Javascript, ni Go, ni Python, ni C++, ni beaucoup de langages ne sont "objets" ou "fonctionnels". Ultimement tout ces langages se retrouvent exécuté par un CPU, donc représenté d'une manière ou d'une autre par une suite d'instruction assembleur. Donc a partir du moment ou ton langage est Turing-Complete, tu peux réutiliser ces paradigmes.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: J'ai pas écouté le podcast, mais mon grain de sel

    Posté par  (site web personnel) . En réponse au lien Is functional programming the hipster programming paradigm?. Évalué à 6.

    En Go, tu peux passer des fonctions en argument à d'autres, ce qui implique que tu as des fonctions d'ordre supérieur. Avec les generics de Go 1.18 on voit apparaître des patterns typique de la programmation fonctionnelle (cf les 2 liens que j'ai cité avec l'implémentation des map/filter/reduce/…).

    La POO c'est le concept d'encapsulation des données pour présenter une API (les méthodes) pour les manipuler. A ce que je sache, Go permet cela avec les structures et les fonctions qui prennent un paramètre "receiver".

    De plus, les interfaces de Go permettent d'avoir du polymorphisme.

    La programmation fonctionnelle, c'est l'art d'organiser son code autour de fonctions, de closures, et de combinators. Toutes ces choses sont possible en Go.

    Tu peux aussi faire de la POO en C : https://www.cs.rit.edu/~ats/books/ooc.pdf

    C'est quand même ballot de prendre comme exemple un langage qui n'est ni objet, ni fonctionnel…

    Pas si ballot que ça du coup, vu que c'est le parfait exemple d'un langage qui permet de marier les 2 concepts.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • # J'ai pas écouté le podcast, mais mon grain de sel

    Posté par  (site web personnel) . En réponse au lien Is functional programming the hipster programming paradigm?. Évalué à 8.

    Dans le domaine du jeu vidéo, on voit apparaître depuis 10-15 ans un retour vers la programmation fonctionnelle (qui est très vieille), notamment avec le concept de Data-Oriented Design et les Entity Component System.

    La programmation orienté objet s'est faite malmenée en entreprise. J'entends par la que les paradigmes initiaux ont été détourné de telle sorte qu'elle ne tient plus ses promesses (réutilisation du code).

    J'en profite pour citer sans connaître l'auteur original :

    En POO, si tu veux une banane, tu dois ajouter le gorille qui tient la banane et toute la forêt.

    Sauf que POO et Programmation Fonctionnelle ne sont pas incompatible. Prenons l'exemple de Go (ugh, qu'est-ce que je déteste ce langage trop pratique…). Il y a des interfaces, des structures, des méthodes. Pas d'héritage, que de la composition. C'est suffisant pour implémenter de l'injection de dépendance, pattern très utilisé en programmation fonctionnelle pour découpler le business code de l'implémentation.

    Les ECS en gamedev sont aussi une forme d'injection de dépendance, ajouter un composant (uniquement des data) à une entité (uniquement un id) lui ajoute de la fonctionnalité via les systèmes (uniquement de l'implémentation).

    Cette découpe est non seulement pratique d'un point de vue maintenance du code, mais en plus, elle scale bien.

    Et si on reprend l'exemple de la POO dans le monde des entreprises, avec les dizaines de couche d'abstraction pour faire un print, les librairies qui font plus que leur scope initiale (coucou log4shell), ça ne scale pas. On se retrouve 10 ans plus tard avec un monolithe qui mets 45min a build un simple commit.

    J'ai même envie de faire le parallèle avec l'infrastructure. Aujourd'hui, les microservices sont au centre d'une grosse hype. L'idée étant d'avoir des briques élémentaires (fonctions, microservices, …) que tu assembles comme des Lego pour construire un système complexe.

    Alors oui, les microservices ne répondent que a un problème organisationnel de l'entreprise (comment plusieurs teams peuvent bosser sur le même projet sans se marcher dessus ?), j'ai envie de dire que la programmation fonctionnelle répond à un problème organisationnel du code source (comment plusieurs systèmes peuvent fonctionner en parallèle sans fuite de l'API et avec une bonne "Separation of Concerns").

    L'autre raison que je vois à la popularité de la programmation fonctionnelle, c'est :

    • React/Redux et ses successeurs, dont l'architecture est purement fonctionnelle (l'UI est une fonction qui prend en paramètre le state)

    Le plus marrant je trouve la dedans, c'est que la montée en popularité de la programmation fonctionnelle a lieu surtout dans des langages qui ne sont pas fonctionnels :

    Alors qu'il y a OCaml, Elixir, Haskell, Purescript (si tu veux compiler en du Haskell en JS), etc…

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: super article !

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust (partie 2). Évalué à 6.

    En fait la continuation va indiquer ce qu'on fait ensuite.

    Donc pour parser la ligne const a := 42;, la fonction qui parse const va retourner la fonction qui parse le reste ou une fonction qui retourne une erreur.

    Et ainsi de suite, on va parser a et retourner la fonction qui parse le reste.

    L'utilisateur va donc pouvoir contrôler quand et à quel rythme l'analyse se déroule. Il peut faire une simple boucle (ou fonction récursive) :

    def get_ast({:ok, ast}), do: {:ok, ast}
    def get_ast({:error, reason}). do: {:error, reason}
    def get_ast({:fun, fun}), do: get_ast(fun.())

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Erreurs

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust (partie 2). Évalué à 5.

    Ça se tient.

    Tu remarqueras cependant qu'aucun compilo/interpréteur ne fait la différence, ils appellent les deux une erreur de syntaxe. D'où ma confusion.

    Pour ma part, j'utilise le type "ParseError" et "CompilationError", donc je fais même pas la différence avec l'erreur sémantique.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: super article !

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust (partie 2). Évalué à 4.

    Merci pour ton retour :)

    Est-ce que tu va parler de la forme de l'ast ?

    Je l'ai déjà évoqué rapidement dans la première partie, c'est un simple arbre ou chaque noeud contient les informations nécessaire pour passer à la phase suivante.

    Ces informations sont complétées au fur et à mesure des différents parcours de l'arbre, produisant à la fin le code source Rust à compiler.

    A priori, la forme SSA simplifie la manipulation du code

    Je ne connais pas la théorie derrière, c'est la première fois que j'entends le nom de ce concept, la page wikipedia parle du concept de CPS (Continuation Passing Style) qui semble plus utilisé par les langages de programmation fonctionnelle. C'est un concept que j'utilise pas mal pour l'implémentation de protocole TCP (le dernier en date, un subset du protocole de Redis), je trouve cela assez naturel.

    Qu'en penses-tu ?

    A ce stade, l'AST dans sa forme actuelle me semble suffisant, mais je garde l'article wikipedia sous la main,, on sait jamais je pourrais en avoir besoin :) Merci pour l'info.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • # Trop dur d'attendre Vendredi

    Posté par  (site web personnel) . En réponse au lien Vim users be like:. Évalué à 3.

    Emacs users be like

    https://www.reddit.com/r/vim/comments/l9hv5k/emacs_users_be_like/

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: lisp ?

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust. Évalué à 3.

    Dites les critiques, vous êtes sûrs que vous ne réagissez pas de manière un peu 'instinctive'?

    Lorsque l'on propose un changement de syntaxe à un langage, il faut regarder comment ça s'intègre avec le reste du langage.


    Par exemple, l'opérateur de pipeline en Elixir:

    a |> b() |> c()
    # vs
    c(b(a))

    Si je voudrais l'ajouter tel quel en C, ça donnerait :

    a |> b() |> c()

    Mais est-ce que ça a du sens en C ? En C un opérateur s'applique sur des valeurs, or b() n'est pas une valeur.

    Du coup, est-ce que a |> b est un pointeur de fonction ? Si oui, cela pointe vers quoi ? Est-ce que cela produit un nouveau symbole dans le .o ?

    Toutes ces questions me font répondre que : non, l'opérateur de pipeline n'a pas sa place dans le langage C.


    La totalité du langage Lisp repose sur le principe suivant : ( expression ), et f a b c est une expression ou f est une fonction. Ce qui veut dire qu'en Lisp, il n'y a pas d'opérateurs, + est aussi une fonction.

    Dans mon exemple, f prend 3 arguments, maintenant pour le second exemple : (g (cons a b c)). Ici g prend un seul argument, une liste, construite avec cons.

    On a (defvar a 42), (defun ...), (lambda ...) et (defmacro ...). Tout se ressemble. C'est cohérent. Du coup introduire f(a b c), ça casse la cohérence.

    Le Lisp classique a d'ailleurs '(x y) pour l'abbréviation de (quote x y)..

    Après m'être documenté, 'x est l'abbréviation de (quote x). Et donc '(x y) devrait être (quote (x y)). Il semble que les différents Lisp (Scheme et compagnie) gèrent ça différemment.

    cf: https://stackoverflow.com/a/20643658/1020897

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: lisp ?

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust. Évalué à 3.

    (je trouve que) l'on délimite mieux les portées des uns et des autres avec (f (g ) )

    Le meilleur exemple "non-lisp" (entre grosses guillemets) c'est les templates Go :

    {{ $d := dict "foo" (list 1 2 3) "bar" (list 1 2 3) }}
    {{ $test := eq (index (index $d "foo") 0) (index (index $d "bar") 0) }}
    

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: toy ?

    Posté par  (site web personnel) . En réponse au lien Un Kubernetes dans un Kubernetes dans un Kubernetes dans un Kubernetes dans un Kubernetes dans.... Évalué à 4.

    Pour moi on a pas le meilleur des deux comme promis mais le pire des deux.

    Je suis pas vraiment d'accord. L'intérêt d'un cluster dans un namespace c'est plusieurs choses :

    • pouvoir mettre des quotas sur l'utilisation d'un cluster (nombre de pods etc…)
    • pouvoir isoler 2 clients à qui tu fournis une plateforme pour déployer leurs workloads
    • pouvoir créer/supprimer/mettre à jour des clusters à la volée
    • pouvoir contrôler le coût de fonctionnement de ton infra plus précisément

    De plus : https://www.vcluster.com/docs/architecture/synced-resources#sync-other-resources

    Syncing other resources such as deployments, statefulsets and namespaces is usually not needed as those just control lower level resources and since those lower level resources are synced the cluster can function correctly.

    However, there might be cases though were custom syncing of resources might be needed or beneficial. In order to accomplish this, vcluster provides an SDK to develop your own resource syncers as plugins. To find out more, please take a look at the plugins documenation.

    Cela veut dire que tu peux créer un opérateur Kubernetes en mode SaaS :

    • tu installes l'opérateur sur le cluster hôte
    • tu ajoute tes CRDs au vcluster, ainsi que le plugin pour les synchroniser sur le cluster hôte
    • tu fournit un vcluster a tes clients, qui peuvent bénéficier de ton opérateur sans devoir gérer l'installation / la mise à jour / …
    • derrière, tu scale le cluster hôte, ça scale tout le monde

    C'est d'autant plus intéressant car vcluster ce n'est pas de la virtualisation (c'est pas KinD - Kubernetes in Docker), c'est juste un niveau d'abstraction supplémentaire pour découper proprement les choses.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg

  • [^] # Re: Générer du Rust

    Posté par  (site web personnel) . En réponse au journal [Letlang] Écrire un compilateur en Rust. Évalué à 3.

    Tu peux utiliser des templates dans une macro, et simplement convertir la le résultat avec TokenStream::parse a la fin

    Ah effectivement, ça peut être intéressant.

    L'aventage de la macro comparé a apeller rust toi même c'est que ça s'intègre avec cargo qui gère les flag compliqué pour toi comme la cross compilation, le compilation incrementale, et que sais-je.

    A terme, j'aimerais bien que mon compilo soit self-hosted (écrire letlang en letlang). Du coup je vais finir par devoir les gérer moi même ces flags, non ?

    L'idée est qu'il n'y a pas de main.rs. Uniquement un Cargo.toml qui dit a cargo d'ouvrir lui même (a cause du path = "Cargo.toml") et de simplement apeller ta macro qui fait le reste.

    Fun, je vais regarder ça de plus près.

    Merci pour tes retours.

    https://link-society.com - https://kubirds.com - https://github.com/link-society/flowg