Journal Contrats et exceptions

Posté par  (site web personnel) .
Étiquettes : aucune
4
12
mar.
2009
Un très intéressant article dans 01 sur l'introduction de la programmation par contrat dans les langages .NET.
En passant, Sun va devoir faire vite évoluer Java, car celui-ci commence à prendre du retard niveau fonctionnalités sur C#/.NET : langage LINQ, closures (certains préfèrerai un type Block à la SmallTalk/Ruby/Lisaac/etc...)
C'est l'opinion de Betrand Meyer, créateur d'Eiffel qui est ici intéressant : celui-ci regrette le maintien du système d'exceptions, le qualifiant de mécanisme "brutal".
J'ai déjà parlé ici de notre discussion sur ce problème, lors de notre réunion annuelle du projet Isaac. Nicolas avait lancé ici même le débat il y a un peu plus d'un an
A première analyse, Mildred avait très bien analysé les avantages/inconvénient des deux systèmes (contrats et exceptions). Si on synthétise un peu, on arrive à la conclusion que :
  • Les contrats sont parfait pour vérifier la validité des données transmises aux méthodes, ie. les erreurs statiques. Elle permettent en outre une bonne documentation (comme le souligne à raison B. Meyer), et couvre une partie de la problématique de test de manière déclarative
  • Les exceptions sont un très bon mécanisme pour gérer les erreurs dynamique (impossible d'ouvrir un fichier, donnée extérieur attendue non disponible, etc...) Bref, toutes les erreurs venant du monde extérieur à propos desquels le programme ne peut pas grand chose
On oublie souvent la notion de contrôle d'invariant dans les contrats, qui ne sont pas que des asserts pre/post. Ils sont important pour la vérification du code, et dans sa thèse, J-C Filliâtre insiste sur le fait que ceux-ci sont essentiel pour la preuve de code.
C'est d'ailleurs pour cela que cet nouvelle feature, va permettre à krosoft de proposer de la vérification statique à la compilation, par model checking, ou peut être une espèce d'analyse de flot sur le code de la machine virtuelle (mais j'y crois moins).

Autre problème, évoqué à la fin de l'article : la détection des appels sur Null. Il y est fait référence du mécanisme de void safety d'Eiffel.
Je ne sais pas comment ça marche, et ce que cela permet vraiment de détecter, ni si cela implique de blinder le code contrats (google ne me donne pas grand chose).
Je vais encore parler de Lisaac (désolé...) puisqu'un mécanisme de détection d'appel sur Null très efficace y est implémenté.
On peut assimiler un Null à un type. Donc si l'on est capable de tracer tous les typages possibles dans tous les chemins d'exécution du code, on peut retrouver la très grande majorité des appels sur Null, le restant étant les cas où les informations sont insuffisantes pour les détecter (les erreurs dynamiques justement).
On a deux technique de prédiction de type :
  • Le lambda calcul de type, implémenté dans Objective Caml
  • L'analyse de flot implémenté dans Lisaac et (il me semble) Pypython
Ce genre de détection évite pas mal de tests à effectuer.


