Определенные типы все еще необходимы?

20

Одна вещь, которая пришла мне в голову на днях, это конкретные типы, которые все еще необходимы, или наследие, которое сдерживает нас. Я имею в виду следующее: действительно ли нам нужны short, int, long, bigint и т. Д. И т. Д.

Я понимаю причину, переменные / объекты хранятся в памяти, память должна быть выделена, и поэтому мы должны знать, насколько большой может быть переменная. Но на самом деле, не должен ли современный язык программирования обрабатывать «адаптивные типы», т. Е. Если что-то выделяется только в диапазоне сокращений, оно использует меньше байтов, а если что-то внезапно выделяется очень большое число, выделяется память в соответствии с этим конкретным случаем.

Float, real и double немного сложнее, так как тип зависит от того, какая точность вам нужна. Однако строки должны иметь возможность занимать меньше памяти во многих случаях (в .Net), где в основном используется ascii, но строки всегда занимают вдвое больше памяти из-за кодировки в юникоде.

Одним из аргументов для конкретных типов может быть то, что это часть спецификации, то есть, например, переменная не должна быть больше определенного значения, поэтому мы устанавливаем ее в shortint. Но почему бы вместо этого не иметь ограничения типа? Было бы гораздо более гибким и мощным иметь возможность устанавливать допустимые диапазоны и значения для переменных (и свойств).

Я осознаю огромную проблему в обновлении архитектуры типов, поскольку она настолько тесно интегрирована с базовым оборудованием, и такие вещи, как сериализация, могут стать действительно сложными. Но с точки зрения программирования это должно быть здорово, нет?

Homde
источник
6
PHP, Ruby, Perl и другие не требуют указывать типы переменных. Окружающая среда понимает это для вас.
FrustratedWithFormsDesigner
7
Строки Unicode не должны занимать дополнительную память, когда они используются только для ASCII (UTF-8).
2
Но есть разница между вариантным и адаптивным типами IMO. Варианты не набираются вообще, но набираются при назначении, тогда как адаптивные типы будут набираться, но более свободно. (и мне нравится концепция ограничения типов)
Homde
Это напоминает мне об этом проекте: tom.lokhorst.eu/media/…
LennyProgrammers
4
А как насчет Ады? type hour is range 0 .. 23;
Mouviciel

Ответы:

12

Я полностью верю, что это так. Семантические ограничения стоят больше, чем ограничения реализации. Беспокойство о размерах чего-то напоминает беспокойство о скорости чего-либо, когда появляется объектно-ориентированное программирование.

Он не заменил программирование, критичное к производительности. Это просто сделало не критичное к производительности программирование более продуктивным.

Марк Канлас
источник
1
Проверьте кодовые контракты в .NET 4.0.
Стивен Джеурис
+1 Когда речь идет о хранении / передаче данных (например, сети), ограничения имеют основополагающее значение для максимизации эффективности протокола / реализации. Кроме того, есть много возможностей, если доступны типизированные коллекции. Кроме этого, можно с уверенностью предположить, что эффективность может отойти на задний план (особенно если это снижает вероятность семантических ошибок).
Эван Плейс
9

Адаптивные типы означают логику для выполнения адаптации, означают работу во время выполнения для выполнения этой логики (для шаблонов и времени компиляции потребуется определенный тип, а вывод типа является особым случаем, когда вы получаете лучшее из двух миров). Эта дополнительная работа может быть полезна в средах, где производительность не критична, а система сохраняет разумные размеры. В других средах это не так (встроенные системы - одна, где вам иногда приходится использовать 32/64-битные целочисленные типы для производительности процессора и 8/16-битные целочисленные типы для оптимизации статического резервного копирования памяти).

Даже языки общего назначения, которые поддерживают позднюю привязку (разрешение типов во время выполнения, например, VB6), как правило, теперь способствуют строгой типизации (VB.NET) из-за снижения производительности, которое возникало при злоупотреблении поздней привязкой, и потому, что вы часто в конечном итоге получается некрасивый код, когда типы не являются явными ( Справочник / Профессиональный рефакторинг в Visual Basic - Даниэль Арсеновски ).

