• [^] # Re: Sûreté

    Posté par (site web personnel) . En réponse à la dépêche GHC 9.2. Évalué à 10.

    Merci pour cette question, en effet cela demande des précisions.

    En premier lieu, "mon" format n'est pas plus sûr que le format de python, c'est le même format. Plus exactement, un sous ensemble.

    La réponse de barmic est un cas extrême d'exploitation du coté dynamique de python. Mais j'avoue que je ne pensais même pas à cela en rédigeant ce paragraphe.

    Je parlais de "sûreté" au sens "typage".

    En pratique, le formatage de chaîne python est réalisé dynamiquement : lors de l’exécution, l’interpréteur python va construire la chaîne de formatage, interpréter celle-ci et y insérer les différents expressions / valeurs nécessaire. À ce moment, il peut échouer (et lever une exception) si le formatage demandé n'a pas de sens.

    Par exemple, si le type à formater n'est pas compatible avec le formater, ici une chaîne à formater en octal :

    >>> name = "Guillaume"
    >>> f"Bonjour {name:o}"
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    ValueError: Unknown format code 'o' for object of type 'str'

    Ou si la syntaxe du formater est incorrecte ou incohérente :

    >>> f"Bonjour {name:l}"
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    ValueError: Unknown format code 'l' for object of type 'str'
    >>> f"Bonjour {3:0.3d}"
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    ValueError: Precision not allowed in integer format specifier

    Cette erreur arrive lors de l’exécution.

    De son coté, PyF fait ses vérifications lors de la phase de vérification des types, avant l’exécution.

    Prelude PyF> name = "Guillaume"
    Prelude PyF> [fmt|Bonjour {name:o}|]
    <interactive>:5:6: error:
      No instance for (Integral [Char])
     arising from a use of PyF.Internal.QQ.formatAnyIntegral
      In the second argument of (<>), namely
     ((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal)
     PyF.Formatters.Minus)
     Nothing)
     Nothing)
     name
     In the expression:
     (("Bonjour "
     <>
     ((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal)
     PyF.Formatters.Minus)
     Nothing)
     Nothing)
     name))
     In an equation for it:
     it
     = (("Bonjour "
     <>
     ((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal)
     PyF.Formatters.Minus)
     Nothing)
     Nothing)
     name))
    Prelude PyF> [fmt|Bonjour {name:l}|]
    <interactive>:7:6: error:
      <interactive>:1:15:
     |
    1 | Bonjour {name:l}
     | ^
    unexpected 'l'
    expecting '#', '%', '+', '-', '.', '0', 'E', 'F', 'G', 'X', 'b', 'c', 'd', 'e', 'f', 'g', 'n', 'o', 's', 'x', '}', integer, or space
      In the quasi-quotation: [fmt|Bonjour {name:l}|]
    Prelude PyF> [fmt|Bonjour {3:0.3d}|]
    <interactive>:6:6: error:
      <interactive>:1:15:
     |
    1 | Bonjour {3:0.3d}
     | ^
    Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
      In the quasi-quotation: [fmt|Bonjour {3:0.3d}|]

    Ici j'ai exécuté les exemples dans l’interpréteur Haskell, mais je vous demande de me croire que l'erreur est bien une erreur "statique" détectée pendant la compilation et non lors de l’exécution.

    De plus, les erreurs de PyF apportent plus de contexte (notez le petit marqueur ^ qui montre précisément la localisation du problème.) Ce ne fut pas une tache aisée, et c'est loin d'être parfait, mais cela permet tout de même de voir l'erreur pendant la phase de développement et non lors de l’exécution.

    D'ailleurs, pour rentrer dans les détails, PyF peut échouer dans 2 contextes différents.

    • Lors du parsing du la chaîne de formatage ou l'analyse de celle-ci. A ce moment ne sont testé que la cohérence de la chaîne de formatage, et une erreur très précise peut être générée par la libraire.
    • Passé cette étape, la libraire PyF génère du code Haskell et le compilateur va tester la cohérence des expressions. Ici PyF n'a plus aucun contrôle et les erreurs remontées par le compilateur peuvent être un peu complexe à lire, comme ici le No instance for (Integral [Char]) qui veut simplement dire qu'une chaîne de caractère n'est pas convertible en nombre entier. Il y aurait moyen d'améliorer cela un petit peu, il faut que j'y passe un peu de temps, mais il est possible en Haskell de contrôler les message d'erreur des erreurs de type.

    Bref, au final, PyF apporte, comparé à Python, une partie de vérification "statique" lors de la compilation. C'est à mon avis appréciable, après tout, c'est ce que j’apprécie en Haskell, mais ce n'est pas l'apport le plus intéressant qui reste la partie formatage, et là, PyF n'apporte rien de mieux que ce que python propose en standard.