Преимущества динамических языков в реальной жизни?

Я изучаю несколько возможностей для разработки новой системы (веб-приложения).

Я "старомодный" парень, объектно-ориентированный в природе (преобразованный из процедурного много лет назад). Я играл с Python и немного изучил Ruby, но, честно говоря, меня привлекло использование инструментов Microsoft (C#, ASP.NET MVC). Вся эта типизация во время выполнения, отсутствие ошибок компилятора в базовых вещах и т. Д. Просто усложняет мою жизнь, когда дело доходит до создания больших сложных приложений.

Я постоянно слышу, как люди говорят о замечательных вещах, которые вы можете делать с динамическими языками, но за исключением примеров с собаками, кошками и того, как быстро вы можете кодировать классный способ подсчета вещей, "промышленная сила" Visual Studio, кажется, просто устраняет изящные мелочи, которые предлагают динамические языки, особенно теперь, когда у вас есть бесплатные экспресс-версии VS и полные версии, доступные бесплатно для стартапов.

Мне кажется, что я чего-то здесь упускаю, потому что большие приложения действительно разрабатываются с помощью динамических языков, так что же это за замечательные вещи, которые эти языки позволяют вам делать, глядя на большие сложные приложения? Что может заставить вас отдать силу VS?

9 ответов

Решение

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

Преимущества динамических языков.

  1. Нет компиляции, нет сборки. Просто код и тест с последующим развертыванием в производство.

  2. Немедленное удовлетворение. Не тратя времени на то, чтобы разобраться, что может быть вызовом API. Просто введите его в командной строке Python >>> и посмотрите, что он на самом деле делает.

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

  4. Меньше кода. Динамический язык самоанализ уменьшает громкость источника. Я не пишу это в своих приложениях; Я полагаюсь на рамки, чтобы сделать это для меня. Но основанный на фреймворке код часто очень короткий; нет повторяющихся объявлений, которые так распространены в Java, где вы должны повторять вещи в конфигурации XML.

  5. Никаких загадок. Как мы говорим в сообществе Python: "Используй источник, Люк". Нет никакой двусмысленности в том, что делает фреймворк или что на самом деле означает API.

  6. Абсолютная гибкость. Поскольку наши требования меняются, нам не приходится бороться с разрушительными изменениями, которые нарушают всю архитектуру. Мы можем - тривиально - внести изменения в несколько классов, потому что Python Duck Typing избавляет от необходимости модифицировать отсутствующие определения интерфейса там, где мы не думали, что они будут необходимы. Их просто нет; код, который мы не написали, - это код, который мы не должны исправлять или поддерживать.

  7. Упругость. Когда у наших актуариев есть умственный пердеть, нам не нужно тратить месяцы, чтобы понять, как интегрировать эту новую, более сложную модель андеррайтинга в приложения. Практически все можно втиснуть. Опять же, это строго следствие печати по утке. Мы освобождены от принудительного встраивания его в архитектуру, которая не могла предвидеть новую бизнес-модель.

  8. Поскольку источником является приложение, источником может быть его собственный файл конфигурации. У нас нет файлов конфигурации XML или INI в каком-то внешнем синтаксисе. У нас есть файлы конфигурации в Python. Фреймворк Django делает это, и мы следуем их примеру. У нас есть очень сложные макетные декларации данных для демонстрации продаж и модульного тестирования. Суперсложные данные на самом деле представляют собой набор объектов Python, которые поступили бы из базы данных, за исключением того, что мы не загружали базу данных. Проще просто настроить конструктор объекта Python вместо загрузки базы данных SQL.

[BTW. После 30 с лишним лет разработки программного обеспечения на Cobol, Fortran, PL/I, Java, C, C++ я просто устал от относительно низкоуровневой оптимизации рук, которая требуется большинству компилируемых языков. Несколько лет назад я прочитал комментарий о неэффективности большинства компиляторов: он побуждает нас создавать сложные системы сборки, чтобы обойти ограничения компилятора. Нам нужно только make так как cc так медленно.]


редактировать

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

Если вы хотите написать много кода на основе неправильно понятого API, то может помочь динамический язык. Вы можете написать много кода, который аварийно завершает работу на одном языке: C#, VB, C++, Java или Python. Вы всегда можете написать код, который не будет работать.

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

Python может заранее предупредить вас, что код не будет работать. Как правило, вы не можете заставить его работать в интерактивном режиме. Тем не менее, вы все равно можете написать много кода, который не проходит все модульные тесты.

Статическая типизация является формой преждевременной оптимизации. Это заставляет вас принимать подробные решения заранее, когда у вас может не хватить знаний для их принятия. Это особенно не помогает корректности программы, если вы не создаете достаточно типов, чтобы иметь логический смысл. Это затрудняет изменение структур данных на лету.

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

Система типов Common Lisp хороша для этого. Все объекты данных знают свой тип, и вы можете явно указать этот тип, если хотите.

Модель исполнения с циклом eval позволяет очень легко тестировать подпрограммы по мере их написания. Вам не нужно явно писать тесты заранее (хотя ничто не мешает вам сделать это); Вы можете написать их и выполнить на лету (а затем вы можете преобразовать их в набор тестов - думайте об этом как о разработке дополнительных тестов).

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

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

В целом, я предпочитаю говорить об "интерактивных" языках, а не о "динамических" языках. Когда у вас есть только цикл редактирования / компиляции / запуска, любой оборот занимает много времени. Ну хотя бы по порядку "нужно сохранить, скомпилировать, проверить отчет компиляции, запустить тест, проверить результаты теста".

С помощью интерактивного языка обычно легко изменить небольшую деталь, а затем сразу же проверить результаты. Если ваши тестовые прогоны все еще занимают много времени, вы не так много выиграли, но обычно вы можете тестировать на более мелких случаях. Это способствует быстрому развитию. Если у вас есть известная правильная реализация, это также помогает оптимизации, поскольку вы можете быстро разрабатывать и тестировать свои новые, улучшенные функции и экспериментировать с различными представлениями или алгоритмами.

Вот часть моего ответа на предыдущий, похожий вопрос ( я знаю C#. Буду ли я более продуктивно работать с Python?):

Я сам из C#/.NET. Начал программировать в.NET в ок. 2001, и примерно в то же время был представлен Python. В 2001 году мое время, проведенное в C# по сравнению с Python, составило около 90% C# / 10% Python. Теперь соотношение составляет 5% C# / 95% Python. В моей компании мы по-прежнему поддерживаем линейку продуктов на основе.NET. Но все новое основано на Python.

Мы создали нетривиальные приложения на Python.

По моему опыту, что делает меня более продуктивным в Python против C#, это:

  • Это динамический язык. Использование динамического языка часто позволяет удалить целые архитектурные слои из вашего приложения. Динамическая природа Pythons позволяет создавать многократно используемые абстракции высокого уровня более естественным и гибким (по синтаксису) способом, чем в C#.
  • Библиотеки. Стандартные библиотеки и множество библиотек с открытым исходным кодом, предоставляемых сообществом, отличаются высоким качеством. Диапазон приложений, для которых используется Python, означает, что диапазон библиотек широк.
  • Ускоренный цикл разработки. Отсутствие шага компиляции означает, что я могу тестировать изменения быстрее. Например, при разработке веб-приложения сервер dev обнаруживает изменения и перезагружает приложение при сохранении файлов. Запуск модульного теста из моего редактора - всего лишь нажатие клавиши, и он выполняется мгновенно.
  • "Легкий доступ" к часто используемым функциям: списки, списки, генераторы, кортежи и т. Д.
  • Менее многословный синтаксис. Вы можете создать веб-каркас Python на основе WSGI с меньшим количеством строк кода, чем ваш обычный.NET web.config файл:-)
  • Хорошая документация. Хорошие книги.

Мой опыт

Я работал с обоими, вероятно, около десяти лет с каждым профессионально, всего.

Я тратил гораздо больше времени на то, чтобы заставить статически типизированные языки понять, что я хочу сделать (возможно, недели - возможно, всего месяцы), чем потратил на исправление ошибок, вызванных ошибками динамического типа (возможно, час или два в год?).

Исследования

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

тоталитарный

Статический анализ типа полезен в некоторых ситуациях. Я не думаю, что это не должно быть встроено в ваш язык. Это должен быть инструмент в вашей интерактивной вычислительной среде, наряду с вашими тестами, вашим REPL, вашими рефакторами, вашими документами, вашим грамотным кодированием и т. Д.

Системы статического типа могут заставить вас думать о полезных вещах. Это особенно верно в таких языках, как Haskell с классами типов и монадами (которые, признаюсь, до сих пор не совсем понятны). Это хорошо, но я считаю, что тоталитарно полагать, что это всегда хорошо. Вы должны подумать об этом, когда это уместно. Язык не должен заставлять вас думать об этом или осознавать это с самого начала развития.

Слишком ограничительный и недостаточно ограничительный

Системы статического типа, не являющиеся полными по Тьюрингу, имеют ограниченную выразительность. Зачем запечатывать это на языке, используя новый специальный предметно-ориентированный язык только для разговоров о типах? Теперь ваш язык устанавливает точный уровень выразительности, к которому у вас есть доступ. Хочу больше? Вам нужно переписать свой код или обновить язык. Хотите меньше? Вам не повезло - используйте другой язык.

Вместо этого, почему бы не использовать базовый динамический язык для описания типов и систем типов, когда вы хотите? Как библиотека. Это более выразительно; более мощный (при желании); более гибкий; и означает меньший базовый язык.

Boilerplate

Языки со статической типизацией, кажется, поощряют создание шаблонов или кода. Я не уверен, если это присуще. Возможно, достаточно мощная система макросов преодолеет это. Я сравниваю состояние макетов для тестирования в Swift с состоянием цели c.

монолитный

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

Напротив, в интерактивных вычислительных средах вы не создаете новое приложение, а вместо этого расширяете систему, чтобы она делала больше. Системы, которые я знаю (машины Lisp, Smalltalk и Unix - их инструменты имеют динамически типизированный интерфейс между ними), используют динамическую типизацию для сборки деталей.

Мы должны создавать крошечные расширения для целого, а не создавать новые целые приложения, поэтому эта монолитная тенденция, если она существует, наносит ущерб.

скорость

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

Долгосрочный

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

Интерактивные Оболочки! Это огромный прирост производительности. Просто запустите irb/python, чтобы сидеть перед интерактивной оболочкой (для ruby ​​и python соответственно). Затем вы можете в интерактивном режиме протестировать свои классы, функции, поэкспериментировать с выражениями (отлично подходит для регулярных выражений), различным синтаксисом и алгоритмами. Это настоящая игровая площадка для программистов и отличный инструмент для отладки.

Просто мне два цента об ошибках:

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

Обнаруживаемые компилятором ошибки - это те ошибки, которые вы обнаружите, когда будете выполнять различные автоматические тесты (модульные, функциональные и т. Д.), И те, которые вы должны написать в любом случае. Вероятно, Линус Торвальдс может сказать: " Регрессионное тестирование"? Что это? Если он компилируется, это хорошо, если он загружается, он идеален, но у него есть тысячи тестеров, которые сделают за него работу. А с помощью интерактивных оболочек вы получаете автоматическая обратная связь о вашем синтаксисе, например, когда вы компилируете приложение, но код выполняется на месте.

Мне тоже нравится статическая типизация. Но я не нахожу, что я так сильно скучаю, когда пишу на Python (если используемые мной классы хорошо документированы - и я бы сказал, что никакая языковая функция никогда не спасет нас от плохой документации), Также я не скучаю по большинству динамических возможностей Python при написании C++ (лямбда, я скучаю: привожу C++0x, даже если синтаксис ужасен. И перечислите понимание).

Для обнаружения ошибок большинство ошибок "передан неправильный тип" и "неправильный метод называется" не являются тонкими, поэтому основное отличие состоит в том, что исключения заменяют ошибки компилятора. Вы должны убедиться, что вы действительно выполняете каждый путь к коду и все важные пути к данным, но, конечно, вы делаете это в любом случае для серьезных проектов. Я подозреваю, что редко бывает трудно проверить все классы, которые могут быть присвоены данной переменной. Основная проблема заключается в том, что люди, привыкшие к C++, научились опираться на компилятор и не стараются избегать больших классов ошибок, которые компилятор поймает. В Python вы можете думать об этом во время написания кода или подождать, пока не начнутся тесты, чтобы узнать об этом. Аналогично, операция "автоматического рефакторинга" в C++ "измените параметры и посмотрите, что не скомпилируется", нуждается в некоторой модификации в Python, если вы хотите найти все сайты вызовов.

Поэтому вам необходимо убедиться, что вы выполняете правильное подмножество тестов, поскольку полный модульный тест большого приложения Python займет гораздо больше времени, чем допустимо на этапе "компиляции" вашего текущего кода C++ /compile/code/ цикл компиляции / кода / компиляции / тестирования. Но это точно та же проблема, что и нежелание перестраивать все в C++.

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

Кроме того, стоит помнить, что многие ошибки и предупреждения компилятора C++ на самом деле не связаны с вашим дизайном, а связаны с тем, что существует так много разных способов написания кода, который выглядит правильно, но ведет себя совершенно неправильно. Программисты Python не нуждаются в предупреждении, что они случайно вставили триграф или набрали указатель типа, потому что у них нет психотического препроцессора или оптимизаций, основанных на строгом алиасинге.

Рассмотрим случай, когда у вас есть подпрограмма, которая принимает один аргумент:

sub calculate( Int $x ){ ... }

Теперь ваши требования меняются, и вам приходится иметь дело с несколькими аргументами:

multi sub calculate( Int $x ){ ... }
multi sub calculate( Int @x ){
  my @ret;
  for @x -> $x {
    push @ret, calculate( $x );
  }
  return @ret;
}

Обратите внимание, что было мало изменений между разными версиями.

А что если вы узнали, что вам действительно следовало использовать числа с плавающей запятой:

multi sub calculate( Num $x ){ ... }
multi sub calculate( Num @x ){
  my @ret;
  for @x -> $x {
    push @ret, calculate( $x );
  }
  return @ret;
}

Это было меньшее изменение, чем раньше, и обратите внимание, что любой фрагмент кода, который вызывает эти подпрограммы, продолжит работать без единого изменения.

Эти примеры были написаны в Perl6

Я знаю, что это было давно, но принятый ответ перечисляет свойства, которые не ограничиваются языками с динамической типизацией. Например, #4 (меньше кода) также верно для Scala (если я правильно помню). Это определенно верно для Groovy (который построен на основе Java, так что технически Groovy является как статическим, так и динамическим). Единственное, с чем я согласен, это # ​​7 (устойчивость)

Из википедии,

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

Исходя из моего опыта, преимущества динамических языков заключаются в меньшем количестве кода / более эффективном коде и лучших "лучших" методах (например, в культуре строгого тестирования)... вещах, которые не относятся к динамическим языкам.

Говоря о культуре тестирования, некоторые энтузиасты динамического языка (в частности, Ruby) утверждают, что все тесты, которые они пишут (заражение тестами), позволяют им легко реорганизовывать приложения. Я склонен не покупать это заявление - плохо написанные тесты (которые очень, очень легко написать), как правило, становятся кошмаром обслуживания.

Подводя итог: динамические языки, как правило, способствуют быстрой доставке продукта, приложение может быть или не быть простым в обслуживании, но ужасно для высокопроизводительных приложений (статически скомпилированные приложения работают намного быстрее)

Другие вопросы по тегам