Матье
источник
Пожалуйста, определите «автопечатание».
@delnan: заменил автопечатание на позднюю привязку, что я и имел в виду :)
Матье
Есть много языков общего назначения, которые разрешают типы во время выполнения, Common Lisp, чтобы назвать только один. (В целях повышения производительности вы можете объявлять типы в Common Lisp, поэтому вы можете делать это только в разделах, критичных к производительности.)
Дэвид Торнли,
@ Дэвид Торнли: «принуждение» к строгой типизации могло быть слишком сильным, «продвижение» было бы более уместным, соответственно обновил мой ответ. Язык, который позволяет вам выбирать между двумя типами привязки в зависимости от ситуации, безусловно, лучше, чем принуждение тем или иным способом. Особенно, когда не занимаешься программированием на низком уровне и фокусируешься на логике.
Матье
4

Простота, память и скорость Когда я объявляю переменную, память для этой переменной выделяется в одном блоке. Чтобы поддерживать динамически растущую переменную, мне нужно добавить в эту переменную концепцию несмежной памяти (либо ту, либо зарезервировать самый большой блок, который может представлять переменная). Несмежная память снизит производительность при назначении / извлечении. Выделение как можно большего было бы расточительным в сценарии, где мне нужен только байт, но система резервирует долго.

Подумайте о компромиссах между массивом и вектором (или связанным списком). Для массива поиск определенной позиции - это простой вопрос получения начальной позиции и смещения указателя памяти на x пробелов, чтобы найти эту новую позицию в памяти. Представьте, что int - это бит [32], чтение которого включает в себя обход этого массива для получения всех значений битов.

Чтобы создать динамический тип числа, вы должны изменить его с массива битов на вектор битов. Чтение вашего динамического числа включает в себя переход к голове, получение этого бита, выяснение, где находится следующий бит в памяти, перемещение в это место, получение этого бита и т. Д. Для каждого бита в динамическом числе вы выполняете три операции чтения ( текущий), читать (адрес следующего), двигаться (следующий). Представьте себе чтение значений миллиона чисел. Это миллион дополнительных операций. Это может показаться незначительным. Но подумайте о системах (например, финансовых), где важна каждая миллисекунда.

Было принято решение, что возложение на разработчика ответственности за проверку размера и проверку является небольшим компромиссом по сравнению с влиянием на производительность системы.

Майкл Браун
источник
1
Другой альтернативой является реализация чисел, аналогичных спискам массивов, где массив перераспределяется, когда число превышает текущий размер. Также вы должны учитывать случай, когда пользователь ХОЧЕТ переполнение цикла.
Майкл Браун
Это правда, но несколько упрощение. Вы могли бы придумать более эффективную структуру массива, хотя в большинстве случаев не так быстро, как статически типизированная, может быть «достаточно быстро». например, вы можете сохранить информацию о блоках разных типов, если массив не был полностью неровным, что не занимало бы столько памяти или производительности. Или массив может пожертвовать некоторой памятью, чтобы иметь некоторый индекс. Массив может даже самооптимизироваться в зависимости от его содержимого. Вы все еще можете иметь возможность набирать размер памяти через ограничение типа, если вам нужна производительность.
Homde
Чтобы быть справедливым, это не так жестоко, как вы делаете. Сравни мой предстоящий ответ.
Пол Натан
3

Определенные типы требуются для аппаратно-ориентированных языков и проектов. Одним из примеров являются протоколы сетевых подключений.

Но давайте создадим - для забавы - тип varint на языке, подобном C ++. Создайте его из newмассива d.

Нетрудно реализовать сложение: просто скомпонуйте байты вместе и проверьте старшие биты: если есть операция переноса, newв новом старшем байте и перенесите бит. Вычитание следует тривиально в представлении дополнения 2. (Это также известно как сумматор пульсации).

Умножение следует аналогично; использовать итеративное добавление / сдвиг. Как всегда, настоящий поворот вашего хвоста - это деление [*].

Что вы потеряли, когда это произойдет?

  • Детерминированное время. У вас есть системный вызов ( new), который может срабатывать в точках, которые не обязательно контролируются.

  • Детерминированное пространство.

  • Полупрограммная математика идет медленно.

Если вам нужно использовать язык аппаратного уровня, а также работать на высоком (медленном) уровне и не хотите встраивать механизм сценариев, это varintимеет большой смысл. Это наверное где-то написано.

