Forum Programmation.autre [OCaml] Typage complexe et type "Block"

Posté par (page perso) .
Tags : aucun
1
7
mar.
2011

Bonjour, j'implémente en ce moment une machine à état hiérarchique en OCaml. Une machine à état hiérarchique est une machine à état dont chaque état peut contenir une machine à état.
Cette machine à état permet de contrôler le comportement d'un agent.

Je tente de réaliser cela avec un mix d'objet et de type somme en OCaml. J'ai donc une classe agent et une classe state qui elle représente mon état. State a un état parent (celui qui le contient), un état enfant (la machine à état qu'il contient et l'état de "départ" auquel il est lié). On y a diverses actions qui feront l'objet de la question 2. Les transitions permettent de construire un arbre composant des conditions et des évènements.

class agent = 
object
  val mutable startState = new state;
end


and


 transition = Condition of (agent -> bool) | 
Event of (agent -> bool) |
ConditionOr of  (agent -> bool) |
ConditionAnd  of  (agent -> bool) |
ConditionXor of   (agent -> bool) |
ConditionNor of   (agent -> bool)

and 
 stateOuRien = State of state | None


and
  state = 
object
 val mutable parentstate = State( new state);
 val mutable substate = State( new state);
 val mutable transitions  = [];
 val mutable begin_action = (function a-> a#startState = new state);
  val mutable action  = (function a-> a#startState = new state);
  val mutable end_action  = (function a-> a#startState = new state);
end;;

Première question : J'ai une belle syntaxe error.. Le problème est que mes définitions de type sont tous interdépendantes mais que je ne parviens pas à faire en sorte de lui faire manger tout cela d'un seul coup. Y a t-il une astuce ?

Seconde question : Je suis en train de lui imposer une notion que je pense un peu limite. En effet, mon

val mutable end_action  = (function a-> a#startState = new state);
correspond au type block qu'on trouve dans les langages objets avancés (Smalltalk, Ruby, Lisaac, ...). En gros, c'est une propriétés de ma classe dans laquelle j'aimerai affecter/changer une fonction quand bon me semble.

#

Question subsidiaire : j'ai du mal à lui faire comprendre quel type je veux exactement pour mes propriétés, s'il y avait un moyen de simplement lui définir un type (propriétés : type ) comme dans les langages objets classique, je suis preneur..

Merci !

  • # Ouh là

    Posté par (page perso) . Évalué à 3.

    Bon, allons y doucement :)

    Tout d'abord, ton souci de compilation est dû au fait que OCaml n'aime pas mélanger les classes et et les types. De manière générale, évite les classes si tu n'utilises pas l'héritage, et utilise plutôt les record. De cette manière, tu pourrais écrire:

    type agent = 
        {
          mutable startState : state 
        }
    and transition = Condition of (agent -> bool) 
                     | Event of (agent -> bool) 
                     | ConditionOr of (agent -> bool) 
                     | ConditionAnd  of (agent -> bool) 
                     | ConditionXor of (agent -> bool) 
                     | ConditionNor of (agent -> bool)
    and state = 
        {
          mutable parentstate : state option;
          mutable substate : state option;
          mutable transitions : state list;
          mutable begin_action : state -> state;
          mutable action : state -> state;
          mutable end_action : state -> state
        }
    Note que je ne suis pas tout à fait sûr de tes types pour state, j'ai mis ce qui me paraissait logique. Le type option de la bibliothèque standard remplace avantageusement ton stateOuRien.

    J'espère que cela répond également à la question subsidiaire: quand bien même l'on peut gérer la plupart de ses types implicitement, il est pratique de définir tout d'abord ce sur quoi on travaille, à l'aide de records et de variants. Il est ensuite possible par exemple de forcer un type dans une fonction, par exemple:

    let run_state (a:state) = a
    sera typée comme fun : state -> state au lieu du générique fun : a ->a. Cela peut notamment rendre les messages d'erreur plus explicites et moins ésotériques.

    Pour la question 2, je sèche, je ne comprends pas ce qu'est un type bloc (je suis un programmeur un peu old school :) ). Si tu as un record, tu peux le modifier directement:

    let a =
      {
        parentstate = None;
        substate = None;
        transitions = [];
        begin_action = (fun e -> e);
        action = (fun e -> e);
        end_action = (fun e -> e)
      };;
    
    a.transitions <- [a];;
    C'est autre chose que tu cherches?

    De manière générale, pour s'en sortir en Ocaml, mieux vaut une approche bottom-up:

    • Créer les types dont on a besoin, à partir de records et de variants, en tentant de les garder aussi non-mutables que possible, et laisser de côté l'objet tant qu'on est sûr de ne pas en avoir besoin
    • Construire les fonctions qui vont travailler sur ces types, en partant du plus basique, et en testant souvent
    • Augmenter la complexité en écrivant des fonctions de plus haut niveau, s'appuyant sur les plus basses.

    Bonne chance!

    • [^] # Re: Ouh là

      Posté par (page perso) . Évalué à 2.

      Merci beaucoup, tant pour l'aspect technique que le conseil méthodologique.
      Pour répondre à ta question, un type block, c'est une variable qui est typé pour recevoir une fonction. Et comme toute variable tu peux lui mettre ce que tu veux n'importe quand.

      mutable begin_action : state -> state;
      
      begin_action est un block :-)

      Je pense faire d'avantage de record, surtout si je peux y coller des fonctions, mais j'ai un problème avec l'agent. Ce serait vraiment pratique de pouvoir hériter de l'agent.

      A la limite, je pourrai créer une classe qui sera de la colle sur le record.

      Je risque d'avoir d'autres questions !

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

Suivre le flux des commentaires

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