2017-09-26 2 views
3

Je veux avoir un type spécifique d'un argument dans une fonction F #:F arguments # fonction annotation de type ne fonctionne pas

type NewType = string*int 

let testFunction (arg1:NewType) : NewType = 
    ("resultString", 2) 

testFunction ("test", 3) 

Je veux que le type de la fonction à:

NewType -> NewType 

Mais le type de fonction est:

string*int -> NewType 

Que dois-je faire pour appliquer le type de arg1 argument pour être « NewType »?

Répondre

4

type NewType = string * int est ce qu'on appelle une abréviation de type. Il donne un nom ou un alias à un autre type, mais il est effacé lors de la compilation. Il n'y a pas d'encapsulation, pas de nouvelle référence et fondamentalement pas de nouveau type du tout.

Il peut servir de documentation, mais le compilateur F # traite le nouveau nom et le type d'alias de la même manière. Ceci est particulièrement visible si vous créez une DLL avec une fonction/méthode publique qui utilise NewType et essayez de l'appeler à partir d'un autre projet - vous verrez des résultats mixtes comme dans votre cas.

Ceci peut ne pas poser de problème si ce que vous voulez réaliser est juste une meilleure lisibilité. Si je vois let testFunction (arg1:NewType) : NewType = ... dans le code, surtout sur le web comme sur GitHub, où il n'y a pas d'info-bulles IntelliSense, cela me donne une bonne idée de ce que la fonction prend et retourne, même si le vrai type est string * int.

Si vous voulez une meilleure sécurité de type, la pratique habituelle consiste à définir un seul cas de discrimination syndicale que vous pouvez combiner avec un motif correspondant à ceci:

type NewType = NewType of (string * int) 

let testFunction (NewType arg1): NewType = 
    NewType ("resultString", 2) 

testFunction (NewType ("test", 3)) 

Vous pouvez en lire plus ici, et dans d'autres articles de la série: https://fsharpforfunandprofit.com/posts/type-abbreviations/

+1

Mais pourquoi le type de retour est "NewType", pourquoi fonctionne-t-il avec le type de retour et non avec l'argument. Au fait, merci pour le lien! – FraK

+1

@FraK, c'est une bonne question, à laquelle je ne connais pas de réponse satisfaisante autre que: c'est ainsi que la version actuelle du compilateur a décidé de le faire (source par exemple: https://github.com/fsharp/fsharp/issues/717). Comme Tomas le dit gentiment, le type original et son alias sont interchangeables pour le compilateur (et d'autres outils), et il ne donne aucune garantie quant à quand vous verrez l'un ou l'autre. –

+0

Merci pour la réponse – FraK

4

La déclaration de type de NewType est un alias de type, ce qui signifie que NewType est échangeable avec string * int - le compilateur les traite comme la même chose et parfois cela signifie qu'il rapportera un à la place de l'autre en dépit d'une annotation de type.

Si vous voulez un type qui doit toujours être référencé via son nom complet, vous devrez le définir comme un nouveau type explicite - à ce stade, il est probablement préférable d'utiliser un enregistrement (qui permettra également vous nommez les champs individuels), mais si vous voulez quelque chose laconique, vous pouvez utiliser une union discriminée cas unique au lieu:

type NewType = NT of (string*int) 

let testFunction (NT arg1) : NewType = 
    NT("resultString", 2) 

testFunction (NT("test", 3)) 
+0

Mais alors pourquoi le type de retour fonctionne-t-il toujours, c'est "NewType" et pas la chaîne * int? – FraK

+0

@FraK Je pense que c'est simplement un effet secondaire de certains détails d'implémentation dans le compilateur - lors de l'unification de deux types 't1' et' t2', le compilateur vérifie s'ils sont égaux - puis choisit et utilise l'un d'entre eux comme tapez le nom qu'il montre. –

+0

Merci pour la réponse – FraK