[*] Cf аппаратные математические алгоритмы для более быстрых способов сделать это - обычно дело в параллельных операциях.

Пол Натан
источник
2

Это хороший вопрос. Это объясняет, почему такой язык, как Python, не нуждается в «short, int, long, bigint и т. Д.»: Целые числа, ну, в общем, целые числа (в Python 3 есть единственный целочисленный тип), и не имеют предельного размера (за исключением память компьютера, конечно).

Что касается Unicode, кодировка UTF-8 (которая является частью Unicode) использует только один символ для символов ASCII, так что это не так уж плохо.

В целом, динамические языки, кажется, идут в том направлении, которое вы упоминаете. Однако из соображений эффективности в некоторых случаях полезны более ограниченные типы (например, программы, которые должны работать быстро). Я не вижу особых изменений в обозримом будущем, поскольку процессоры организуют данные в байтах (или в 2, 4, 8 и т. Д. Байтах).

Эрик О Лебиго
источник
1

На основании теории языка вы правы. Типы должны основываться на наборе правовых состояний, доступных для этих преобразований преобразованиях и операциях, выполняемых в этих состояниях.

Однако это примерно то, что дает ООП-программирование в его типичной форме. На самом деле, в Java, вы фактически говорите о BigIntegerи BigDecimalклассах, которые распределяют пространство на основе того , сколько требуется для хранения объекта. (Как отмечает FrustratedWithFormsDesigner, многие языки скриптового типа идут еще дальше по этому пути и даже не требуют объявления типа и будут хранить все, что вы им дадите.)

Однако производительность по-прежнему актуальна, и поскольку переключение типов во время выполнения обходится дорого, а компиляторы не могут гарантировать максимальный размер переменной во время компиляции, у нас все еще есть переменные статического размера для простых типов во многих языках.

jprete
источник
Я понимаю, что какая-то динамическая / адаптивная типизация кажется дорогостоящей и менее производительной, чем та, которую мы имеем сейчас, и с использованием современных компиляторов они наверняка будут. Но уверены ли мы на 100%, что если вы создадите язык и компилятор с нуля, вы не сможете сделать их, если не так быстро, как статически типизированные, по крайней мере достаточно быстро, чтобы это того стоило.
Homde
1
@MKO: Почему бы тебе не попробовать и посмотреть?
Анон.
1
Да, вы можете сделать это реально быстро (но, вероятно, никогда не так быстро, как статическая система для чисел). Но часть "стоит ли это того" хитрее. Большинство людей работают с данными, чей диапазон удобно вписывается в intили или double, и если это не так, они знают об этом, поэтому динамическое определение размера - это функция, за которую им не нужно платить.
jprete
Как и все программисты, конечно, я мечтаю когда-нибудь создать свой собственный язык;)
Homde
@jprete: я не согласен; большинство людей не знают о возможных больших промежуточных результатах. Такой язык может и был сделан достаточно быстро для большинства целей.
Дэвид Торнли
1

Это зависит от языка. Для языков более высокого уровня, таких как Python, Ruby, Erlang и т. Д., У вас есть только концепция целых и десятичных чисел.

Однако для определенного класса языков наличие этих типов очень важно. Когда вы пишете код для чтения и записи двоичных форматов, таких как PNG, JPeg и т. Д., Вам необходимо точно знать, сколько информации читается за раз. То же самое с написанием ядер операционной системы и драйверов устройств. Не все делают это, и на языках более высокого уровня они используют библиотеки C, чтобы выполнить детальную тяжелую работу.

В России shortвсе еще есть место для более конкретных типов, но многие проблемы разработки не требуют такой точности.

Берин Лорич
источник
0

Недавно я создал редактор лестничной логики и среду выполнения и решил ограничиться типами:

  • логический
  • номер
  • строка
  • DateTime

Я считаю, что это сделало его более интуитивным для пользователя. Это радикальное отклонение от большинства ПЛК, которые имеют весь «нормальный» диапазон типов, который вы видите на таком языке, как C.

Скотт Уитлок
источник
0

