Не сказать, что всё понял. Но как-то мне стало жабисто делать DYNAMICALLY_TYPED для списков. Например, функция format из лиспа вполне способна определить ошибочный тип прямо во время компиляции. Отказ от этого снижает качество ЯП.

И тут я вспомнил, что нужно обязательно поменять в компиляторе: состояние лексера является глобальным, поэтому нельзя развязать этапы компиляции между собой. В момент возникновения ошибки "текущая лексема" должна хранить то место, где ошибка произошла. Из-за этого этапы компиляции у Вирта дико перемешаны. Значит, нужно прежде всего сделать состояние лексера записью и весь контекст хранить в этой записи.

Тогда вместо "ошибка" можно будет сказать "ошибка в лексеме" и дать лексему. После этого можно будет начать развязывать узлы компилятора.

Но это я отвлёкся. Какие у нас варианты использования полиморфизма, которые точно нужны?

1. тип вариант, способный вместить любое. Зачем? Для REPL - там бывает переменная типа * в лиспе. Она имеет тип "любое".
2. функция, способная принять любой объект. Зачем? print
3. функция, способная принять любое кол-во любых объектов. Зачем? лисповый список, json.
4. функция, способная принять в качестве параметра тип. Такие функции уже есть в КП: MIN, MAX, SIZE. Значит, сам по себе механизм "вызова типа по имени" в языке есть. Нет оснований не дать программисту возможность использовать его в полную силу.
5. функция, к-рая строит список из заданных объектов односвязный список данного типа, со связью по полю данного типа. Я приводил пример выше. Это гибрид из обычной ф-ии, и ф-ии, способной принять любое кол-во любых объектов. Т.е. часть параметров проверяется, а часть - любые.
6. Функция, принимающия имя типа записи, значения полей, и строящая экземпляр записи с этими значениями. Первый аргумент - типа "тип", а последующие - типа "что угодно". НО! Уже во время компиляции можно определить ошибки типизации, потому что последующие, на самом деле - не типа "что угодно", а типа, соответствующего данному полю в данной записи. Зачем это? Для сериализации (аналог #S в лиспе).

Итак, что же отвечает всем этим требованиям? Была такая идея:
Вводим понятие "макро-функция". Такая ф-я принимает все параметры в том виде, в котором их знает компилятор. Далее она должна из дерева ИмяМакроФункции(арг1,арг2,аргN)
построить другое дерево:

КакаяТоДругаяФункция(ФункцияАдаптер1(арг1),ФункцияАдаптер2(арг1),ФункцияАдаптерN(аргN));

Здесь КакаяТоДругаяФункция и ФункцияАдаптерi вычисляются телом макро-функции. Ограничение состоит в том, что они должны принадлежать явно проимпортированным модулям.

Полученное дерево снова подвергается контролю на правильность типов.

Но так нельзя сделать функцию LIST из лиспа, т.к. придётся делать MAX-ARG-COUNT вариантов конструктора списка - это явный бред. Значит, здесь не хватает механизма для обработки произвольного числа аргументов. И есть сомнения в том, что нужны разные функции-адаптеры. Может быть, нужен только "тривиальный" адаптер, к-рый просто передаёт аргумент, и второй адаптер ITEM. С т.з. эффективности это не фонтан (например, теряем возможность оптимизировать константы), зато с точки зрения простоты это лучше (теряем возможность влететь из-за того, что константа 2 обрабатывается иначе, чем переменная, равная 2 - такие вещи лучше обрабатывать общими механизмами частичных вычислений, как мне кажется, но это уже дикие дебри и пора спать).