Я немного подумал об этом. Основная проблема заключается в том, что в целом мы не знаем, насколько велико значение полиморфного типа. Если у вас нет этой информации, вы должны как-то ее получить. Мономорфизация получает эту информацию для вас, специализируясь на полиморфизме. Бокс получает эту информацию для вас, помещая все в представление известного размера.
Третий вариант - отслеживать эту информацию по видам. По сути, вы можете ввести разные типы для каждого размера данных, и тогда полиморфные функции могут быть определены для всех типов определенного размера. Я нарисую такую систему ниже.
KindsType constructorsκA::=n::=|∀a:κ.A|α|A×B|A+B|A→BrefA|Pad(k)|μα:κ.A
Здесь идея высокого уровня заключается в том, что тип типа сообщает вам, сколько слов требуется для размещения объекта в памяти. Для любого данного размера легко быть полиморфным по всем типам этого определенного размера. Поскольку каждый тип - даже полиморфный - по-прежнему имеет известный размер, компиляция не сложнее, чем для 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
Γ⊢Pad(k):kΓ,α:n⊢A:nΓ⊢μα:n.A:n
Таким образом, полный квантификатор требует, чтобы вы дали вид, на который вы попадаете. Аналогично, спаривание - это распакованный тип пары, который просто размещает рядом с в памяти (как тип структуры C). Несвязанные объединения принимают два значения одинакового размера, а затем добавляют слово для тега дискриминатора. Функции - это замыкания, представленные, как обычно, указателем на запись среды и кода.A×BAB
Ссылки интересны - указатели - это всегда одно слово, но они могут указывать на значения любого размера. Это позволяет программистам реализовывать
полиморфизм для произвольных объектов с помощью бокса, но не требует от
них этого. Наконец, когда в игру вступают явные размеры, часто полезно ввести тип заполнения, который использует пробел, но ничего не делает. (Поэтому, если вы хотите взять несвязанное объединение целого и пары целых чисел, вам нужно добавить отступ первого целого, чтобы макет объекта был равномерным.)
У рекурсивных типов есть стандартное правило формирования, но обратите внимание, что рекурсивные вхождения должны быть одинакового размера, что означает, что вы обычно должны вставлять их в указатель, чтобы сортировка работала. Например, тип данных списка может быть представлен как
μα:1.ref(Pad(2)+int×α)
Таким образом, это указывает на пустое значение списка или пару int и указатель на другой связанный список.
Проверка типа для подобных систем также не очень сложна; алгоритм в моей работе ICFP с Джошуа Данфилдом, « Полная и простая двунаправленная проверка типов для более высокого ранга полиморфизма» применяется к этому случаю практически без изменений.