Voici un exemple de code minimal que j'utilise pour expliquer mon problème. Le code suivant est organisé en deux fichiers et compile bien:Comment F # infère-t-il les types et les balises des autres modules?
DataStruct.fs
module MyMod
type XXX = {
a: int
}
with
static member GetNew =
{
a = -1
}
type YYY = {
a: float
}
with
static member GetNew =
{
a = -1.0
}
type Choice =
| XXX of XXX
| YYY of YYY
Program.fs
open MyMod
let generator =
let res = XXX.GetNew
Choice.XXX res
let myVal : XXX =
match generator with
| XXX x -> x
| _ -> printfn "expected XXX, got sth else!"; XXX.GetNew
La chose intéressante est que j'ai un type de choix qui a deux balises nommé de la même manière que les types qu'ils marquent. C'est, d'après ce que je comprends, une convention commune en F #.
Maintenant, je modifie DataStruct de telle sorte que je l'ai placé dans un espace de noms et que MyMod soit l'un des modules de cet espace de noms. En conséquence, dans Program.fs j'ouvre l'espace de noms et d'utiliser tout préfixé avec le nom du module:
DataStruct.fs
namespace DataStruct
module MyMod =
type XXX = {
a: int
}
with
static member GetNew =
{
a = -1
}
type YYY = {
a: float
}
with
static member GetNew =
{
a = -1.0
}
type Choice =
| XXX of XXX
| YYY of YYY
Program.fs
open DataStruct
let generator =
let res = MyMod.XXX.GetNew
MyMod.Choice.XXX res
let myVal : MyMod.XXX =
match generator with
| MyMod.XXX x -> x
| _ -> printfn "expected XXX, got sth else!"; MyMod.XXX.GetNew
maintenant Program.fs contient deux erreurs . Dans les deux lignes où j'essaie d'appeler GetNew, il est écrit: "Le champ, le constructeur ou le membre 'GetNew' n'est pas défini" Ceci est dû au fait que MyMod.XXX est inféré comme étant un cas de type MyMod.Choice. Maintenant, sans changer la structure de mon code, je renomme simplement les tags Choice pour qu'ils soient différents des types qu'ils représentent et tout fonctionne correctement.
DataStruct.fs comme ci-dessus, mais avec
type Choice =
| TX of XXX
| TY of YYY
Program.fs
open DataStruct
let generator =
let res = MyMod.XXX.GetNew
MyMod.Choice.TX res
let myVal : MyMod.XXX =
match generator with
| MyMod.TX x -> x
| _ -> printfn "expected XXX, got sth else!"; MyMod.XXX.GetNew
Maintenant, l'appel à GetNew est légal puisque le MyMod.XXX est déduit correctement le type que je voulais utilisation.
La question est maintenant: le problème décrit ci-dessus est-il un bug ou une caractéristique de F #? Autrement dit, s'il est conseillé d'utiliser les mêmes noms pour les étiquettes et leurs types, cela semble être un problème pour le mécanisme d'inférence de type. Est-ce que le conseil est mauvais ou est-ce que j'utilise des espaces de noms, des modules, des types et des étiquettes dans un mauvais chemin?
Merci pour l'effort, j'espère que ce sera corrigé bientôt. Et je suis un peu surpris qu'il n'y ait pas eu beaucoup de gens qui ont trébuché sur ce sujet, car une telle utilisation d'une union discriminée apparaîtra fréquemment dans F #, je crois. –
@FriedrichGretz: merci pour la confirmation. Satisfaisant pour la prime? –