Journal La plus belle ligne de code

31
13
oct.
2023

Je voulais partager avec vous la plus belle ligne de code que je connaisse:

if (condition) return

Voilà, simple, efficace.

On peut exprimer la même chose un peu différemment, en profitant de ruby par exemple:

return if condition

ou encore:

return unless autreCondition

C'est beau n'est ce pas?

Bien sûr cela ne fait pas grand chose, il faut même considérer que cela ne fait rien, ou plutôt, cela arrête ou empêche de faire, avec ce return indispensable.

D'ailleurs vous pourrez remarquer qu'il vaut mieux mettre cette ligne avant d'autres, sinon elle n'a absolument aucune utilité, ce qui la rendrait bien peu intéressante.

if (objetDeTravail == null) return;

objetDeTravail.callOtherMethod();
objetDeTravail.callYetAnotherMethod();

Ah oui, là vous pouvez remarquer un usage très fréquent de ce type de clause, en java => s'assurer que l'on n'appelle pas une méthode sur un objet null. Ce qui enclenche une malédiction malfaisante sur la malheureuse machine où s'exécute ce malheureux code.

On pourrait exprimer la même chose sans utiliser notre belle ligne de code avec son return si bien placé, cela donne alors quelque chose comme ça:

if (objetDeTravail != null) {
  objetDeTravail.callOtherMethod();
  objetDeTravail.callYetAnotherMethod();
}

C'est fou non? On voit de mieux en mieux ce qu'apporte notre magnifique ligne: moins d'accolades, de begin/end, d'indentations! :-)

Mais, certaines recommandations de style enjoignent, ou enjoignaient, à ce que le code qui n'a qu'un point d'entrée n'ait aussi qu'un seul point de sortie. Afin de proposer une lecture la plus linéaire possible.

Ce qui pouvait donner ce genre de construction:

def methodeLineaire:
  result = ""

  if condition:
    result = "autre résultat"

  return result

Alors que franchement:

def methodePlusBelle:
  if not condition: return ""

  return "autre résultat"

Moins de variable, moins de potentiels else, on teste et on sort vite, une fois ou plusieurs s'il le faut. Car oui, on peut multiplier cette belle ligne, on rajoute des conditions de sortie autant de fois que nécessaire!

Un des effets notables de ce style de code, avec des clauses qui bloquent l'exécution le plus tôt possible, c'est que l'on découvre des points pivot dans notre code, que l'on peut transformer en méthode protégées par ces "gardes".

Parois des centaines ou des milliers de lignes s'accumulent et se regroupent, mystérieusement, dans une seule méthode. Des enchaînements de blocs de code, avec des chemins d'exécution un peu compliqués, des duplications ici et là, des conditions très complexes, des variables utilisées du début à la fin mais pour des usages différents, des boucles contenant des boucles contenant des boucles, une horreur à relire et à maintenir. Promis, je n'ai jamais jamais fait ce genre de code personnellement⸮

Pour sortir de cet enfer on peut justement se concentrer sur ces pivots, ces chemins d'exécution qui sont "protégés" par des conditions peut-être un peu mal organisées. Souvent le plus simple est d'étudier les blocs de code dans leur profondeur.

Car oui, une méthode constituée de milliers de lignes de code est souvent aussi très profonde: elle contient des boucles contenant des conditions avec d'autres boucles et conditions imbriquées, sur tellement de niveaux que si le code est bien indenté, et bien on a besoin de faire défiler la vue horizontalement! /o\

Ces blocs de code tout en profondeur sont souvent de bons candidats pour être "promus" dans leur propre méthode. On les enferme dans leur propre boite, et avec un nommage décent on rend tout ce plat de spaghettis un peu plus digeste!

Je crois que, de plus en plus, les programmeurs cherchent à grandement limiter la profondeur de leur programme, pour le rendre plus lisible et plus maintenable. Et je constate que ça marche bien. On obtient du code aéré, bien découpé, élégant!

