J'ai spécifié un type personnalisé qui prend deux flottants et en fait une paire (un nombre complexe):
let complexToPair ((x,y):complex) = (x,y);;
La fonction makeComplex
est de type float -> float -> complex , ce qui est tout à fait correct. Cependant, je souhaite créer une fonction qui prend un type complexe et en fait également une paire normale.
Voici la fonction que j'ai essayée:
type complex = (float * float);; let makeComplex x y = complex(x,y);;
Mais le type résultant est float * float -> float * float
, alors qu'il devrait vraiment l'être complex -> float * float
. Est-ce que j'utilise la mauvaise syntaxe pour le ((x,y):complex)
part?
3 Réponses :
Les abréviations de type ne masquent ni ne créent un type distinct de leurs définitions. complex
et float * float
sont toujours entièrement échangeables, vous venez de lui donner un autre nom.
La façon idiomatique de créer un type distinct en F # est d'utiliser un union discriminée à cas unique:
type complex = private Complex of float * float
Vous pouvez ensuite écrire vos fonctions en utilisant ce constructeur pour créer une correspondance de valeur et de modèle pour la déconstruire:
let makeComplex x y = Complex (x,y) let complexToPair (Complex (x, y)) = (x, y)
Créer des DU avec un seul cas est une mauvaise pratique (à moins que vous n'ayez l'intention d'ajouter d'autres cas plus tard), car il existe des DU pour contenir et faire correspondre des données discriminées.
@CharlesRoddie Pouvez-vous étayer cela avec autre chose qu'une opinion? Il existe de nombreuses preuves pour les contrary .
le premier lien n'est pas une preuve mais une opinion. Mes raisons plus en détail sont: 1. Dénomination. Les DU sont appelés «syndicats discriminés» et sont donc destinés à des données discriminées, et un type de données n'a pas besoin de discrimination. 2. Complexité. Les DU contiennent des échafaudages pour la discrimination, contrairement aux types d'enregistrement et aux types d'enregistrement, qui sont donc des moyens plus appropriés de mettre en œuvre cette idée. Vous pouvez utiliser sharplab.io pour le confirmer.
Votre lien "contraire" est très bien car il est parfaitement bon d'utiliser un DU pour avoir un seul cas qui est ouvert à une extension future avec plus de cas, comme discuté ici et mentionné dans mon commentaire original ici.
@CharlesRoddie Donc, le mérite technique de votre argument est essentiellement simplement qu'il existe une méthode supplémentaire de Tag
? Cela semble assez insignifiant compte tenu de la surcharge d'environ 16 octets que vous ajoutez en enveloppant la valeur dans un objet, ce que font à la fois un enregistrement et une union discriminée à cas unique.
De l'argument 2 uniquement. Le Tag est le principal ajout conceptuel par rapport à un enregistrement, et il est inutile. Toutes ces méthodes s'enroulent dans un objet, il n'y a donc aucune différence. La différence entre les lignes de code C # et sharplab.io est de 44, il peut donc y avoir d'autres inefficacités de l'UA. Il existe également un argument de dénomination supplémentaire selon lequel il n'est pas possible de trouver de bons noms pour le type DU et le cas d'union DU.
@CharlesRoddie La différence est que les lignes de code joliment imprimées ne sont pas pertinentes. Il ajoute quelques octets par type mais pas par valeur , ce qui est totalement insignifiant par rapport au surcoût par type d’une classe et par valeur d’un objet, qui est le même chose avec un disque.
Si vous souhaitez toujours utiliser une abréviation de type, utilisez l'annotation de type, comme ceci:
type complex = float * float let complexToPair: complex -> float * float = id
Vous pouvez créer une classe:
type Complex = { Real:float; Imaginary:float }
Ou un enregistrement:
type Complex(x:float, y:float) = member t.Real = x member t.Imaginary = y
Ce seront des types distincts et vous permettront également de créer et organiser des méthodes utiles (telles que les opérateurs arithmétiques).