J'ai lu sur cette chose appelée types dépendants .
Imaginons par exemple une fonction firstNPrimes(int n)
qui retourne un tableau de longueur n. En d'autres termes, le type renvoyé serait int[n]
. (Dans les langages de programmation normaux sans types dépendants, tout ce que nous pouvons dire, c'est qu'il renvoie int[]
où le type ne spécifie pas la longueur du tableau.)
Je ne sais pas vraiment à quel point c'est utile. Je suppose que si vous aviez un produit croisé (dans lequel cette première fonction pourrait être envoyée comme arguments):
int[3] Cross(int[3] A, int[3] B){..}
qui ne fonctionnait que sur des vecteurs tridimensionnels alors peut-être que le compilateur pourrait taper ceci et éviter les problèmes de pointeur.
Existe-t-il de véritables utilisations pratiques des types dépendants qui seraient utiles dans la programmation quotidienne? Ou sont-ils principalement utiles pour la logique abstraite?
Je pense que ce qui pourrait vous manquer, c'est que la valeur dont vous dépendez ne doit pas être une constante comme 3. La longueur est souvent spécifiée de manière générique, donc vous ne connaissez peut-être pas la longueur exacte, mais vous pouvez spécifier des contraintes sur cette longueur entre vos valeurs de retour et différents arguments.
De nombreuses fonctions nécessitent n > 0
, comme head
par exemple.
Il y a beaucoup de fonctions qui prennent un index qui doit être dans la taille du vecteur.
Il y a beaucoup de fonctions qui prennent deux vecteurs qui doivent être de la même taille, comme un produit scalaire par exemple.
La saisie dépendante vous permet de vérifier toutes ces sortes de contraintes au moment de la compilation, en validant à l'extrémité même de votre système.
Je suppose que si vous aviez un produit croisé:
int[3] Cross(int[3] A, int[3] B){..}
qui ne fonctionnait que sur des vecteurs tridimensionnels alors peut-être que le compilateur pourrait taper ceci et éviter les problèmes de pointeur.
Oui, un compilateur pourrait vérifier que A
et B
sont de la bonne taille dans tous les endroits où Cross
est utilisé, si la taille fait partie du système de type. Ce pourrait être une erreur quand ce n'est pas le cas. Et s'il y a de tels cas où il ne peut pas être vérifié, c'est une erreur ou un avertissement à la discrétion du concepteur de langage.
Existe-t-il de véritables utilisations pratiques des types dépendants qui seraient utiles dans la programmation quotidienne?
Il s'agit d'une fonctionnalité qui restreint ce que vous pouvez faire. Vous ne pouvez pas passer la mauvaise taille de tableau dans Cross
. Et c'est une bonne chose. Cette fonctionnalité peut rendre la langue plus facile à lire et plus difficile à se tromper.
De plus, il est idéal pour le marshaling automatique, car la taille du tampon nécessaire pour transférer des informations fait partie du système de type, vous pouvez le déclarer comme faisant partie de l'interface et - s'il est déclaré correctement - pourrait vous éviter des problèmes de dépassement de tampon.
Il vous permet également de faire correspondre les types de base de données dans votre code. Par exemple, une chaîne limitée à un nombre donné de caractères.
Ou sont-ils principalement utiles pour la logique abstraite?
Un type dépendant est un type paramétré par des valeurs. Cela arrive tout le temps en - roulement de tambour - C++. Vous pouvez créer un modèle qui a des paramètres de valeur.
Voici un exemple simple:
template<int N>
struct S { int a[N]; };
Cela définit un membre du tableau de la taille spécifiée comme argument de modèle.
Exemple d'utilisation:
S<10> s; // s.a is an array of 10 int
s.a[9] = 4;
Exemple tiré de cppreference .
C'est ainsi std::array
fonctionne. std:array
est un modèle qui prend un type et une taille. Vous pouvez utiliser make_array si vous voulez déduire la taille. Si vous allez lire à propos de std::array
vous constaterez qu'il est moins polyvalent que les tableaux de style C. Comme je le disais, cette fonctionnalité restreint ce que vous pouvez faire. En échange, vous obtenez un contrôle statique.
Nous pourrions utiliser le système de modèles C++ pour définir l'algèbre vectorielle à N dimensions. Et étant donné que la spécialisation du support C++, nous pourrions également gérer des opérations non communes à tous les nombres de dimensions. Par exemple, un produit croisé de deux vecteurs 2D étant un vecteur 2D n'a pas de sens, mais un produit de coin de deux vecteurs 2D qui résulte est la longueur du produit croisé des deux vecteurs 2D augmentée avec 0 dans la troisième dimension est logique.
Pour un autre exemple, je vais me tourner vers Ada. Il s'agit d'une déclaration de sous-type dans Ada:
subtype Count_To_Ten is Integer range 1 .. 10;
Ce qui précède définit un type Count_To_Ten
, toutes les valeurs de ce type sont également de type Entier et sont comprises dans la plage inclusive de 1 à 10.
Alors
subtype Ten_Characters is String (Count_to_Ten);
Ce qui précède définit un type Ten_Characters
, toutes les valeurs de ce type sont des chaînes et l'idéx des caractères est un Count_to_Ten
, est donc une chaîne de 10 caractères.
Cet exemple est tiré de Ada Programming/Type System .
Nous pouvons définir ces vecteurs 3D dans Ada:
type Axis is range 1 .. 3;
type Vector3 is array (Axis) of Integer;
D'accord, pas exactement comme ça, mais j'ai trouvé un Vector3
type défini dans Ada dans OpenGLAda - Liaison OpenGL pour Ada (ceux-ci utilisent une énumération pour l'index et vous permettent de spécifier le type des éléments tant qu'il a des opérations arithmétiques). Je dirais que oui, cela a une utilité pratique: les jeux vidéo.
Prenons un exemple concret. Ou mieux, real-mars.
Mars Climate Observer de la NASA s'est écrasé sur Mars parce que le logiciel a confondu mètres et pieds. Cela était possible car le logiciel traitait toutes les distances comme de simples chiffres. En physique, les quantités sont associées à des unités. Les pieds et les mètres sont des unités différentes, mais les deux sont utilisés avec des longueurs. Les secondes et les compteurs sont des unités différentes pour des quantités différentes.
En C++, vous pouvez l'exprimer dans le système de type. Et vous pouvez même l'utiliser pour déduire des quantités dérivées, telles que la vitesse, en mètres par seconde (m ∙ s-1) et les surfaces, en mètres carrés (m2).
Si la NASA avait utilisé cela, elle aurait pu comparer les mètres et les pieds. Le compilateur aurait introduit le bon facteur d'échelle lors de la conversion des pieds en mètres.