Любопытствуя на главной странице сайта языка скриптового программирования, я натолкнулся на этот отрывок:
Когда система становится слишком большой, чтобы держать ее в голове, вы можете добавлять статические типы.
Это заставило меня вспомнить, что во многих религиозных войнах между статическими, скомпилированными языками (например, Java) и динамическими интерпретируемыми языками (в основном Python, потому что он более используется, но это «проблема», разделяемая большинством языков сценариев), одна из жалоб на статичность Поклонники типизированных языков по сравнению с динамически типизированными языками заключаются в том, что они плохо масштабируются для больших проектов, потому что «однажды вы забудете тип возврата функции и вам придется искать его, в то время как со статически типизированными языками все явно объявлено ".
Я никогда не понимал таких утверждений, как этот. Если честно, даже если вы объявите тип возврата функции, вы можете и забудете ее после того, как написали много строк кода, и вам все равно придется вернуться к строке, в которой она объявлена с использованием функции поиска Ваш текстовый редактор, чтобы проверить это.
Кроме того, так как функции объявляются с type funcname()...
, без знания type
того, что вам придется искать по каждой строке, в которой вызывается функция, потому что вы знаете funcname
, в то время как в Python и т. П. Вы можете просто искать def funcname
или, function funcname
что происходит только один раз, в декларация.
Более того, с помощью REPL тривиально проверить функцию на тип возвращаемого значения с различными входными данными, в то время как со статически типизированными языками вам потребуется добавить несколько строк кода и перекомпилировать все, чтобы узнать объявленный тип.
Итак, кроме того, чтобы знать тип возвращаемого значения функции, который явно не является сильной стороной статически типизированных языков, как статическая типизация действительно полезна в больших проектах?
источник
Ответы:
Это не тривиально. Это не тривиальный вообще . Это тривиально только для тривиальных функций.
Например, вы можете тривиально определить функцию, в которой тип возвращаемого значения полностью зависит от типа ввода.
В этом случае на
getAnswer
самом деле нет единственного возвращаемого типа. Вы не можете написать тест, который бы вызывал это с примером ввода, чтобы узнать тип возвращаемого значения. Это всегда будет зависеть от фактического аргумента. Во время выполнения.И это даже не включает функции, которые, например, выполняют поиск в базе данных. Или делать вещи на основе пользовательского ввода. Или посмотрите глобальные переменные, которые, конечно, динамического типа. Или измените их тип возврата в случайных случаях. Не говоря уже о необходимости каждый раз проверять каждую отдельную функцию вручную.
По сути, доказательство типа возврата функции в общем случае буквально математически невозможно (проблема остановки). Только способом гарантировать тип возврата является ограничение на входе , так что при ответе на этот вопрос не подпадает под областью Проблемы Остановки отвергая программы, которые не доказуемы, и это то , что делает статическая типизация.
В статически типизированных языках есть вещи, называемые «инструментами». Это программы, которые помогают вам делать что-то с вашим исходным кодом. В этом случае, я бы просто щелкнул правой кнопкой мыши и пошел к определению, благодаря Resharper. Или используйте сочетание клавиш. Или просто наведите курсор мыши, и он скажет мне, какие типы участвуют. Мне совершенно безразлично, как копать файлы. Текстовый редактор сам по себе является патетическим инструментом для редактирования исходного кода программы.
По памяти
def funcname
не хватит в Python, так как функция может быть переназначена произвольно. Или может быть объявлен повторно в нескольких модулях. Или в классах. И т.п.Поиск файлов по имени функции - ужасная примитивная операция, которая никогда не должна требоваться. Это представляет собой фундаментальную ошибку вашей среды и инструментов. Тот факт, что вы даже подумаете о необходимости текстового поиска в Python, является серьезным аргументом против Python.
источник
Подумайте о проекте со многими программистами, который изменился за эти годы. Вы должны поддерживать это. Есть функция
Что на земле это делает? Что
v
? Откуда этот элементanswer
?Теперь у нас есть больше информации -; это нуждается в типе
AnswerBot
.Если мы перейдем на язык классов, мы можем сказать,
Теперь мы можем иметь переменную типа
AnswerBot
и вызывать метод,getAnswer
и каждый знает, что он делает. Любые изменения фиксируются компилятором до того, как будет выполнено любое тестирование во время выполнения. Есть много других примеров, но, возможно, это дает вам идею?источник
Похоже, у вас есть несколько неправильных представлений о работе с большими статическими проектами, которые могут омрачить ваше мнение. Вот несколько указателей:
Большинство людей, работающих со статически типизированными языками, используют либо IDE для языка, либо интеллектуальный редактор (например, vim или emacs), который интегрирован с инструментами для конкретного языка. В таких инструментах обычно есть быстрый способ найти тип функции. Например, в Eclipse для проекта Java существует два способа, как обычно вы можете найти тип метода:
someVariable.
), и Eclipse ищет типsomeVariable
и предоставляет раскрывающийся список всех методов, определенных в этом типе; когда я прокручиваю вниз список, тип и документация каждого из них отображаются, пока он выбран. Обратите внимание, что это очень трудно сделать с помощью динамического языка, потому что редактору трудно (или в некоторых случаях невозможно) определить тип этого типаsomeVariable
, поэтому он не может легко сгенерировать правильный список. Если я хочу использовать метод,this
я могу просто нажать Ctrl + пробел, чтобы получить тот же список (хотя в этом случае это не так сложно достичь для динамических языков).Как вы можете видеть, это несколько лучше, чем типичный инструментарий, доступный для динамических языков (не то, что это невозможно в динамических языках, поскольку у некоторых есть довольно хорошая функциональность IDE - smalltalk - это тот, который приходит на ум, - но сложнее для динамический язык и, следовательно, менее вероятно, будет доступен).
Инструменты статического языка обычно предоставляют возможности семантического поиска, то есть они могут точно находить определения и ссылки на конкретные символы без необходимости выполнения текстового поиска. Например, используя Eclipse для проекта Java, я могу выделить символ в текстовом редакторе и щелкнуть правой кнопкой мыши по нему и выбрать «перейти к определению» или «найти ссылки» для выполнения любой из этих операций. Вам не нужно искать текст определения функции, потому что ваш редактор уже точно знает, где он находится.
Однако обратное заключается в том, что поиск определения метода по тексту действительно не работает так же хорошо, как вы предлагаете, в большом динамическом проекте, поскольку в таком проекте может быть несколько методов с одним и тем же именем, и вы, вероятно, не имеете легко доступные инструменты для устранения неоднозначности, какой из них вы вызываете (потому что такие инструменты трудно написать в лучшем случае или невозможно в общем случае), поэтому вам придется делать это вручную.
Не является невозможным иметь REPL для статически типизированного языка. Напоминает пример Haskell, но есть и REPL для других статически типизированных языков. Но дело в том, что вам не нужно выполнять код, чтобы найти тип возвращаемого значения функции на статическом языке - это можно определить путем проверки без необходимости что-либо запускать.
Скорее всего, даже если вам нужно это сделать, вам не придется перекомпилировать все . Большинство современных статических языков имеют инкрементные компиляторы, которые будут компилировать только небольшую часть вашего кода, которая изменилась, так что вы можете получить почти мгновенную обратную связь для ошибок типа, если вы их сделаете. Например, Eclipse / Java будет выделять ошибки типа, пока вы их еще печатаете .
источник
You seem to have a few misconceptions about working with large static projects that may be clouding your judgement.
Ну, мне всего 14 лет, и я программирую на Android меньше года, так что это возможно, я думаю.Сравните с скажем, javascript, Ruby или Smalltalk, где разработчики переопределяют функциональность основного языка во время выполнения. Это усложняет понимание большого проекта.
У больших проектов не просто больше людей, у них больше времени. Достаточно времени, чтобы все забыли или пошли дальше.
Как ни странно, у моего знакомого есть безопасное программирование «Работа для жизни» на Лиспе. Никто, кроме команды, не может понять кодовую базу.
источник
Anecdotally, an acquaintance of mine has a secure "Job For Life" programming in Lisp. Nobody except the team can understand the code-base.
Это действительно настолько плохо? Разве персонализация, которую они добавили, не помогает им быть более продуктивными?Речь идет не о том, что вы забыли тип возврата - это всегда будет происходить. Речь идет об инструменте, который может сообщить вам, что вы забыли тип возвращаемого значения.
Это вопрос синтаксиса, который совершенно не связан со статической типизацией.
Синтаксис семейства C действительно недружелюбен, когда вы хотите посмотреть объявление, не имея в своем распоряжении специализированных инструментов. Другие языки не имеют этой проблемы. Смотрите синтаксис объявления Rust:
Любой язык может быть переведен, и любой язык может иметь REPL.
Я отвечу абстрактно.
Программа состоит из различных операций, и эти операции изложены так, как они есть, из-за некоторых предположений, которые делает разработчик.
Некоторые предположения являются неявными, а некоторые - явными. Некоторые предположения касаются операции рядом с ними, некоторые касаются операции вне их. Предположение легче определить, когда оно выражено явно и максимально близко к местам, где имеет значение его истинная ценность.
Ошибка - это проявление предположения, которое существует в программе, но не имеет места в некоторых случаях. Чтобы отследить ошибку, нам нужно выявить ошибочное предположение. Чтобы удалить ошибку, нам нужно либо удалить это предположение из программы, либо изменить что-то, чтобы это предположение действительно выполнялось.
Я хотел бы разделить предположения на два вида.
Первый тип - это предположения, которые могут или не могут быть выполнены, в зависимости от входных данных программы. Чтобы выявить ошибочные предположения такого рода, нам необходимо найти в пространстве все возможные входные данные программы. Используя обоснованные догадки и рациональное мышление, мы можем сузить проблему и искать в гораздо меньшем пространстве. Но, тем не менее, по мере того, как программа немного расширяется, ее начальное пространство ввода увеличивается с огромной скоростью - до такой степени, что ее можно считать бесконечной для всех практических целей.
Второй вид - это предположения, которые однозначно верны для всех входов или определенно ошибочны для всех входов. Когда мы определяем предположение такого рода как ошибочное, нам даже не нужно запускать программу или проверять какие-либо входные данные. Когда мы определили предположение такого рода как правильное, у нас меньше одного подозреваемого, чтобы заботиться о том, когда мы отслеживаем ошибку ( любую ошибку). Следовательно, имеет смысл иметь как можно больше предположений, относящихся к этому виду.
Чтобы поместить предположение во вторую категорию (всегда истинное или всегда ложное, независимо от входных данных), нам нужен минимальный объем информации, который должен быть доступен в месте, где сделано предположение. По всему исходному коду программы информация устареет довольно быстро (например, многие компиляторы не выполняют межпроцедурный анализ, что делает любой вызов жесткой границей для большей части информации). Нам нужен способ сохранить необходимую информацию свежим (действительным и рядом).
Один из способов заключается в том, чтобы источник этой информации был как можно ближе к месту, где она будет потребляться, но это может быть непрактичным для большинства случаев использования. Другой способ - часто повторять информацию, обновляя ее актуальность по всему исходному коду.
Как вы уже можете догадаться, статические типы - это как раз маяки информации о типах, разбросанные по всему исходному коду. Эта информация может быть использована для помещения большинства предположений о корректности типов во вторую категорию, что означает, что практически любая операция может быть классифицирована как всегда правильная или всегда неверная в отношении совместимости типов.
Когда наши типы неверны, анализ экономит наше время, так как предупреждает об ошибке раньше, чем поздно. Когда наши типы верны, анализ экономит наше время, гарантируя, что при возникновении ошибки мы можем немедленно исключить ошибки типов.
источник
Вы помните старую поговорку «мусор в мусоре», ну, это то, что помогает предотвратить статическая типизация. Это не универсальная панацея, но строгость в отношении того, какие данные подпрограмма принимает и возвращает, означает, что у вас есть уверенность, что вы правильно с ней работаете.
Так что подпрограмма getAnswer, которая возвращает целое число, не будет полезна, когда вы попытаетесь использовать ее в строковом вызове. Статическая типизация уже предупреждает вас, что вы, вероятно, ошибаетесь. (и, конечно, вы можете переопределить его, но вам нужно точно знать, что вы делаете, и указать это в коде с использованием приведения. В общем, вы не хотите этого делать - взломать в круглый колышек в квадратное отверстие никогда не работает хорошо в конце)
Теперь вы можете пойти дальше, используя сложные типы, создав класс с функциональностью руды, вы можете начать передавать их, и вы внезапно получите гораздо больше структуры в вашей программе. Структурированные программы - это те, которые намного проще правильно настроить, а также поддерживать.
источник