Il est si beau ce code, je voudrais le garder pour moi et ne jamais le pousser sur le dépôt central… dans la grande jungle de l'équipe des développeurs, où il risque tellement de tomber sur un moi d'il y a vingt ans!

  • # Inspiration

    Posté par  . Évalué à 4.

  • # return early pattern

    Posté par  . Évalué à 10. Dernière modification le 14 octobre 2023 à 09:21.

    Connu aussi comme return early pattern.
    Il permet de diminuer la complexité, rend le code beaucoup plus lisible et évite le code Hadouken :
    code Hadouken

    • [^] # Re: return early pattern

      Posté par  (site web personnel, Mastodon) . Évalué à 3.

      Top cette image!

      Moi j'appelle aussi ça des escaliers vers l'enfer :)

      • [^] # Re: return early pattern

        Posté par  . Évalué à 4.

        et encore là on a un truc simple avec juste de if if if if if puis else else else…

        on a un bel double escalier

        J'ai un collègue qui me fait if if else if else if if else…, le tout en cœur de boucle et sur un écran de haut…

        Il ne faut pas décorner les boeufs avant d'avoir semé le vent

    • [^] # Re: return early pattern

      Posté par  (site web personnel, Mastodon) . Évalué à 9. Dernière modification le 15 octobre 2023 à 02:07.

      Dans tous les cas, si le code a autant de niveaux d’indentation – ou des milliers de lignes dans une seule méthode comme le mentionne le journal –, c’est un code qui a des problèmes bien plus graves que juste quelque chose qui se résous à coup de return early pattern. C’est un code qui a une complexité cyclomatique délirante, et c’est ce point qu’il faut corriger en premier. D’autant plus que si ce genre de méthode arrive à exister au sein du code, c’est aussi presque toujours signe qu’il n’y a aucun test digne de ce nom.

      La connaissance libre : https://zestedesavoir.com

    • [^] # Re: return early pattern

      Posté par  (site web personnel) . Évalué à 7.

      Un de mes collègues m'a fait une merge request comme ça, avec 9 niveaux d'indentation en mettant toute la logique dans un nouvel indent.

      if (init == OK) {
          if (open_partition() == OK) {
          }
      }

      Il ne comprend pas non plus pourquoi je demande à ce qu'on dépasse pas environ 100 colonnes dans le code. Bah… tout simplement parce que le diff la merge request ne passait pas verticalement avec toute cette indentation.

      Le grand problème quand ce genre d'individu a toujours travaillé tout seul sans jamais regarder le code de quelqu'un d'autre ou d'un projet opensource.

      git is great because linus did it, mercurial is better because he didn't

      • [^] # Re: return early pattern

        Posté par  . Évalué à 3.

        tout simplement parce que le diff la merge request ne passait pas verticalement avec toute cette indentation.

        Autant les pyramids of doom sont un VRAI probleme, et je matraque constamment a mes équipes que guard else c'est pas fait pour le chien (oui, on fait du swift, et guard a en cadeau bonux une garantie du compilateur que le block return ou fatalError()), que la lisibilité et réduire le contexte a garder en tete en relecture est plus important qu'une règle arbitraire "un seul return par fonction", autant l'excuse du "le diff est pas lisible" est assez bidon.

        Utilise un outil different si ton outil est pas capable de faire des trucs de base. J'ai eu le meme débat (assez houleux) a propos de nos règles PMD qui interdit les "space terminated strings", l'excuse donnée étant que le mec qui a mit au point les règles et qui faisait pas mal de review utilisait un outil des bois qui était pas foutu de filtrer les whitespace des diffs.

        Bon, apres, si tu fais surtout du python, effectivement, c'est une autre histoire.

        Linuxfr, le portail francais du logiciel libre et du neo nazisme.

        • [^] # Re: return early pattern

          Posté par  (site web personnel) . Évalué à 9.

          Utilise un outil different si ton outil est pas capable de faire des trucs de base

          L'outil en question est une merge-request sur GitLab avec un écran 27". Je pense que quand le diff ne passe pas sur l'écran splité en deux c'est qu'il y a un vrai problème.

          git is great because linus did it, mercurial is better because he didn't

          • [^] # Re: return early pattern

            Posté par  (site web personnel) . Évalué à 6.

            J'ai aussi rencontré le problème, des lignes interminables et des diffs illisibles même en plein écran en enlevant toutes les fioritures autour. J'ai aussi l'impression que les devs qui codent ainsi passent proportionnellement trop peu de temps à lire le code des autres, et la réaction est toujours là même : change d'outil, achète un écran plus grand, et autres variantes du « chez moi ça marche ». Toujours plus de dépendances et de ressources, il n'y a personne pour pousser vers un environnement de dev plus léger :(

  • # Optional<T>

    Posté par  (site web personnel, Mastodon) . Évalué à 3. Dernière modification le 14 octobre 2023 à 11:53.

    Sinon, en java tu as la classe Optional, qui va gérer les cas des null, sans rajouter d’if…

    • [^] # Re: Optional<T>

      Posté par  . Évalué à 5. Dernière modification le 14 octobre 2023 à 13:39.

      ou juste utiliser Kotlin

      monObj?.call()

    • [^] # Re: Optional<T>

      Posté par  . Évalué à 4.

      Je n'ai jamais compris pourquoi ils ont implémenté Optional aussi tôt… Ils avaient déjà l'idée de ce qui allait se faire et aujourd'hui je trouve que c'est une classe qui utilise mal le langage. À mon avis Optional devrait être une interface sealed implémentée par un record disons Present et une classe (voir un singleton) Absent. Il ne devrait pas y avoir de méthode get() et l'interface ne devrait porter que le méthodes qui en font une monade. Tu aurais une classe dont aucune des méthodes ne peut lever de NPE et au lieu d'avoir tous les analyseurs statiques qui doivent tenter de vérifier si tu utilise get() dans un endroit sûr ce serait vérifié dans le langage par le typage (et pour le quel l'inlining peut rendre ton code efficace). Et tu n'aurait plus besoin de la méthode get() grâce au pattern matching (qui n'en est pas vraiment un en java). On va garder encore 20 ans une classe qui aura finalement rapidement était hors de ce que Java peut produire.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # sealed ?

        Posté par  . Évalué à 1.

        interface sealed

        Ah du coup question, c'est quoi le sens/l'idée du terme sealed ?
        Je l'ai déjà vu passé dans du code, mais je n'ai pas compris le sens, l'idée que ça exprimait.

        Merci

        • [^] # Re: sealed ?

          Posté par  . Évalué à 6.

          Sealed c'est un moyen de contrôler l'héritage d'une classe ou d'une interface.

          Jusqu'à l'arrivée de sealed on avait le mot clef final qui indiquait qu'une classe ne pouvait avoir de fille. sealed permet d'indiquer la liste exhaustive des filles direct d'une classe. Dans l'exemple au dessus, Optional<T> n'a que 2 soustype Present et Absent.

          Ça peut être assez déroutant parce que ça contreviens au principe de substitution de Liskov, mais c'est pratique pour certaines choses. Ça ne doit pas être utilisé pour tout, mais pour des cas où on sait qu'on a un arbre de types limité qui ne devrait pas évoluer sans que le code qui l'utilise le prenne en compte.

          Imaginons (je tire l'exemple de mon chapeau c'est peut être pas un bon exemple), tu veut gérer une machine à états finis, mais ça t'arrangerait que l'état soit plus qu'une simple valeur (donc pas un enum) par exemple pour aider au debug. Tu peut alors créer une classe ou une interface et dont tu sais que toutes les filles direct représente un état de ta machine.

          L'intérêt c'est de pouvoir utiliser ça avec le pattern matching et les switch expressions, puisque tu va pouvoir manipuler ton état tout en garantissant l’exhaustivité des cas. Si tu ajoute un nouvel état c'est un nouveau sous-type et tu devra l'implémenter dans tout les endroits où tu a eu besoin de traiter tes états.

          Une autre manière de voir c'est de s'en servir comme un type somme du pauvre. Tu peut créer un type T qui est soit A, soit B, soit C,… et écrire des fonctions qui sont polymorphiques sur leur paramètre (tu prend en paramètre un type T et en fonction du sous-type tu réagi différemment). Du pauvre parce que les sous-type ne peuvent pas être arbitraires comme tu le ferait en haskel.

          Je suis un peu loquace ?

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: sealed ?

            Posté par  . Évalué à 1.

            Je ne dirais pas que l'explication est limpide, mais elle m'a quand même aider à mieux comprendre le cas que j'avais rencontré.

            Donc merci 👍

            Note: le cas que j'avais rencontré est dans la lib Rust CityHasher :
            https://docs.rs/cityhasher/latest/src/cityhasher/lib.rs.html#434

          • [^] # Re: sealed ?

            Posté par  . Évalué à 4.

            Pourquoi ça contreviendrait au principe de substitution ? J’ai pas compris. Suffit toujours que les classes filles respectent les contrats de la classe mère non, c’est indépendant ?

            • [^] # Re: sealed ?

              Posté par  . Évalué à 3.

              La classe mère qui liste ses classes filles d'une part et parce que c'est globalement fait pour écrire :

              public String foo(A a) {
                  return switch(a) {
                      case B b -> "B: %s".formatted(b);
                      case C c -> "C: %s".formatted(c);
                  };
              }

              Ce qui blesse le cœur de tout ceux qui expliqueraient (non sans raison) que le polymorphisme c'est bien et qu'il faudrait une méthode décrite dans A et implémentée dans B et C. Ici le fait qu'une méthode hors de A, B ou C, cherche à déterminer le type sous-jacent de la référence a est la violation la plus classique de Liskov.

              Présenté autrement : en respectant Liskov si tu crée un sous-type du type T tu devrais pouvoir remplacer n'importe quelle référence de T par une instance de ton sous-type sans changement du code qui utilise la référence. Ce n'est évidement pas le cas ici et c'est même un effet recherché.

              C'est pour ça qu'il faut manier avec précaution dans un langage comme java qui utilise surtout le polymorphisme de classe et qui commence tout juste à utiliser ce genre de mécanismes (tout le monde appel ça du pattern matching, mais ça n'en est pas en java).

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: sealed ?

                Posté par  . Évalué à 4. Dernière modification le 17 octobre 2023 à 17:06.

                On a pas la même définition du principe de substitution de Liskov : https://blog.async-agency.com/arretez-de-developper-nimporte-comment-principe-ouvert-ferme-2/

                Dans ton exemple la méthode foo doit pouvoir prendre en paramètre n’importe quel sous-type de A en respectant le contrat (sur A) de la méthode formatted, donc si formatted est implémentée correctement à mon avis on est bon pour le principe de substitution.

                Il ne vaut pas forcément dans l’implémentation de la fonction mais dans les contrats définis sur les méthodes, je pense.

                Mais tu veux ptete parler du principe d’encapsulation ?

                • [^] # Re: sealed ?

                  Posté par  . Évalué à 5.

                  Dans ton exemple la méthode foo doit pouvoir prendre en paramètre n’importe quel sous-type de A en respectant le contrat (sur A) de la méthode formatted, donc si formatted est implémentée correctement à mon avis on est bon pour le principe de substitution.

                  Si tu crée un nouveau sous type de A, ta fonction ne compile plus.

                  Le fais que du code utilisateur soit conscient des sous type de ses paramètres (ou de ce qu'il reçoit d'une fonction) empêche la substitution.

                  Mais tu veux ptete parler du principe d’encapsulation ?

                  On parle de typage et pas de l'état interne des objets donc je dirais que non.

                  https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                  • [^] # Re: sealed ?

                    Posté par  . Évalué à 4. Dernière modification le 17 octobre 2023 à 19:17.

                    C’est pas plutôt le Principe ouvert fermé (O de SOLID), ça ? Si c’était impliqué par le principe de Liskov il n’y aurait pas besoin de l’avoir en principe en plus. Et je ne vois pas vraiment que le principe de Liskov implique que ton code doive compiler si tu rajoutes un sous type. Le seul truc du principe c’est qu’un sous type ne doit pas casser les invariants comportementaux d’un super type.

                    Cette implication n’est en tout cas pas du tout mentionné dans :en:Liskov substitution principle.

                    Notamment parce que par principe le fait que ce soit "Sealed", justement, empêche l’extension. Le sous typage est toujours possible en sous-typant les cas connus ?

                    • [^] # Re: sealed ?

                      Posté par  . Évalué à 3.

                      C’est pas plutôt le Principe ouvert fermé (O de SOLID), ça ? Si c’était impliqué par le principe de Liskov il n’y aurait pas besoin de l’avoir en principe en plus.

                      Tu montre bien qu'il y a des recoupements entre les concepts que l'on nomme 😊

                      Et je ne vois pas vraiment que le principe de Liskov implique que ton code doive compiler si tu rajoutes un sous type. Le seul truc du principe c’est qu’un sous type ne doit pas casser les invariants comportementaux d’un super type.

                      Non effectivement il ne s'intéresse pas ou peu à l'utilisation, mais je pense qu'il s'agit d'un évitement du principe de Liskov, car l'objectif c'est de retirer des traits du parents et de les implémenter dans le code utilisateur.

                      Le fait que le code ne compile pas est extrêmement important et il faut vraiment y faire attention. Le type A peut techniquement être dans une bibliothèque chargée dynamiquement par exemple. L'utilisation et l'exposition de ce genre de type est plus risqué que du polymorphisme car l'arbre de type va faire partie du contrat (et pas uniquement le type parent).

                      Notamment parce que par principe le fait que ce soit "Sealed", justement, empêche l’extension. Le sous typage est toujours possible en sous-typant les cas connus ?

                      En java en tout cas oui. À noter qu'il n'est pas possible de tricher par contre. Sealed est vérifié à la compilation et à l'exécution. La jvm t'interdira de charger une nouvelle sous classe ni vu ni connu en jouant avec le bytecode.

                      Je n'ai aucun problème à utiliser sealed, il y en a déjà un certains nombre dans mon code de prod et je suis impatient de pouvoir enfin utiliser les switch expression avec. Mais je comprends les gens qui sont un peu orthodoxes de la POO qui voient des problèmes avec cette forme de polymorphisme (il y en a aussi dans le polymorphisme de classe mais ils ne sont pas nouveaux pour eux).

                      Désolé pour mon commentaire plus bas j'avais oublié de le soumettre

                      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                      • [^] # Re: sealed ?

                        Posté par  . Évalué à 4.

                        Pas tellement d’accord, j’ai l’impression que c’est plutôt une utilisation du principe de liskov pour implémenter un pseudo type somme en java.

                        Le sous-typage est utilisé transférer une propriété de la classe mère : "une de mes instance est instance est instance d’exactement un de ces types", à ses classes filles. Le respect du principe de substitution de Liskov implique que ce qui doit être vrai pour toute instance de la classe mère soit également vrai de toute instance de ses sous-types. Propriété qui est vérifié par le compilo et la jvm, ce qui ne sera pas forcément le cas dans le cas de Java pour d’autres types d’invariants ou de précondition qu’on pourrait attacher aux méthodes.

                        Après en lisant Wikipédia en anglais il peut effectivement y avoir un hiatus avec le terme ''substitution'', il semblerait que Liskov elle même plutôt récemment évite de l’utiliser et qu’une terminologie plus moderne soit :en:behavioral subtyping, plus précis et qui met l’accent sur les propriétés associées, et qui prend en compte les classes abstraites, on peut pas instancier ces classes et donc substituer des objets strictement de ce type … Elles voulaient définir ce qu’est un sous typage correct, en fait.

                        When applying subtyping, generally we are not substituting subtype objects for supertype objects, we are simply using subtype objects as supertype objects. That is, it is the same objects, the subtype objects, that are also supertype objects.

                        In an interview in 2016, Liskov herself explains that what she presented in her keynote address was an "informal rule", that Jeannette Wing later proposed that they "try to figure out precisely what this means", which led to their joint publication[1] on behavioral subtyping, and indeed that "technically, it's called behavioral subtyping".[3] During the interview, she does not use substitution terminology to discuss the concepts.

                        • [^] # Re: sealed ?

                          Posté par  . Évalué à 2. Dernière modification le 18 octobre 2023 à 00:02.

                          Pas tellement d’accord, j’ai l’impression que c’est plutôt une utilisation du principe de liskov pour implémenter un pseudo type somme en java.

                          Comment l'absence de partage de trait pourrait être une utilisation du principe de Liskov ?

                          Si j'écris

                          sealed interface A {
                             record B(int i) extend A {}
                             record C(long l) extend A {}
                          }

                          Et si on applique le principe de Liskov, il n'y a pas de propriétés prouvable sur A a appliquer à B ou C. Le behavioural subtyping de la même façon parle d'un comportement défini dans le parent qui doit être respecté dans les enfants.

                          Tout le principe du sealed/switch c'est de ne pas faire porter le comportement sur le type. Je suis d'accord pour dire que ce n'est pas un viol du behavioral subtyping ou du principe de Liskov, mais c'est fait en se plaçant hors de leur champ : on retire la propriété prouvable sur le type parent.

                          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                          • [^] # Re: sealed ?

                            Posté par  . Évalué à 3.

                            Il y en a au moins une : la propriété portée par le sealed lui même, dont héritent ses enfants. C'est un "invariant" au sens du sous-typage comportemental.

                            • [^] # Re: sealed ?

                              Posté par  . Évalué à 2.

                              Euh je vois pas. Quelle est la propriété que décrite sealed que tu peux appliquer aux filles ?

                              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                              • [^] # Re: sealed ?

                                Posté par  . Évalué à 3.

                                Le mot clé sealed dit "toutes mes instances doivent être soit des B soit des C" (et pas les deux je pense)

                                Les B et les C héritent de l'interface A. Donc de la propriété. Et donc tout B étant un A est interdit d'être un C.

                                Vu comme ça sealed est juste une manière de définir un invariant de classe dans le langage.

                                • [^] # Re: sealed ?

                                  Posté par  . Évalué à 3.

                                  Ah mais non l’exclusivité est garantie par "class" mais pas par "interface" en java /o.

                                  Dans ce cas il n’y a pas grand chose à hériter effectivement. Mais c’est pas forcément intéressant pour autant comme construction, c’est comme hériter "d’object" en java, qui est trivial. Mais ton exemple n’a pas l’air de compiler donc je suis pas certain de savoir ce que tu voulais dire exactement.

                                  • [^] # Re: sealed ?

                                    Posté par  . Évalué à 3.

                                    J'avais écris le code rapidement sur téléphone, la version vérifiée c'est :

                                    sealed interface A {
                                       record B(int i) implements A {}
                                       record C(long l) implements A {}
                                    }

                                    Mais je pense avoir compris ce que tu veux dire et je me suis clairement mélangé les pinceaux entre propriété de classe et d'instances.

                                    Sans parler de Liskov ou de subtyping behavior, à mon avis il faut avoir conscience de ce que ça contraint sur le code et qu'il faut choisir une balance entre implémenter des comportements dans les class et faire porter le comportement dans des méthodes extérieures aux classes.

                                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: sealed ?

                Posté par  . Évalué à 4. Dernière modification le 17 octobre 2023 à 18:18.

                J’ai vu que Wikipédia avait jusqu’à aujourd’hui un paragraphe qui allait dans ton sens dans Principe de substitution de Liskov … Ce qui a sûrement aidé à propager cette idée … qui n’était pas vraiment sourcée sérieusement.

                Il n’y avait même pas de sous-typage dans l’exemple, donc a fortiori pas de contrats comportementaux, donc rien qui ne puisse violer la définition formelle :

                Si q(x) est une propriété démontrable pour tout objet x de type T, alors q(y) est vraie pour tout objet y de type S tel que S est un sous-type de T.

                • [^] # Re: sealed ?

                  Posté par  . Évalué à 4.

                  • [^] # Re: sealed ?

                    Posté par  . Évalué à 3.

                    Comme je le disais plus haut ça viole un principe de substitution (sans parler de Liskov) car le code appelant est conscient de l'ensemble du typage et donc que tu ne peu plus ajouter un sous type sans récrire le code qui utilise ton objet.

                    Je comprends que Liskov ne s'intéresse qu'à la modélisation et pas à comment ils sont utilisés, mais ici il s'agit de retirer les traits du type pour le mettre dans le code appelant. On peut effectivement dire que ça n'est pas un viol du principe de Liskov, mais c'est une manière de s'en passer. Dans des cas extrêmes, mais qui ne sont pas difficiles à trouver, tu n'a aucun trait dans le type parent.

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: sealed ?

                Posté par  . Évalué à 3.

                Ça casse pas plus le principe de liskov que final (interface a, public final class b implements a).

                Liskov dit que les classes filles doivent conserver exactement le même contrat que la classe mère. Pas que la classe mère peut être étendu à l’infini.

                Linuxfr, le portail francais du logiciel libre et du neo nazisme.

      • [^] # Re: Optional<T>

        Posté par  (site web personnel, Mastodon) . Évalué à 3.

        Je n'ai jamais compris pourquoi ils ont implémenté Optional aussi tôt…

        Parce qu’ils en avaient besoin pour gérer toute la programmation fonctionnelle à l’arrivée de Java 8, époque où les types scellés ou le pattern matching n’étaient encore qu’un rêve en Java.

        La connaissance libre : https://zestedesavoir.com

        • [^] # Re: Optional<T>

          Posté par  . Évalué à 3.

          Bof si c'est pour findAny() et findFirst(), ils auraient pu retourner des valeur null comme le fait l'API de collection. Ça me parait moins pire que de se coltiner du code des décennies alors qu'on avait déjà planifié au moment de son écriture qu'elle allait être désuète. Le plus drôle c'est que vavr utilise déjà ce genre de design avec java 7…

          Dans 2 ans on aura :

          • les fonctions qui retournent null
          • les fonctions qui retournent Optional
          • les fonctions qui retournent Neither ou un truc comme ça qui tirera profit de ce qu'on a actuellement

          Ouai !…

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Optional<T>

            Posté par  (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 15 octobre 2023 à 01:58.

            Non, c’est plus vaste que ça, ça permet en particulier d’écrire des chaines d’appels complètes sans avoir à gérer des null en plein milieu et de couper la logique fonctionnelle avec des batteries de if.

            D’ailleurs, la doc dit :

            API Note:

            Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

            Note aussi que, dès aujourd’hui, tu peux aussi utiliser l’un des trop nombreux frameworks qui permettent de gérer les null avec des annotations @Nullable / @NonNull. C’est juste moins intégré au langage lui-même.

            La connaissance libre : https://zestedesavoir.com

            • [^] # Re: Optional<T>

              Posté par  . Évalué à 3.

              Non, c’est plus vaste que ça, ça permet en particulier d’écrire des chaines d’appels complètes sans avoir à gérer des null en plein milieu et de couper la logique fonctionnelle avec des batteries de if.

              Ça n'est pas plus vaste, c'est un choix de vouloir continuer à avoir une API fluent un peut plus loin que les Stream. Je ne crois pas que le prix en vaille la chandelle. Avoir une API fluent ce n'est pas de la programmation fonctionnelle, ça vient à mon avis d'un mélange : la programmation fonctionnelle ne connaît que des expressions et pas d'instruction et éventuellement ils ont des monades quand ils ont besoin.

              Note aussi que, dès aujourd’hui, tu peux aussi utiliser l’un des trop nombreux frameworks qui permettent de gérer les null avec des annotations @Nullable / @NonNull. C’est juste moins intégré au langage lui-même.

              La JSR305 est sympa même si elle mériterait d'avoir un peu plus d'amour (de la part de ces concepteurs et des utilisateurs). Elle a l'avantage d'être utilisable dans bien plus de cas.

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: Optional<T>

        Posté par  . Évalué à 1.

        Je suis d'accord sur le fond - bien que je soie plutôt content de pouvoir chaîner des .map sur un Optional depuis Java 8 :)

        Dans le même ordre d'idée, j'adorerais un équivalent standard en Java du Result<T, E> de Rust. Peu probable au vu de la sacro-sainte rétro-compatibilité.

    • [^] # Re: Optional<T>

      Posté par  . Évalué à 4.

      même pas Optional autorise null…

      Il ne faut pas décorner les boeufs avant d'avoir semé le vent

      • [^] # Re: Optional<T>

        Posté par  (site web personnel) . Évalué à 4.

        Le compilateur pourrait traiter un pointeur comme un Optional<*,NULL> et vérifier que le cas NULL est bien filtré.

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

    • [^] # Re: Optional<T>

      Posté par  . Évalué à 4.

      euh, ouais, pas vraiment. Optional.get() peut te peter un NoSuchElement (unchecked en plus, la classe) et la difference entre myVar.isPresent() et myVar != null est quand meme vachement subtile.

      Sans compter que ca devient vachement meta quand tu te rends compte que ton optional peut être null, et qu'en fonction du contexte, tu vas potentiellement devoir null check ton optional.

      Linuxfr, le portail francais du logiciel libre et du neo nazisme.

  • # j'ai du mal à comprendre un truc ....

    Posté par  . Évalué à 4.

    Ne serait-il pas plus judicieux de traiter l'appel de méthode sur objet null via une exception ?

    • [^] # Re: j'ai du mal à comprendre un truc ....

      Posté par  (site web personnel, Mastodon) . Évalué à 4.

      Les exceptions rajoutent un chemin d'exécution, pas forcément simple à suivre malheureusement.

      Et ça rajoute de la complexité au niveau de l'appelant, là où justement on essaye de simplifier en utilisant une méthode.

      Pour les NPE le plus simple c'est l'opérateur ?. qui permet de proprement gérer l'éventuelle nullité. C'est en train d'arriver petit à petit dans beaucoup de langages.

      Et puis d'avoir une garantie de présence comme dans rust, ça se tente.

    • [^] # Re: j'ai du mal à comprendre un truc ....

      Posté par  . Évalué à 5.

      Ça n'est pas forcément un cas exceptionnel.

      Tu peux vouloir écrire :

      int size(String str) {
          return str == null ? 0 : str.size();
      }

      Mais sinon c'est une option à prendre en compte : ne rien faire et laisser l'exception arriver. Maintenant que les NullPointerException donnent (enfin !) des informations sur ce qui était null. Ça vaut le coup de laisser faire le langage naturellement.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: j'ai du mal à comprendre un truc ....

        Posté par  . Évalué à 4.

        Je suis peut-être puriste, mais retourner une taille de 0 pour un champ alors que l'objet est de type null, ça me pose problème. C'est pour ça que je préfèrerais gérer ça via exception … Mais c'est peut-être parce que j'ai fait trop d'Erlang dont la philosophie est de ne pas faire de programmation défensive mais de laisser planter (et avoir un superviseur qui relance le processus).

        • [^] # Re: j'ai du mal à comprendre un truc ....

          Posté par  . Évalué à 4.

          Null n'est pas un type en java.

          Ça m'embête supposé très doctrinal. Ça dépend complètement de la sémantique que tu donne. Si je compte la taille d'un message, je peux vouloir considérer que sa taille est nulle ou valant 0 qu'il s'agisse d'une chaîne vide, d'un champ absent ou d'un champ ayant pour valeur null ou undefine si c'est représenté par du json par exemple. Multiplier les chemins d'exécutions pour ça n'a pas de sens et planter pour relancer le processus est un bug : une valeur nulle n'est pas synonyme d'erreur.

          Penser que les choses ont un sens intrinsèque et que ton modèle est universel est à mon avis une erreur pas lié au langage ou à la stack utilisée.

          Tu peux vouloir éliminer les valeurs null en les représentants dans ton typage et c'est très bien, tu n'a plus à avoir ce genre de garde dans tes fonctions mais c'est pas une absence de programmation défensive, elle est déplacée aux entrées de ton programme. Mais ça ne me paraît pas pertinent si le langage ne permet pas de l'exprimer. Java ne le permet pas et il me semble qu'erlang non plus toute références peuvent être nil il me semble.

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: j'ai du mal à comprendre un truc ....

            Posté par  (site web personnel, Mastodon) . Évalué à 3.

            A quel point la notion est utile ou même obligatoire en informatique?

            Notamment au niveau base de données.

            Moi je jongle un peu trop avec des tables comportant des colonnes optionnelles par exemple… donc null :(

            • [^] # Re: j'ai du mal à comprendre un truc ....

              Posté par  (site web personnel, Mastodon) . Évalué à 4.

              À quel point la notion de null

            • [^] # Re: j'ai du mal à comprendre un truc ....

              Posté par  . Évalué à 4.

              A quel point la notion est utile ou même obligatoire en informatique?

              À moins que ça m'échappe je ne connais pas de langages de programmation commun1 qui ne l'implémentent pas il me semble. Mais il y a 2 manières de faire soit c'est une valeur qui est valide dans tous les types nullables (donc toute références en java, tout pointeur en C/C++, etc) soit c'est un type.

              Quand c'est un type généralement le langage permet de créer des types sommes ce qui permet de créer un type « entier ou null » et ça t'oblige à vérifier que c'est un entier avant de pouvoir utiliser ta variable. Je trouve ça plus élégant personnellement.


              1. Je ne classe pas Malbolge comme commun, ni les langages simulant une machine de Turing 

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

        • [^] # Re: j'ai du mal à comprendre un truc ....

          Posté par  . Évalué à 6.

          dont la philosophie est de ne pas faire de programmation défensive mais de laisser planter (et avoir un superviseur qui relance le processus).

          Le probleme c'est que les memes causes ont généralement les memes consequences, laisser le process planter et prier tres fort qu'il se banane pas apres avoir été relancé, c'est tres #yolo quand meme. Si la cause du bug c'est une race, ou un évènement ponctuel spécifique qui est null quand il devrait pas l'être, c'est une chose, et ton process va probablement revenir a la normal (ou pas), potentiellement au prix d'avoir atomise les autres transactions qui étaient potentiellement en cours dans le meme process (donc potentiellement, pas nul du tout).

          Mais si c'est un job qui process des données, ou profile utilisateur qui a un champ nul quand il devrait pas, tu peux relancer le processus autant de fois que tu veux, ca va pas mieux marcher apres le millionième relancement que la premiere fois.

          Soit ton typage comprends la notion de null, et tu force le call site lui meme a répondre a la question existentielle "une collection qui n'existe pas peut elle avoir une taille?", soit il le fait pas. Dans ce cas, t'as 2 options: declarer qu'une collection qui n'existe pas a une taille de 0, ou decider que c'est une exception (et ca, ca va vraiment pas arranger la lisibilité du code, parce qu'une exception c'est juste 2 goto empilé l'un sur l'autre qui portent un gros imperméable avec un faux nez et une fausse moustache sur celui du haut).

          Au sujet de la semantique, declarer qu'une collection nulle a une taille de 0 est loin d'être ouf. Si tu regardes la question sous l'angle "combien de patates sont dans ce sac a patates", effectivement, c'est mal définit quand le sac a patate n'existe pas.

          Mais la question réellement posée a 99% de chances d'être "De combien de patates suis-je en possession?", et ca enleve l'ambiguïté: tu ne possedes aucune patate si tu ne possèdes pas de sac a patates, ou si le sac a patates est vide. Et c'est a priori renforcé par le fait que si t'as ce probleme en premier lieu, t'es en mode fonctionnel, pas objet (sinon t'aurais le probleme du null avant meme de poser la question), et donc la question est littéralement "combien de patates?" pas "quelle est la taille de ce sac a patates?".

          Tu peux probablement trouver une variante de ce probleme qui ne se résoud pas en changeant l'énoncé de la question, mais dans ce cas ci, ca se résoud tres facilement.

          Linuxfr, le portail francais du logiciel libre et du neo nazisme.

          • [^] # Re: j'ai du mal à comprendre un truc ....

            Posté par  (site web personnel) . Évalué à 6.

            Le problème c'est que les mêmes causes ont généralement les mêmes conséquences, laisser le process planter et prier très fort qu'il se banane pas après avoir été relancé, c'est très #yolo comme même.

            oui :-)
            et ça a même un nom, cela s'appelle la méthode Shadoks : « plus ça rate, plus on a de chances que ça marche » /o\

          • [^] # Re: j'ai du mal à comprendre un truc ....

            Posté par  . Évalué à 3.

            […] potentiellement au prix d'avoir atomise les autres transactions qui étaient potentiellement en cours dans le meme process (donc potentiellement, pas nul du tout).

            En erlang les threads userlands sont appelés process (ouai je trouve pas que ce soit un super nommage) et je pense que ça de ça qu'il parlait donc ça ne devrait pas être aussi impactant.

            Tu peux probablement trouver une variante de ce probleme qui ne se résoud pas en changeant l'énoncé de la question, mais dans ce cas ci, ca se résoud tres facilement.

            Oui le nommage de ma fonction à l'arrache n'était pas génial et tu as mieux exprimé que moi ce que je voulais dire par le fait que c'est une question de sémantique.

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # Shell

    Posté par  (site web personnel) . Évalué à 6. Dernière modification le 14 octobre 2023 à 13:31.

    condition || return 1
    condition || exit 1

    (suivant les cas)

    • [^] # Re: Shell

      Posté par  . Évalué à 7.

      J'aime beaucoup perl pour ça :

      condition or die "Arg !";

      et je le reproduit en shell quand le cas se présente en écrivant ma propre méthode die()

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: Shell

        Posté par  (Mastodon) . Évalué à 4.

        Il y a une erreur, il faut écrire

        'Arrrrrrrrrg'

        En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

      • [^] # Re: Shell

        Posté par  (site web personnel, Mastodon) . Évalué à 2.

        Bah, kif-kif non ?

        perl ↔ shell
        condition or exitcondition || exit 1
        condition or die ""condition || return
        condition or die "message"condition || { echo "message" >&2 ; return }

        Je n’utilise une fonction de sortie dédiée que quand je veux faire des trucs chiadés (par exemple écrire dans un log en plus d’envoyer un message en couleur sur le terminal) et ou mutualisé (par exemple une série de tests pour fail early avec le même message, donc mode DRY)

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: Shell

          Posté par  . Évalué à 3.

          Exactement sauf que je n'utilise jamais de conjonction de bloque donc pas de {…} && {…} || {…} (c'est verbeux et piégeux, par exemple il manque un ; dans ton dernier exemple d'après mon dash 0.5.12) et j'évite les return en shell particulièrement parce que maintenant bash ne les accepte plus que dans certaines conditions.

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Shell

            Posté par  (site web personnel, Mastodon) . Évalué à 2.

            Ah on est d’accord sur le côté piégeux :) (pour la verbosité il y a moins de caractères que d’autres formes)
            Les return en fait c’est pour les fonctions utilitaires ; ça quitte la fonction (retour au point d’appel) alors que exit lui va quitter le script (ou le shell). Il est vrai que pour l’analogie c’est bancal car exit et die (avec quelque chose comme `! ||?>>8 || 255` si j’ai bonne mémoire) de perl arrêtent bien le script.

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: Shell

      Posté par  . Évalué à 4.

      Pour ce qui concerne les shells « classiques » (ie: pour simplifier de manière inexacte : sh, bash et ksh, voire zsh…) J’ai lu une fois qu’on pouvait toujours utiliser || ou && (avec les accolades qui vont bien si nécessaires) pour remplacer, de manière fort élégante je trouve aussi, l’utilisation d’un if (et des then et fi sans lequel il n’est rien qu’une erreur de syntaxe !). Il semblerait donc que ce soit toujours possible sauf dans le cas (pur ce que serait le plus simple qui ne soit pas faisable avec les deux opérateurs logiques && et ||) d’un :

      if … then … elif … then … else … fi

      Qu’en penses-tu ?

      Je n’ai jamais cherché à vérifier de peur de me faire de vilains nœuds indéboulonnables au cerveau droit, que ce soit en cherchant sur le web ou en essayant de réfléchir au problème.

      Sinon en dehors de ça, pour ce qui est est des constructions programmatique contre-intuitives mais souvent pratiques et concises, il y a l’utilisation du mécanisme de gestion des exceptions pour implémenter la logique du programme, c’est manifestement une chose souvent bien vue, en Python notamment, bien que ce langage se présente comme le plus « évident », le moins piégeux. Le try() … catch … n’a pourtant pas été créé comme une construction usuelle à la base !

      • [^] # Re: Shell

        Posté par  (site web personnel) . Évalué à 4.

        Qu’en penses-tu ?

        ça paraît jouable (mais faut-il jouer ?) :

        $ a=1;b=42; if [ $a -eq 1 ]; then echo "a";  elif [ $b -eq 1 ] ; then echo "b" ; else echo "c"; fi 
        a
        $ a=0;b=1; if [ $a -eq 1 ]; then echo "a";  elif [ $b -eq 1 ] ; then echo "b" ; else echo "c"; fi 
        b
        $ a=0;b=0; if [ $a -eq 1 ]; then echo "a";  elif [ $b -eq 1 ] ; then echo "b" ; else echo "c"; fi 
        c
        
        $ a=0;b=0; { [ $a -eq 1 ] && echo "a" ; } || { [ $b -eq 1 ] && echo "b" ; } || echo "c" ;
        c
        $ a=0;b=1; { [ $a -eq 1 ] && echo "a" ; } || { [ $b -eq 1 ] && echo "b" ; } || echo "c" ;
        b
        $ a=1;b=1; { [ $a -eq 1 ] && echo "a" ; } || { [ $b -eq 1 ] && echo "b" ; } || echo "c" ;
        a

        (shellcheck ne me dit pas de rejoindre l'enfer des syntaxes honteuses, donc ça doit être portable)

        • [^] # Re: Shell

          Posté par  . Évalué à 5.

          Malheureusement, ce n'est pas tout à fait équivalent, car si, par exemple, le echo "a" échoue, tu va exécuter l'alternative, ce que ne fait pas un if else.

          $ a=1
          $ if [ $a -eq 1 ] ;then ls --bad; else echo oups; fi
          ls: unrecognized option '--bad'
          Try 'ls --help' for more information.
          $ { [ $a -eq 1 ] && ls --bad ; } || echo oups
          ls: unrecognized option '--bad'
          Try 'ls --help' for more information.
          oups
      • [^] # Re: Shell

        Posté par  . Évalué à 2.

        Sinon en dehors de ça, pour ce qui est est des constructions programmatique contre-intuitives mais souvent pratiques et concises, il y a l’utilisation du mécanisme de gestion des exceptions pour implémenter la logique du programme, c’est manifestement une chose souvent bien vue, en Python notamment, bien que ce langage se présente comme le plus « évident », le moins piégeux. Le try() … catch … n’a pourtant pas été créé comme une construction usuelle à la base !

        J'ai pas compris ce que tu voulais dire.

        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

        • [^] # Re: Shell

          Posté par  . Évalué à 5.

          La construction try() … catch … est prévue pour gérer les exceptions, des trucs censés survenir exceptionnellement, en d’autres termes : ne jamais survenir si tout se passe bien. Ce dont je parle c’est le fait d’exploiter ce mécanisme pour des cas prévisibles et attendus. Mais c’est vrai qu’un exemple parlant serait le bienvenue. J’ai pas trop le temps là… Mais tentons toujours :

          Ce serait par exemple le fait de faire (pseudo code), au lieu d’un :

          if isint(userinput): then int_var=userinput; else tell_user_he_is_dumb()

          ceci :

          try: int_var=userinput; catch 'TypeMismatch': tell_user_he_is_dumb()

          Mais je sais pas si c’est plus clair ^^

      • [^] # Re: Shell

        Posté par  . Évalué à 5.

        J'ai mis trop de temps pour éditer mon message

        J’ai lu une fois qu’on pouvait toujours utiliser || ou && (avec les accolades qui vont bien si nécessaires) pour remplacer, de manière fort élégante je trouve aussi, l’utilisation d’un if (et des then et fi sans lequel il n’est rien qu’une erreur de syntaxe !). Il semblerait donc que ce soit toujours possible sauf dans le cas[…]

        Pour moi c'est un peu comme si tu trouve un vert très joli et que du coup tu repeins TOUT ton logement dans cette couleur du sol au plafond, fenêtres et vaisselles inclue. Il y a des cas où c'est très élégant d'utiliser ce genre de construction, mais quand on l'utilise là où elle n'a pas particulièrement de sens voir qu'elle oblige à faire des circonvolutions ça devient moche.

        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

        • [^] # Re: Shell

          Posté par  . Évalué à 4.

          Je suis entièrement d’accord. Le post de Benoît Sibaud illustre très bien ton propos, c’est proprement illisible. Le genre de code qui te fait maudire le développement, ainsi que le développeur qui a écrit ça ainsi que ses parents sur au moins dix générations.

          Je cherchais juste à vérifier le caractère strictement indispensable ou pas du if. J’ai bien écrit « pouvait «, pas « devait ». ;)

          wismerhill semble confirmer avec son exemple, on a bien un résultat différent de l’équivalent utilisant les opérateurs booléens donné par Benoît. En toute humilité je n’affirmerai pas qu’il n’est absolument pas possible de faire un équivalent avec des opérateurs logiques, je continuerai juste à continuer penser que c’est effectivement le cas (que c’est impossible).

          Par ailleurs, le if then else ne serait que sucre syntaxique, je pense qu’il serait toujours présent dans les langages de programmation, pour la raison que tu donnes.

          • [^] # Re: Shell

            Posté par  . Évalué à 4.

            Pour illustrer le "c'est pas parce qu'il existe déjà une façon de faire qu'on va pas en créer une autre", en perl, tu peux écrire :

            cond and b()
            cond && b()
            not cond or b()
            not cond || b()
            if (cond) b()
            b() if cond
            unless (not cond) b()
            b() unless not cond

            Je dois évidemment en oublier, il n'y a pas de forme intrinsèquement supérieure, mais elles tour à tour être plus pratique ou exprimer de manière un peu plus naturelle l'intention (naturelle pour celui qui code au moment où il code).

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

            • [^] # Re: Shell

              Posté par  . Évalué à 4.

              Sur ce point, Perl (le “Pathologically Eclectic Rubbish Lister” selon la page man ^^) est de loin le champion. L’intro de la documentation ($ perldoc perl) le dit explicitement : “The Perl motto is "There's more than one way to do it.””.

              C’est un langage que j’ai très peu utilisé (j’ai pas du en faire depuis deux ou trois ans, au moins), et dont je connais seulement le très très basique. Il y a deux particularités que j’ai retenues en commençant son apprentissage, c’est l’utilisation du principe des pointeurs, comme en C, c’est une gymnastique intellectuelle particulière je trouve, mais on s’y fait, et l’application à l’extrême du principe d’implicite. De mémoire il me semble qu’on peut écrire des trucs du genre for list print, ce qui permet d’écrire un code qu’on trouve soit d’une perfection ultime, soit d’une horreur absolue, mais qui ne laisse pas indifférent. :)

              Mais il permet aussi d’écrire un code atteignant un équilibre entre concision et clarté, et c’est ça qui en fait un excellent langage de script àmha.

              C’est strictement le contraire de Python, qui bien qu’il permette de faire la même chose de plusieurs manières, dans une moindre mesure que Perl c’est certain, considère qu’une seule syntaxe est « Pythonique », dans l’esprit du langage en quelque sorte, et qu’elle est donc la syntaxe fortement recommandée pour ceci ou cela. C’est logique étant donné que le but du langage est d’être facile à apprendre, à écrire et à relire. Il cherche l’homogénéité syntaxique pour favoriser la collaboration.

              Ceci dit il me semble que c’est avec Python (3) mais pas avec de Perl (5), qu’on peut utiliser n’importe quels caractères Unicode pour nommer les variables/fonctions/class/etc… ce qui peut probablement produire un code avec un fort aspect artistique et une lisibilité très relative ! ^^

              Perl est Python sont vraiment antagonistes à ce niveau là.

  • # continue

    Posté par  . Évalué à 8.

    continue est bien pratique aussi, pour passer à l’itération suivante en zappant le fin de la boucle pour l’itération en cours.

    • [^] # Re: continue

      Posté par  . Évalué à 3.

      À ce sujet, je conseille ce post de blog très pertinent sur l'usage de break, continue et return.

      • [^] # Re: continue

        Posté par  . Évalué à 8.

        Chouette billet, merci.

        On peut traduire cette phrase :

        Quand on s’intéresse à la compilation, on comprend rapidement que ce sont même là les constructions les plus simples à compiler : un simple saut vers un endroit statiquement connu, facilement identifié.

        par :

        GOTO 10 ❤️

        • [^] # Re: continue

          Posté par  (site web personnel) . Évalué à 2.

          Justement non.
          Sur ta ligne 10, il n'y a rien qui permette d'identifier que c'est l'arrivée d'éventuels sauts.
          Il faudrait un label.

          Pourquoi bloquer la publicité et les traqueurs : https://greboca.com/Pourquoi-bloquer-la-publicite-et-les-traqueurs.html

        • [^] # Re: continue

          Posté par  (Mastodon) . Évalué à 10.

          GOTO 10 ❤️

          Je fais partie des adeptes du goto en C. Justement c'est parfait dans le cas de gestion d'erreurs.

          En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

      • [^] # Re: continue

        Posté par  . Évalué à 4.

        return, ok, mais break/continue peuvent tres tres souvent (mais pas toujours!) être remplacés de façon bien plus elegante par une combinaison de map/flatMap/findFirst/filter/forEach/que sais je encore. Alors, ok, tout les langages n'ont pas ce genre d'api, mais quand meme java l'a depuis une décennie, on est en droit de penser que c'est une feature de base :)

        Sans forcement les interdire, les déconseiller me parait être une bonne idée.

        Linuxfr, le portail francais du logiciel libre et du neo nazisme.

        • [^] # Re: continue

          Posté par  . Évalué à 3.

          C’est bien possible qu’en Python ce soit considérer comme “non pythonic” et que les fonctions que tu site soit recommandée (ça reste à vérifier).

          Quand j’ai découvert map() ma vie a changé je dois l’avouer :)

          • [^] # Re: continue

            Posté par  . Évalué à 3.

            Python a plus ou moins 3 syntaxe il me semble :

            • impératif avec des boucles classiques
            • "fonctionnel" avec map/filter
            • comprehension

            Il me semble clair que le dernier est mis en avant sur les autres.

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # Bof

    Posté par  . Évalué à 9.

    Voilà, simple, efficace.

    Pour ce qui est de la simplicité et de l'efficacité, je préfère celle-ci :

        end

    C'est beau n'est ce pas?

    Pas vraiment. Je ne comprends pas comment on peut trouver belle une instruction unique qui ne trouve sa justification que par son contexte. Une routine, oui, une instruction, non.

  • # Tiré d'un script Perl/CGI…

    Posté par  . Évalué à 5.

    … que j'ai écrit il y a looooongtemps. C'était un script qui lisait une image générée dynamiquement et l'affichait pour le client HTTP de l'autre côté (il était invoqué par un script Perl/CGI parent).

    print while(<>);
    • [^] # Re: Tiré d'un script Perl/CGI…

      Posté par  (site web personnel, Mastodon) . Évalué à 3.

      Et qui était utile?

      Ça ne semble pas super clair comme ça :)

      • [^] # Re: Tiré d'un script Perl/CGI…

        Posté par  (site web personnel, Mastodon) . Évalué à 6.

        Si, si, c’est clair pour toute personne qui PERL :) <> : ce raccourci est très utilisé et connu dans le langage comme null filehandle ou diamond operator.
        C’est de toute beauté : on allie cela avec "action-simple [si] condition-simple" vanté par le journal. On peut bien sûr écrire cela en plusieurs lignes, mais bizarrement je n’ai croisé personne qui aime.

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: Tiré d'un script Perl/CGI…

          Posté par  . Évalué à 2.

          J'ai toujours beaucoup apprécié Perl pour ce genre de raccourcis.

          C'est sa plus grande qualité et son plus grand défaut à la fois.

      • [^] # Re: Tiré d'un script Perl/CGI…

        Posté par  . Évalué à 8. Dernière modification le 19 octobre 2023 à 10:49.

        Comme dit plus bas, <> est un raccourci pour dire <STDIN> : on lit l'entrée standard (qui est un blob binaire qui contient le GIF à afficher dans mon cas, et qui était généré dynamiquement), et on émet les valeurs sur la sortie standard tant qu'on n'a pas atteint EOF.

        En plus verbeux, on pourrait écrire :

        while (<STDIN>) {
            print;
        }

        En encore plus verbeux :

        while (<STDIN>) {
            print $_; # $_ est la variable qui récupère implicitement la lecture du file handle  en Perl.
        }

        En encore plus verbeux :

        while(my $line = <STDIN>) {
            print $line;
        }

        … Et pour un nouveau/une nouvelle venu-e en Perl, c'est sans doute un chouïa plus lisible, mais pas vraiment idiomatique, et franchement pénible pour qui comprend la syntaxe du Perl.

  • # C'est beau… presque autant que du fonctionnel

    Posté par  . Évalué à 3. Dernière modification le 18 novembre 2023 à 16:06.

    Ce que tu dis est la raison pour laquelle je trouve magnifique les langages fonctionnels.

    fib 0 = 1
    fib 1 = 1
    fib x = fib (x - 1) + fib (x - 2)

    Le code de chaque condition particulière est séparé complètement du reste.
    Chaque cas est visible d'un coup d'œil, même sans être trop réveillé.

Suivre le flux des commentaires

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