Языки программирования движутся в этом направлении. Возьмем строки, например. В старых языках вы должны объявлять размер строки, как PIC X(42)в COBOL, DIM A$(42)в некоторых версиях BASIC или [ VAR] CHAR(42)в SQL. В современных языках у вас есть только один динамически распределенный stringтип, и вам не нужно думать о размере.

Целые числа разные, однако:

Я имею в виду следующее: действительно ли нам нужны short, int, long, bigint и т. Д. И т. Д.

Посмотрите на Python. Используется для различения целых чисел машинного размера ( int) и произвольного размера ( long). В 3.х прежний исчез (старый long- новый int), и никто не пропустил его.

Но есть еще специализированный тип для последовательностей 8-битных целых чисел в форме bytesи bytearray. Почему бы не использовать tupleили listцелых чисел, соответственно? Правда, bytesесть дополнительные строковые методы, которых tupleнет, но, безусловно, эффективность во многом с этим связана.

Float, real и double немного сложнее, так как тип зависит от того, какая точность вам нужна.

На самом деле, нет. Подход «все с двойной точностью» очень распространен.

dan04
источник
1
Может быть, базовые типы должны объявлять базовое намерение типа, то есть int для «обычных» чисел, удваивается для всех обычных «десятичных дробей» (разве не должно быть, чтобы целые числа имели десятичные дроби, хотя для простоты?) «Money» для работы с суммами и байтами для работы с двоичными данными. Ограничение типа, объявленное с помощью атрибута, может позволять объявлять допустимый диапазон, десятичную точность, обнуляемость и даже допустимые значения. Было бы здорово, если бы вы могли создавать пользовательские и повторно используемые типы таким образом
Homde
@konrad: ИМХО, причина, по которой "беззнаковые" целые числа вызывают такие головные боли в C, заключается в том, что они иногда используются для представления чисел, а иногда для представления членов обертывающего абстрактного алгебраического кольца. Наличие отдельных типов «кольцо» и «число без знака» может гарантировать, что подобный код unum64 += ring32a-ring32bвсегда будет давать правильное поведение, независимо от того, является ли целочисленный тип по умолчанию 16 битами или 64 [обратите внимание, что использование +=является обязательным; выражение вроде unum64a = unum64b + (ring32a-ring32b);должно быть отклонено как неоднозначное.]
суперкат
0

Я понимаю причину, переменные / объекты хранятся в памяти, память должна быть выделена, и поэтому мы должны знать, насколько большой может быть переменная. Но на самом деле, не должен ли современный язык программирования обрабатывать «адаптивные типы», т. Е. Если что-то выделяется только в диапазоне сокращений, оно использует меньше байтов, а если что-то внезапно выделяется очень большое число, выделяется память в соответствии с этим конкретным случаем.

Float, real и double немного сложнее, так как тип зависит от того, какая точность вам нужна. Однако строки должны иметь возможность занимать меньше памяти во многих случаях (в .Net), где в основном используется ascii, но строки всегда занимают вдвое больше памяти из-за кодировки в юникоде.

У Фортрана было что-то похожее (я не знаю, действительно ли это то, что вы имеете в виду, поскольку на самом деле я вижу два вопроса). Например, в F90 вверх вам не нужно явно определять размер шрифта , так сказать. Это хорошо, не только потому, что дает вам центральное место для определения типов данных, но и является портативным способом их определения. REAL * 4 не одинаков во всех реализациях на всех процессорах (и под процессором я имею в виду CPU + компилятор), не в общем.

selected_real_kind (p, r) возвращает значение типа реального типа данных с десятичной точностью, превышающей, по меньшей мере, p цифр, и диапазон показателей, превышающий, по меньшей мере, r.

Так вы идете, например;

program real_kinds
integer,parameter :: p6 = selected_real_kind(6)
integer,parameter :: p10r100 = selected_real_kind(10,100) !p is precision, r is range
integer,parameter :: r400 = selected_real_kind(r=400)
real(kind=p6) :: x
real(kind=p10r100) :: y
real(kind=r400) :: z

print *, precision(x), range(x)
print *, precision(y), range(y)
print *, precision(z), range(z)
end program real_kinds

(Я думаю, что это довольно очевидный пример).

До сих пор не знаю, правильно ли я понял ваш вопрос, и это то, что вы думаете.

ладья
источник