August 6th, 2012

flow

Вредные привычки типизации

Одна из проблем современных языков программирования - неуправляемые системы типов. В каждом языке проведены границы между:
1. Значениями. Примеры: 42, "xyz".
2. Типами. Примеры: int, float, Maybe a, List[T].
3. Типами времени трансляции. Примеры: тип "локальная переменная", тип "свойство объекта".

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

Например, в языках со статической типизацией типы вычисляются в compile-time, константны в пределах сеанса работы приложения/сервиса. Хочется подгрузить из сторонней схемы во время работы программы - упс, нельзя. Придётся дописывать уровень косвенности со своей системой типов или использовать готовый язык с динамической типизацией.

В то же время, в языке с динамической типизацией теряется преимущество compile-time проверок и оптимизаций с константной частью описаний типов. Всё в рантайме, медленно, с более поздним обнаружением ошибок типизации.

Значения и типы при статической типизации распадаются на разные миры. Вычисления на типах - ад, коровники и Александреску в любом языке. Уходим на динамическую типизацию - получаем более внятный код, но менее эффективный и предсказуемый результат.

А что "типы времени трансляции"? Они есть, но вам недоступны.
log_value(y);
В большинстве языков подобный кусок кода может вывести "15.0" или "float 15.0", использование сишного препроцессора позволит получить "y = 15.0", но никто не даст вам identity "локальной переменной y, которая определена в строке [...] версии [...] файла [...]" или identity "значения этой переменной для данного фрейма".

Принято заранее делить на разные identity "это значение переменной, оно может меняться", "это её тип, он всегда неизменен" и "это информация времени компиляции, хрен знает что с ней там, но мы её просто никому не дадим". Или как-то иначе, если pure functional, например. Но любое из таких решений, как в случае использования одного основного языка проекта, так и в случае пары системный+скриптовой, приводит к расхождению с предметными областями в разных частях проекта. С коим приходится бороться.

Пока есть разница между типом и значением, классом и экземпляром - порядка не будет.

Надо работать с прототипами, где этой разницы нет. И тогда прототип "float" и прототип "аргумент функции" будут лишь константными кусочками прототипа для identity "distance", вычисленными во время компиляции. А работа с dependent types становится простой как 2+2.