J'y ai réfléchi un peu. Le problème principal est qu'en général, nous ne savons pas quelle est la valeur d'une valeur de type polymorphe. Si vous ne disposez pas de ces informations, vous devez les obtenir d'une manière ou d'une autre. La monomorphisation obtient ces informations pour vous en spécialisant le polymorphisme. La boxe obtient ces informations pour vous en mettant tout dans une représentation de taille connue.
Une troisième alternative est de garder une trace de ces informations dans les types. Fondamentalement, ce que vous pouvez faire est d'introduire un type différent pour chaque taille de données, puis des fonctions polymorphes peuvent être définies sur tous les types d'une taille particulière. Je vais esquisser un tel système ci-dessous.
SortesConstructeurs de typesκUNE: : = N: : =|∀ a : κ .UNE|α|A × B|A + B|A → Br e fUNE|P a d ( k )|μ α : κ .UNE
Ici, l'idée de haut niveau est que le type d'un type vous indique le nombre de mots nécessaires pour disposer un objet en mémoire. Pour une taille donnée, il est facile d'être polymorphe sur tous les types de cette taille particulière. Étant donné que chaque type, même polymorphe, a toujours une taille connue, la compilation n'est pas plus difficile que pour C.
α : n ∈ ΓΓ ⊢ α : nΓ , α : n ⊢ A : mΓ ⊢ ∀ α : n .A : m
Γ ⊢ A : nΓ ⊢ B : mΓ ⊢ A × B : n + mΓ ⊢ A : nΓ ⊢ B : nΓ ⊢ A + B : n + 1
Γ ⊢ A : mΓ ⊢ B : nΓ ⊢ A → B : 1Γ ⊢ A : nΓ ⊢ r e fA : 1
Γ ⊢ P a d ( k ) : kΓ , α : n ⊢ A : nΓ ⊢ μ α : n .A : n
A × BUNEB
Les références sont intéressantes - les pointeurs sont toujours un mot, mais ils peuvent pointer vers des valeurs de n'importe quelle taille. Cela permet aux programmeurs d' implémenter le
polymorphisme à des objets arbitraires par boxe, mais ne les oblige
pas à le faire. Enfin, une fois que les tailles explicites sont en jeu, il est souvent utile d'introduire un type de remplissage, qui utilise de l'espace mais ne fait rien. (Donc, si vous voulez prendre l'union disjointe d'un int et d'une paire d'entiers, vous devrez ajouter un remplissage au premier int, afin que la disposition de l'objet soit uniforme.)
Les types récursifs ont la règle de formation standard, mais notez que les occurrences récursives doivent avoir la même taille, ce qui signifie que vous devez généralement les coller dans un pointeur pour que le tri fonctionne. Par exemple, le type de données de liste pourrait être représenté comme
μ α : 1.r e f( P a d ( 2 ) + i n t × α )
Donc, cela pointe vers une valeur de liste vide, ou une paire d'int et un pointeur vers une autre liste liée.
La vérification de type pour des systèmes comme celui-ci n'est pas non plus très difficile; l'algorithme de mon article ICFP avec Joshua Dunfield, Typechecking bidirectionnel complet et facile pour le polymorphisme de rang supérieur s'applique à ce cas avec presque aucun changement.