Eu pensei um pouco sobre isso. A questão principal é que, em geral, não sabemos quão grande é o valor do tipo polimórfico. Se você não possui essas informações, precisa obtê-las de alguma forma. A monomorfização obtém essas informações para você especializando o polimorfismo. O boxe obtém essas informações para você, colocando tudo em uma representação de tamanho conhecido.
Uma terceira alternativa é acompanhar essas informações nos tipos. Basicamente, o que você pode fazer é introduzir um tipo diferente para cada tamanho de dados e, em seguida, funções polimórficas podem ser definidas sobre todos os tipos de um tamanho específico. Vou esboçar esse sistema abaixo.
KindsType constructorsκA::=n::=|∀a:κ.A|α|A×B|A+B|A→BrefA|Pad(k)|μα:κ.A
Aqui, a ideia de alto nível é que o tipo de tipo diga quantas palavras são necessárias para colocar um objeto na memória. Para qualquer tamanho, é fácil ser polimórfico em todos os tipos desse tamanho específico. Como todo tipo - mesmo os polimórficos - ainda tem um tamanho conhecido, a compilação não é mais difícil do que para 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Γ⊢refA:1
A ⊢ P a d ( k ) : kΓ , α : n ⊢ A : nY ⊢ u ct : n .A : n
A × BUMAB
As referências são interessantes - ponteiros são sempre uma palavra, mas podem apontar para valores de qualquer tamanho. Isso permite que os programadores implementem o
polimorfismo em objetos arbitrários por meio de boxe, mas não exige que
eles o façam. Finalmente, quando os tamanhos explícitos estão em jogo, geralmente é útil introduzir um tipo de preenchimento, que usa espaço, mas não faz nada. (Portanto, se você deseja obter a união disjunta de um int e um par de entradas, precisará adicionar preenchimento ao primeiro int, para que o layout do objeto seja uniforme.)
Os tipos recursivos têm a regra de formação padrão, mas observe que as ocorrências recursivas devem ter o mesmo tamanho, o que significa que você geralmente precisa colocá-las em um ponteiro para que a classificação funcione. Por exemplo, o tipo de dados da lista pode ser representado como
μ α : 1.r e f( P a d ( 2 ) + i n t × α )
Portanto, isso aponta para um valor de lista vazio ou um par de um int e um ponteiro para outra lista vinculada.
A verificação de tipo para sistemas como esse também não é muito difícil; o algoritmo no meu artigo da ICFP com Joshua Dunfield, Tipechecking bidirecional completo e fácil para polimorfismo de classificação mais alta se aplica a esse caso quase sem alterações.