Бьярн Страуструп считает, что у него есть лучший способ делать дженерики


Бьярн Страуструп, конечно же, изобретатель C ++. Со временем он добавил к нему функции, и теперь он предлагает добавить Concepts, чтобы дженерики работали так, как должны были.

У меня была неоднозначная реакция на C ++ за все время его существования. Я был и поражен тем, как Бьярну Страуструпу удалось превратить C в объектно-ориентированный язык с помощью только препроцессора.
То есть, C ++ сначала был синтаксическим сахаром, который был сопоставлен непосредственно с C. Это был простой язык, который вы могли понять с точки зрения того, как он отображался на C, который был еще более простым языком. Однако со временем я столкнулся с программами и идиомами C ++, которые заставляли меня чувствовать, что язык слишком сложен и способен выражать вещи способами, которые в лучшем случае были ошибочными попытками абстракции, а в худшем – просто попытками произвести впечатление.
Однако одно всегда оставалось верным. Всякий раз, когда я читаю что-либо создателя языка, Бьярна Страуструпа, я еще раз убеждаюсь, что C ++ – простой чистый язык, способный к большой ясности выражения. Другими словами, C ++, написанный Бьярном, – это тот язык, которым он всегда должен был быть. Если вы мне не верите, попробуйте прочитать одну из его книг, перечисленных на боковой панели.
Обобщения – проблема любого языка, но кажется, что шаблоны C ++, представленные в 1987 году, особенно разочаровали Бьярна:
Я провалил. Я хотел три свойства для шаблонов:
• Полная общность / выразительность
• Отсутствие накладных расходов по сравнению с ручным кодированием
• Хорошо определенные интерфейсы
Тогда никто не мог понять, как получить все три, поэтому мы получили
• Полнота по Тьюрингу
• Лучше, чем при ручном кодировании
• Паршивые интерфейсы (в основном утиная типизация во время компиляции)

Большая проблема, с которой мы сталкиваемся, заключается в том, что если вы примете строго типизированный язык, вы не сможете написать алгоритм, работающий с сильно различающимися типами объектов. Например, если вам нужен метод сортировки, вы должны написать его для целых чисел, чисел с плавающей запятой, символов, строк, векторов и так далее. Алгоритм в значительной степени не зависит от типа, но его нельзя напрямую выразить определенным для типа способом без создания версии для каждого типа, с которым он должен работать.
Здесь на помощь приходят универсальные шаблоны. Обычно, сделав тип параметром определения, вы можете написать единственную функцию, которую можно использовать для реализации алгоритма.
Например, используя шаблон C ++, вы можете реализовать функцию сортировки
template void sort (T & c) {};
и когда вы используете функцию, вы даете “параметру типа” T значение, которое затем позволяет компилятору выполнить проверку типа кода.
вектор <строка> против; сортировка (против);
Проблема в том, что шаблон будет пытаться принять любой тип, с которым пользователь хочет попытаться использовать его, и любые ошибки генерируются только при создании экземпляра шаблона. Как говорится в новой статье:
двойной d = 7;
sort (d); // ошибка: d не имеет оператора []
У нас проблемы:
• Как вы, вероятно, знаете, сообщение об ошибке, которое мы получаем от sort (d), является подробным и далеко не таким точным и полезным, как можно было бы предположить из моего комментария.
• Чтобы использовать сортировку, нам нужно предоставить ее определение, а не просто ее объявление, это отличается от обычного кода и меняет модель того, как мы организуем код.
• Требования сортировки к типу аргумента неявны («скрыты») в теле функции.
• Сообщение об ошибке для sort (d) появится только при создании экземпляра шаблона, и это может быть намного позже точки вызова.
• Нотация шаблона уникальна, многословна, повторяется и широко не нравится.
Однако настоящая проблема заключается в том, что нет явного указания, какие свойства T должны быть допустимым типом для шаблона. Здесь на помощь приходят концепции. Концепция – это предикат, который принимает значение true или false при применении к типу. Это условие, которое должно выполняться, чтобы тип был приемлемым для шаблона. Например:
// Общий код с использованием концепции (с возможностью сортировки):
пустая сортировка (Сортировка и c); // Концепции: принять любой c, который можно сортировать
vector vs = {“Привет”, “новый”, “Мир”};
сортировка (vs); // хорошо: vs – это сортируемый контейнер
двойной d = 7; sort (d);
// ошибка: d не подлежит сортировке (double не предоставляет [] и т. д.)
Таким образом, мы использовали концепцию, чтобы указать точную функциональность, которую должен иметь тип, чтобы он работал в шаблоне.
Конечно, все становится сложнее, и пример в статье демонстрирует типичную концепцию;
шаблон требует Sequence && Equality_comparable , T> Iterator_of find (S & seq, const T & value);
Давайте посмотрим на строку за строкой:
• Это шаблон, который принимает два аргумента типа шаблона (здесь нет ничего нового).
• Первый аргумент шаблона должен быть последовательностью (Sequence ), и мы должны иметь возможность сравнивать элементы последовательности со значением с помощью оператора == (Equality_comparable , T>).
• Эта функция find () принимает свою последовательность по ссылке, а значение, которое нужно найти, – как ссылку на константу. Он возвращает итератор (здесь ничего нового).
Идея состоит в том, что в библиотеках будет определено множество концепций, которые вы можете просто использовать, чтобы указать, какие типы свойств должны иметь, но есть также способ определения новых концепций.
Так это ли путь?
Если вам нужен строго типизированный язык, вы должны изобрести что-то подобное, иначе вы можете забыть о шаблонах и использовать указатели объектов для написания общего кода и отказаться от проверки типов.
Конечно, есть языки – например, JavaScript – где все является общим просто потому, что проверка типов не выполняется. Вместо этого программист на JavaScript должен написать явный код, чтобы убедиться, что используемый объект действительно имеет требуемые свойства.
Отойдя на мгновение назад, вы почувствуете себя немного странно. Сначала мы начинаем писать общие функции, потому что нет типа. Затем мы изобретаем строгую типизацию, чтобы не было общих функций. Затем мы изобретаем шаблоны или что-то подобное, и у нас есть общие функции, но нет строгой типизации. Затем мы изобретаем концепции или что-то подобное, чтобы управлять шаблонами и прояснять условия, которым должны удовлетворять типы …
Это долгий и повторяющийся путь, и он заставляет задуматься, не являются ли концепции более глубокой реальностью, игнорируя исключительно эзотерическую формулировку Карри-Ховарда.


Добавить комментарий