Я читаю код гораздо чаще, чем пишу код, и я предполагаю, что большинство программистов, работающих над промышленным программным обеспечением, делают это. Я предполагаю, что преимущество вывода типов заключается в меньшей детализации и меньшем количестве написанного кода. Но с другой стороны, если вы будете читать код чаще, вам, вероятно, понадобится читаемый код.
Компилятор выводит тип; Есть старые алгоритмы для этого. Но реальный вопрос заключается в том, почему я, программист, хочу выводить тип моих переменных при чтении кода? Разве не быстрее для кого-то просто читать тип, чем думать, какой тип существует?
Изменить: В заключение я понимаю, почему это полезно. Но в категории языковых возможностей я вижу это в виде корзины с перегрузкой операторов - полезно в некоторых случаях, но влияет на читабельность при злоупотреблении.
источник
Ответы:
Давайте посмотрим на Java. У Java не может быть переменных с выведенными типами. Это означает, что мне часто приходится произносить тип по буквам, даже если для читателя совершенно очевидно, что это за тип:
И иногда это просто раздражает излагать весь тип.
Эта многословная статическая типизация мешает мне, программисту. Большинство типовых аннотаций - это повторяющиеся строки-наполнители, не содержащие содержимого регургии того, что мы уже знаем. Тем не менее, мне нравится статическая типизация, так как она действительно помогает обнаруживать ошибки, поэтому использование динамической типизации не всегда является хорошим ответом. Вывод типа является лучшим из обоих миров: я могу опустить нерелевантные типы, но все же быть уверенным, что моя программа (type-) проверена.
Хотя вывод типа действительно полезен для локальных переменных, он не должен использоваться для открытых API, которые должны быть однозначно задокументированы. И иногда типы действительно важны для понимания того, что происходит в коде. В таких случаях было бы глупо полагаться только на вывод типа.
Есть много языков, которые поддерживают вывод типов. Например:
C ++. В
auto
ключевых словах типа триггеров умозаключения. Без этого написание типов для лямбд или входов в контейнеры было бы адом.C #. Вы можете объявить переменные с помощью
var
, который запускает ограниченную форму вывода типа. Он по-прежнему управляет большинством случаев, когда требуется вывод типов. В некоторых местах вы можете полностью исключить тип (например, в лямбдах).Haskell, и любой язык в семье ML. Несмотря на то, что конкретный вид вывода типов, использованный здесь, достаточно мощный, вы все равно часто видите аннотации типов для функций по двум причинам: первая - документация, а вторая - проверка того, что вывод типов действительно нашел ожидаемые вами типы. Если есть расхождение, скорее всего, есть какая-то ошибка.
источник
int
, это может быть любой числовой тип, включая дажеchar
. Также я не понимаю, почему вы хотели бы прописать весь тип для случая,Entry
когда вы можете просто ввести имя класса и позволить вашей IDE выполнить необходимый импорт. Единственный случай, когда вам нужно прописать полное имя, это когда у вас есть класс с таким же именем в вашем собственном пакете. Но мне все равно кажется плохой дизайн.int
пример, я думал о (на мой взгляд, довольно здравомыслящем поведении) большинства языков, которые имеют вывод типа. Они обычно делают выводint
или,Integer
или как там это называется на этом языке. Прелесть вывода типа в том, что он всегда необязателен; Вы все еще можете указать другой тип, если он вам нужен. Что касаетсяEntry
примера: хороший момент, я заменю его наMap.Entry<Integer, Map<Integer, SomeObject<SomeObject, T>>>
. У Java даже нет псевдонимов типов :(colKey
и очевиден, и не имеет значения: нас интересует только его пригодность в качестве второго аргументаdoSomethingWith
. Если бы я(key1, key2, value)
извлек этот цикл в функцию, которая выдает Iterable из -triples, была бы самая общая сигнатура<K1, K2, V> Iterable<TableEntry<K1, K2, V>> flattenTable(Map<K1, Map<K2, V>> table)
. Внутри этой функции реальный типcolKey
(Integer
, а неK2
) абсолютно не имеет значения.View.OnClickListener listener = new View.OnClickListener()
. Вы все равно знаете тип, даже если программист «ленив» и сокращает его доvar listener = new View.OnClickListener
(если это было возможно). Такая избыточность встречается часто - я не буду рисковать догадками здесь - и ее устранение происходит из-за размышлений о будущих читателях. Каждая языковая функция должна использоваться с осторожностью, я не подвергаю сомнению это.Это правда, что код читается гораздо чаще, чем пишется. Однако чтение также занимает много времени, и на двух экранах кода сложнее ориентироваться и читать, чем на одном экране кода, поэтому нам нужно расставить приоритеты, чтобы упаковать наилучшее соотношение полезной информации / усилий при чтении. Это общий принцип UX: слишком много информации сразу переполняет и фактически снижает эффективность интерфейса.
И мой опыт показывает, что часто точный тип не имеет (того) значения. Вы , конечно , иногда гнездятся выражения:
x + y * z
,monkey.eat(bananas.get(i))
,factory.makeCar().drive()
. Каждое из них содержит подвыражения, которые оценивают значение, тип которого не записан. Все же они совершенно ясны. Мы в порядке, оставляя тип неустановленным, потому что его достаточно легко выяснить из контекста, и его написание принесет больше вреда, чем пользы (загромождает понимание потока данных, занимает ценный экран и пространство кратковременной памяти).Одна из причин не вкладывать выражения, как будто завтра не наступит, состоит в том, что строки становятся длинными, а поток значений становится неясным. В этом помогает введение временной переменной, она устанавливает порядок и дает имя частичному результату. Однако не все, что извлекает выгоду из этих аспектов, также выигрывает от того, что его тип прописан:
Имеет ли значение,
user
является ли объект сущности, целое число, строка или что-то еще? Для большинства целей этого не достаточно, достаточно знать, что он представляет пользователя, исходит из HTTP-запроса и используется для получения имени, отображаемого в правом нижнем углу ответа.И когда это имеет значение, автор может выписать тип. Эта свобода должна использоваться ответственно, но то же самое верно и для всего остального, что может улучшить читаемость (имена переменных и функций, форматирование, дизайн API, пробелы). И действительно, в Haskell и ML (где все можно вывести без дополнительных усилий) существует соглашение о том, чтобы выписывать типы нелокальных функций-функций, а также локальных переменных и функций, когда это уместно. Только новички позволяют выводить каждый тип.
источник
user
имеет значение, если вы пытаетесь расширить функцию, потому что она определяет, что вы можете делать сuser
. Это важно, если вы хотите добавить некоторую проверку работоспособности (например, из-за уязвимости в системе безопасности) или забыли, что вам нужно что-то делать с пользователем в дополнение к его отображению. Правда, такие виды чтения для расширения встречаются реже, чем просто чтение кода, но они также являются важной частью нашей работы.Я думаю, что вывод типа довольно важен и должен поддерживаться на любом современном языке. Мы все развиваемся в IDE, и они могут очень помочь, если вы хотите узнать тип вывода, только немногие из нас взломают
vi
. Подумайте, например, о многословности и коде церемонии в Java.Но вы можете сказать, что все в порядке, моя IDE поможет мне, это может быть правильным аргументом. Однако некоторые функции не были бы доступны без помощи вывода типов, например, анонимных типов C #.
Linq не будет так хорошо , как сейчас , без помощи умозаключений типа,
Select
например ,Этот анонимный тип будет выведен аккуратно в переменную.
Мне не нравится вывод типов в возвращаемых типах,
Scala
потому что я думаю, что ваша точка зрения здесь применима, нам должно быть ясно, что возвращает функция, чтобы мы могли более свободно использовать APIисточник
Map<String,HashMap<String,String>>
? Конечно, если вы не используете типы, то их объяснение мало что дает.Table<User, File, String>
является более информативным, и есть смысл в написании этого.Я думаю, что ответ на этот вопрос действительно прост: он экономит на чтении и записи избыточной информации. Особенно в объектно-ориентированных языках, где у вас есть тип с обеих сторон знака равенства.
Что также говорит вам, когда вы должны или не должны использовать его - когда информация не является избыточной.
источник
Предположим, кто-то видит код:
Если
someBigLongGenericType
присваивается из возвращаемого типаsomeFactoryMethod
, насколько вероятно, что кто-то, читающий код, будет замечать, если типы не совпадают точно, и с какой готовностью кто-то, кто заметил несоответствие, может распознать, было ли это намеренным или нет?Допуская вывод, язык может предложить кому-то, кто читает код, что, когда тип переменной явно указан, человек должен попытаться найти причину этого. Это, в свою очередь, позволяет людям, которые читают код, лучше сосредоточить свои усилия. Если, напротив, в подавляющем большинстве случаев, когда тип указывается, он точно совпадает с тем, что было бы выведено, тогда тот, кто читает код, может быть менее склонен замечать моменты, когда он слегка отличается ,
источник
Я вижу, что уже есть много хороших ответов. Некоторые из них я повторю, но иногда вы просто хотите выразить это своими словами. Я прокомментирую некоторые примеры из C ++, потому что это язык, с которым я наиболее знаком.
То, что необходимо, никогда не бывает неразумным. Вывод типа необходим, чтобы сделать другие языковые особенности практичными. В C ++ возможно иметь невыразимые типы.
В C ++ 11 добавлены лямбда-выражения, которые также невыразимы.
Вывод типа также поддерживает шаблоны.
Но ваши вопросы были такими: «Почему я, программист, хочу выводить тип моих переменных, когда я читаю код? Разве не быстрее, если кто-то просто прочитает тип, чем подумает, какой тип существует?»
Вывод типа удаляет избыточность. Когда дело доходит до чтения кода, иногда бывает проще и быстрее иметь избыточную информацию в коде, но избыточность может затмить полезную информацию . Например:
Программисту C ++ не нужно много знакомиться со стандартной библиотекой, чтобы определить, что я являюсь итератором,
i = v.begin()
поэтому явное объявление типа имеет ограниченную ценность. Своим присутствием он скрывает детали, которые являются более важными (например, этоi
указывает на начало вектора). Прекрасный ответ @amon дает еще лучший пример многословия, затеняя важные детали. Напротив, использование вывода типа делает более заметными важные детали.Хотя чтение кода важно, но его недостаточно, в какой-то момент вам придется прекратить чтение и начать писать новый код. Избыточность в коде делает изменение кода медленнее и сложнее. Например, скажем, у меня есть следующий фрагмент кода:
В случае, если мне нужно изменить тип значения вектора на двойное, изменив код на:
В этом случае я должен изменить код в двух местах. Сравните с выводом типа, где исходный код:
И модифицированный код:
Обратите внимание, что теперь мне нужно изменить только одну строку кода. Экстраполируйте это на большую программу, и вывод типа может распространять изменения на типы намного быстрее, чем вы можете с помощью редактора.
Избыточность в коде создает возможность ошибок. Всякий раз, когда ваш код зависит от двух частей информации, сохраняющих эквивалентность, существует вероятность ошибки. Например, есть несоответствие между этими двумя типами в этом утверждении, которое, вероятно, не предназначено:
Избыточность затрудняет распознавание намерений. В некоторых случаях вывод типа может быть легче читать и понимать, потому что он проще, чем явная спецификация типа. Рассмотрим фрагмент кода:
В случае, когда
sq(x)
возвращаетсяint
, неясно,y
является лиint
это типом,sq(x)
потому что это тип возвращаемого значения или потому, что он соответствует операторам, которые используютy
. Если я изменю другой код таким образом, чтобы онsq(x)
больше не возвращалсяint
, из этой строки неясно,y
следует ли обновлять тип . Сравните с тем же кодом, но используя вывод типа:В этом намерение ясно,
y
должен быть того же типа, который был возвращенsq(x)
. Когда код меняет тип возвращаемого значенияsq(x)
, типy
изменений автоматически совпадает.В C ++ есть вторая причина, по которой приведенный выше пример проще с выводом типа: вывод типа не может вводить неявное преобразование типа. Если тип возвращаемого значения
sq(x)
- нетint
, компилятор с молча вставляет неявное преобразование вint
. Если возвращаемый типsq(x)
является сложным типом, который определяетoperator int()
, этот скрытый вызов функции может быть произвольно сложным.источник
typeof
делает язык бесполезным. И это недостаток самого языка, который должен быть исправлен на мой взгляд.