Mais revenons sur les contrats.
Dans l'hypothèse de Meyer, il n'y (quasiment) pas d'exceptions, et toutes les erreurs sont gérées par contrat.
Le problème du contrat (tapez moi si je me trompe), c'est qu'il pète sur place, il ne remonte pas.
Pas terrible pour gérer l'erreur, la faire remonter.
De même les exception en java (en C#, je ne sais pas, mais je suppose que c'est similaire) sont à mort subite, pas possible comme en smalltalk/Clisp de reprendre le traitement

Donc si on blinde la lib de contrats, on aura une belle erreur, soit un plantage, soit une log expliquant que le réseau est indisponible, le disque est plein etc...
Ca plante sur place, génial.
Si on a un mécanisme de rattrapage, voire de reéxecution, là ça change beaucoup de choses, car on revient en fait à un système hybride contrat+exceptions : l'erreur remonte et peu être gérée à haut niveau.
Et là, dans ce cas, un invariant devient une gestion d'exception particulière.

Conclusion, il faudrait peut être faire une synthèse des deux...
  • # Les 2

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

    Je pensais à un système de contrat locaux qui peuvent lever un "signal" (version Qt) géré par le langage. Par défaut, il y aurai juste un log.

    Cela veut dire inventer un système d'asynchronisme dans le langage. Un truc qui ressemble aux interruptions, au signaux posix, aux signaux/slot Qt, au call back de gui, etc...

    Il faut que cela soit simple et suffisamment générique pour que cela ne ressemble pas à une grosse verrue. Je pense à une version des signaux/slot de Qt avec une bonne gestion de l'espace des noms (histoire de pouvoir l'étendre par un réseau ensuite).

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

  • # Microsoft et analyse statique

    Posté par  . Évalué à 4.

    En passant :

    C'est d'ailleurs pour cela que cet nouvelle feature, va permettre à krosoft de proposer de la vérification statique à la compilation, par model checking, ou peut être une espèce d'analyse de flot sur le code de la machine virtuelle (mais j'y crois moins).

    Microsoft fournit déjà avec son kit de développement de pilotes un outil baptisé SLAM, combinant analyse statique par interprétation abstraite, démonstration automatique et model-checking pour détecter d'éventuelles erreurs dans le code du pilote. D'après ce que j'ai entendu dire, il s'agit d'une mise en pratique de l'état de l'art de ces différents domaines. D'après Wikipédia, il est (était ?) écrit en OCaml, langage développé en France à l'INRIA. Cocorico !

    * : http://research.microsoft.com/en-us/projects/slam/
    * : http://caml.inria.fr
  • # source

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

    Bon j'ai pas lu le 01 en question, mais je suppose que le journal parle de ca :
    http://blogs.msdn.com/somasegar/archive/2009/02/23/devlabs-c(...)
    http://research.microsoft.com/en-us/projects/contracts/
    Faut bien voir que c'est plus un hack qu'autre chose à mon sens, mais qui a l'avantage d'être utilisable avec le C# d'aujourd'hui.
    Sinon MS fait déjà ca depuis un moment dans Singularity qui utilise une variante du langage C# qui intègre le même type de principe au coeur du langage (Sing# basé sur Spec#).
  • # pas possible ?

    Posté par  . Évalué à 4.


    De même les exception en java (en C#, je ne sais pas, mais je suppose que c'est similaire) sont à mort subite, pas possible comme en smalltalk/Clisp de reprendre le traitement


    Je comprends mal un truc ? En java l'interêt des catch c'est justement de capturer une exception pour en faire ce que tu veux non ?
    Tu peux la remonter ou bien la gérer sur place, comme un contrat (de ce que tu en décris, je ne sais pas ce que c'est)
    • [^] # Re: pas possible ?

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

      Ce que je ceux dire, c'est que tu ne peux pas relancer le code qui a provoqué une erreur.
      Dans le lien que je donne ( http://fr.wikipedia.org/wiki/Syst%C3%A8me_de_gestion_d%27exc(...) ) dans le journal, il est montré comment on fait de la reprise d'erreur sur une exception en smalltalk : tu peux retenter l'exécution de ton code avec des params différents, après un appel, etc...
      En java, tu catch ton erreur, mais à moins de rappeler la fonction (et donc pas le bout de code), tu est obligé d'assumer qu'il y a erreur et de donner une voie différente à ton flot... ou de faire un traitement, mais continuer..

      « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

      • [^] # Re: pas possible ?

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

        En même temps tu propose de faire des choses : retenter l'exécution, continuer l'exécution, etc. Autant dire que tu sais quoi faire. C'est donc pas un comportement inattendu, et ca ne doit pas être géré avec des exceptions, même en Java ou ce que tu veux.
        Le principe des exceptions, c'est de sortir de ton flot d'exécution courant parcque tu ne sais justement pas comment traiter le problème.
        Après y'a un principe d'encapsulation en objet, qui fait que tu n'est pas censé savoir connaître le déroulement exact de telle ou telle méthode, ce qui t'intéresse c'est son contrat. Donc si cette méthode lève une "exception", je trouve ca vraiment dangereux de dire "vas-y continue c'est pas grave", sans savoir exactement ce que faisait la méthode en question et les implications sur la suite du flot d'exécution.
        J'aimerai vraiment un exemple concrêt, parcque j'ai du mal à cerné quels cas vous voulez gérer.
        • [^] # Re: pas possible ?

          Posté par  . Évalué à 2.

          Moi j'aime bien dans le principe.
          L'exemple smalltalk est pas mal. En java je propose de saisir un nom de fichier pour l'ouvrir. Sur un FileNotFoundException je repropose de saisir un nouveau nom de fichier ad vitam eternaem. Sur un autre type d'erreur je plante.
          Je suis sûr qu'en cherchant on doit pouvoir trouver des exemples encore plus pratique. Ca revient à utiliser l'exception pour tester des états mais je ne pense pas que ça consomme plus de cpu ou de mémoire.
          De plus ce mécanisme ne se substitut pas aux exceptions mais les complète.
          • [^] # Re: pas possible ?

            Posté par  . Évalué à 3.

            Un autre exemple (je suis inspiré moi).
            J'ai ouvert une socket. J'écris dedans, pour une raison x ou y elle se ferme. Mon écriture plante. En interceptant l'exception je relance la connexion à la socket et si ça réussi je reprends mon écriture LA où elle s'est arrêté (au moins 2 3 fois, après je plante). J'ai une tolérance à l'erreur ou à une micro interruption de mon réseau.
            L'exmple est peut-être mal choisi en fait, mais c'est parlant.
            • [^] # Re: pas possible ?

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

              C'est extrêmement dangereux, tu sais pas si une partie de ton écriture s'est passé correctement ou non, quand tu vas redemander l'écriture de tes datas sur la socket, si ca se trouve tu vas répéter une séquence qui avait déjà été envoyé...
              Et puis je vois pas ce qui t'empêche de faire :

              for(int try= 0; try <= maxTry && !socket.Send(data); try ++)

              y'a pas de gestion d'exception, et y'a pas besoin d'autre chose... j'ai l'impression que vous voulez remplacer les exceptions dans les cas où de toute façon on les utilise pas...
          • [^] # Re: pas possible ?

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

            Sur un FileNotFoundException je repropose de saisir un nouveau nom de fichier ad vitam eternaem..
            Ben oui mais non, les exceptions ne doivent pas être utilisées comme ca, même en Java. Si tu sais qu'il faut proposer un autre nom, tu fais autre chose :
            on_closing(file)
            {
            return !File.Exists(file);
            }
            Il ne faut surtout pas utiliser les exceptions comme traitement courant. Ca doit rester des cas "non prévus" où tu sais pas quoi faire.
            Donc pour moi c'est pas un bon exemple.
        • [^] # Re: pas possible ?

          Posté par  . Évalué à 2.

          Un exemple concret en Eiffel. Quand j'ouvre des fichiers qui sont censés exister, dans le cas où justement ils existent, il arrive que le système d'exploitation échoue à les ouvrir. Ma procédure d'ouverture est munie d'une clause de sauvetage qui permet plusieurs tentatives avant d'abandonner ou de retenter la chose différemment.

          J'ai des clauses de ce genre aussi pour la gestion des dll externes à mes programmes et en général pour la gestion des résultats dont la fourniture est externe à mes programmes.
          • [^] # Re: pas possible ?

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

            Ben je vois pas pourquoi t'aurais besoin d'un truc particulier pour ca :
            for(int try= 0; try <= maxTry && !openFile(myFile); try ++)

            Evidemment, ca suppose que tes APIs soient bien fait.
            Et c'est d'ailleur une préconisation de design classique dans les bibliothèques : toujours proposer à l'utilisateur un moyen de tester de faire quelque chose sans passer par des exceptions.

            Exemple dans la lib C# :
            Int32.Parse("azerty") => à utiliser quand j'ai pas envie de traiter l'erreur ou que je sais pas quoi faire, lèvera une exception
            si je sais quoi faire :
            if(!Int32.TryParse("azerty", out myInt))
            //je traite le cas particulier qui n'est pas une exception puisque j'ai une règle de traitement pour ce cas.
            • [^] # Re: pas possible ?

              Posté par  . Évalué à 2.

              Tiens, cette discussion ça me rappelle un peu la différence Unix / autre OS d'une ancienne époque que j'ai pas connu : "on" disait que certains systèmes voulaient abstraires la complexité de la gestion d'erreur de certaines tâches, contrairement à unix où on te renvoyait un code d'erreur et c'est à toi de te démerder. Aujourd'hui, on voit qui a gagné : unix.

              Bon, à un moment, il y a bien un niveau qui va abstraire toute cette merde de la gestion d'erreur, le truc c'est de trouver à quel niveau c'est bien.

              (désolé si ce commentaire paraît un peu hors propos ...)
              • [^] # Re: pas possible ?

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

                Euh tu connais beaucoup de bibliothèques modernes ou langages modernes qui utilise des codes d'erreur ? Moi non, je dirais que sur ce point c'est un héritage du langage C qui n'avait pas de gestion d'exception, et on voit que c'est tout sauf Unix qui a gagné.
                • [^] # Re: pas possible ?

                  Posté par  . Évalué à 1.

                  Euh tu connais beaucoup de bibliothèques modernes ou langages modernes qui utilise des codes d'erreur ?

                  Ben oui au moins Eiffel que je connais un peu. La Bibliothèque GOBO propose dans KL_EXCEPTION une catégorisation des exceptions. Elle permet aussi d'engendrer ses propres exceptions, pour décider que tel événement se produisant dans tel contexte est anormal et doit lever une exception.
                • [^] # Re: pas possible ?

                  Posté par  . Évalué à 2.

                  Bon, en fait il faut se situer dans un état intermédiaire : oui, les langages de plus haut niveau ont abstrait ça, mais au niveau de l'OS c'est toujours pareil. Je ne sais toujours pas quelle est la meilleure séparation, vu que je dirais que tu as un avis un peu biasé sur dot Net, mais c'est quelque chose qu'aujoudr'hui on semble avoir tranché dans les langages à base de VM, alors qu'au final je ne sais pas vraiment quelle solution est la meilleure ...
                  • [^] # Re: pas possible ?

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

                    Ce qui est sûr, c'est que les codes d'erreurs "bas-niveau" ala C constitué d'un simple entier sont largement insuffisant sémantiquement. Quelque soit le mécanisme qu'on utilise, je penses que tout le monde s'accordera à dire que c'est insuffisant. Donc bon dire qu'Unix a gagné sur ce point...
            • [^] # Re: pas possible ?

              Posté par  . Évalué à 1.

              C'est du Eiffel, pas du C ou du C#. Mets-toi dans le cas ou openFile viole un contrat et lève une exception.

              Cette manière de gérer les exceptions se trouve dans Object Oriented Software Construction de Bertrand Meyer (première et seconde édition) qui, il est vrai, ne sait pas bien faire les API.
              • [^] # Re: pas possible ?

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

                Ben c'est débile de décider que c'est une violation de contrat et une levée d'exception.
                C'est une entrée utilisateur, il peut sélectionner un fichier où il n'a pas les droits d'accès, c'est un cas normal qui n'a rien d'exceptionnel. Si l'API est mal faite et permet pas d'éviter ce contrat/levée d'exception, c'est un autre problème, qui ne se corrigera pas dans le langage.
                Ou alors on considère que c'est un comportement inattendu, auquel cas on ne sait pas comment traiter le problème (puisqu'on s'y attend pas), et on se bouffera une exception, qui est et doit être violente.
      • [^] # Re: pas possible ?

        Posté par  . Évalué à 2.

        Ouah !! En smalltalk, tu peux utiliser un goto ? Dingue
  • # Faire évoluer java…

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

    Oui enfin si c'était vraiment les fonctionnalités et la productivité d'un langage que faisait qu'il est beaucoup utilisé, ça se saurait.

    Le décideur, qui ne code généralement pas (plus?), il décidera généralement sur les conseils avisé des magasines de décideurs et en faisant comme tout le monde fait. Le gars qui code se contente en général de faire comme le patron à dit.

    De plus, même avec des mécanismes hyper top moumoute, tu as aucune garantie que tous les programmeurs du monde se mettent subitement à ce soucier de faire de la programmation par contrat alors que le matin même ils faisaient une grosse bouillie de html/php bien grouïk, farci au jquery pour tout et surtout n'importe quoi. D'autant que c'est pas le décideur qui va payer la formation (sauf obligation légale tout ça).

    Bref, «Sun va devoir faire vite évoluer Java», mouais, je dit pas qui faut pas le faire, mais je dirais pas non plus que c'est la clef de l'adoption en masse d'un langage, qui à sans doute bien peu de relation avec ses vertus technique.
    • [^] # Re: Faire évoluer java…

      Posté par  . Évalué à 3.

      Il me semble que la stratégie de Sun est justement d'éviter d'introduire les dernières fonctionnalités à la mode sans une longue réflexion dans Java et de préférer les implémentations via des bibliothèques (programmation par contrat => JContractor, LINQ => Quaere, HQL...).

      Java est sur la voie de la stabilité tandis que .NET préfère la course à l'innovation.
  • # Puisqu'on y est

    Posté par  . Évalué à 3.

    Le feu magazine "Developpeur Reference" avait pondu un bon article sur les contrats en comparant les langages.
    Il est récupérable ici
    (PDF de 2 Mo)
    http://web.archive.org/web/20020816124043/http://www.devrefe(...)
    Ca date un peu, mais on y découvre que si on veut faire des contrats avec Java, c'est possible:
    JContractS (ex iContract)
    http://jcontracts.sourceforge.net/
    JContractor
    http://jcontractor.sourceforge.net/

    Certes, leur prise en charge n'est pas aussi bien foutue qu'en Eiffel mais elle a le mérite d'exister
    Et voici le sommaire des mags
    http://web.archive.org/web/20020816124043/http://www.devrefe(...)

Suivre le flux des commentaires

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