Est-il possible de définir des types qui dépendent les uns des autres et sont définis dans des fichiers séparés?

StackOverflow https://stackoverflow.com/questions/3983509

Question

Je suis en train de mettre en œuvre une bibliothèque avec des capacités d'analyse étendues. J'ai décidé que je vais utiliser fsyacc parce que je savais de l'université. Malheureusement, je rencontrais le problème suivant.

Je défini une classe pour la tête de ma grammaire ( Head ) et placer sa mise en œuvre dans un seul fichier. analyseur alors je défini comme:

...
%start head
%type <Head> head
...

Fsyacc génère le module seeparated ( Parser ). Pour réussir, il doit être compilé en ordre suivant: Head.fs Parser.fs

Afin de rendre cette bibliothèque semblable à ce que vous pouvez trouver dans .NET Je voudrais ajouter une statique Parse méthode Head . Malheureusement, je aurais besoin d'utiliser des méthodes de Parser le module .

Je sais que ces Type dépendances peuvent être résolus avec et 'opérateur mais il est uniquement applicable aux types définis dans un fichier.

Y at-il une autre façon de créer des types qui dépendent les uns des autres, même quand ils sont dans des fichiers séparés? Je cherchais mécanisme de séparation déclaration / mise en œuvre comme celle en C / C ++, mais je ne pouvais « quoi que ce soit t find.

Était-ce utile?

La solution

Réponse courte: non. Il n'y a pas moyen de faire des entités mutuellement récursives plusieurs fichiers en F # 2.0. (Ceci est quelque chose que nous prévoyons d'aborder dans la prochaine version de la langue.)

Vous pouvez contourner ce dans une variété de façons, en utilisant généralement un point de indirection et de mutation. Par exemple, votre type Head pourrait avoir une statique méthode « InitializeParser » qui pousse une valeur de fonction dans une variable globale mutable, puis la méthode Parse statique définie dans la tête pourrait appeler via ce mutable global, et après l'analyseur est réellement défini, il peut aller appeler InitializeParser pour pousser la valeur. (Si cela n'a pas de sens, je peux le préciser plus en détail.)

Autres conseils

J'espérais qu'il est possible. Après avoir lu la réponse de Brian je commencé à chercher une solution appropriée. Je ne voulais pas forcer les utilisateurs bibliothèque d'appeler toutes les méthodes d'initialisation. Par conséquent, je suis venu avec diffrent quelque chose.

Si le compilateur ne peut pas résoudre les dépendances à la compilation que je peux le faire moi-même au moment de l'exécution. Voici la définition de mon DepencendciesResolver

module DependenciesResolver = 
    let GetMethod(_type, _method) =
        lazy (
            let a = System.Reflection.Assembly.GetExecutingAssembly()
            let t = a.GetType(_type)
            t.GetMethod(_method)
            )

Et par exemple des classes définies dans les fichiers séparés:

A.fs

namespace MutualRecursion
type A() =
    static member _b = DependenciesResolver.GetMethod("MutualRecursion.B", "b")
    static member b() = A._b.Value.Invoke(null, [||])

B.fs

nameespace MutualRecursion
type B =
    static member b() = 
        printf "Called b()"

Compilation ordre est: A.fs B.fs

Tu ne peux pas travailler autour de ce fichier avec un 3ème qui compile après ces deux et étend la tête avec la nouvelle méthode?

Je ferais quelque chose comme ce qui suit (que je soupçonne est à peu près ce que Brian proposait). Notez que le utilisateur ne doit pas faire une initialisation délicate -. Les types eux-mêmes savent comment « attacher le noeud »

Head.fs

type IParser =
  abstract Parse : string -> int // or whatever
  ...

type Head() =
  static let mutable parser = Unchecked.defaultof<IParser>
  static member internal SetParser p = parser <- p
  member x.DoSomethingCool() =
    let i = parser.Parse("something to parse")
    ...

Parser.fs

type Parser private () =
  static do
    Head.SetParser (Parser())
  interface IParser with
    member x.Parse s = 0
    ...
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top