Почему динамические языки затрудняют работу с большими кодовыми базами? [Дубликат]

    

У этого вопроса уже есть ответ:

    

Большие базы данных сложнее поддерживать, когда они написаны на динамических языках. По крайней мере, это то, что Евгений Брикман, ведущий разработчик, привносящий Play Framework в LinkedIn, говорит в видео-презентации, записанные на JaxConf 2013 (минута 44).

Почему он это говорит? Каковы причины?

221 голос | спросил Jus12 17 TueEurope/Moscow2013-12-17T14:09:46+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 14:09:46 +0400 2013, 14:09:46

6 ответов


549
  

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

Предостережение: я не смотрел презентацию.

Я работал в проектных комитетах для JavaScript (очень динамичный язык), C # (в основном статический язык) и Visual Basic (который является как статическим, так и динамическим), поэтому у меня есть ряд мыслей по этому вопросу; слишком много, чтобы легко вписаться в ответ здесь.

Позвольте мне начать с того, что трудно поддерживать большую базу кода, период . Большой код трудно писать независимо от того, какие инструменты у вас имеются. Ваш вопрос не означает, что поддержание большой базы кода в статически типизированном языке «легко»; скорее, вопрос предполагает только то, что проблема еще сложнее поддерживать большую базу кода на динамическом языке, чем на статическом языке. Тем не менее, есть причины, по которым усилия, затраченные на поддержание большой базы кода на динамическом языке, несколько больше, чем усилия, затрачиваемые на статически типизированные языки. Я изучу несколько из них в этом сообщении.

Но мы опережаем себя. Мы должны четко определить, что мы подразумеваем под «динамическим» языком; «динамическим» языком я имею в виду противоположность «статического» языка.

«Статически типизированный» язык - это язык, предназначенный для облегчения автоматической проверки правильности инструментом, который имеет доступ только к исходному коду, а не к запущенному состоянию программы. Факты, которые выведены инструментом, называются «типами». Разработчики языка создают набор правил о том, что делает программу «безопасным по типу», и инструмент стремится доказать, что программа соответствует этим правилам; если это не так, то возникает ошибка типа.

«Динамически типизированный» язык контрастностью - это тот, который не предназначен для облегчения такого рода проверки. Значение данных, хранящихся в любом конкретном месте, может быть легко определено только при проверке во время работы программы.

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

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

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

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

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

  • Модуляция : код факторизуется в «модулях», где каждый модуль несет четкую ответственность. Действие кода может быть документировано и понято, если пользователь не должен понимать его детали реализации.
  • Инкапсуляция . Модули делают различие между их «публичной» площадью поверхности и их «частными» деталями реализации, так что последние могут быть улучшены без ущерба для правильности программы в целом.
  • Повторное использование . Когда проблема решена правильно один раз, она решается на все время; решение может быть повторно использовано при создании новых решений. Такие методы, как создание библиотеки служебных функций или создание функциональности в базовом классе, которые могут быть расширены производным классом или архитектуры, которые поощряют составление, являются всеми методами повторного использования кода. Опять же, дело в том, чтобы снизить затраты.
  • Аннотации . Код аннотируется для описания допустимых значений, которые могут входить в переменную, например.
  • Автоматическое обнаружение ошибок . Команда, работающая над большой программой, является разумной для создания устройства, которое определяет раннее время, когда была выполнена ошибка программирования, и сообщает вам об этом, чтобы его можно было быстро исправить, ошибка усугубляется большим количеством ошибок. В эту категорию попадают такие методы, как написание набора тестов или запуск статического анализатора.

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

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

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

Возьмем, к примеру, JavaScript. (Я работал с оригинальными версиями JScript в Microsoft с 1996 по 2001 год.) Постепенная цель JavaScript заключалась в том, чтобы обезьяна танцевала, когда вы ее обманывали. Скрипты часто были одной строкой. Мы рассмотрели десять строк сценариев, которые были довольно нормальными, сотни строк сценариев были огромными, а тысячи строк сценариев были неслыханными. Язык был абсолютно не предназначен для программирования в целом, и наши решения по внедрению, целевые показатели производительности и т. Д. Основывались на этом предположении.

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

  • Нет системы модуляции; нет классов, интерфейсов или даже пространств имен. Эти элементы находятся на других языках, чтобы помочь организовать большие кодовые базы.
  • Система наследования - наследование прототипов - слабо и плохо понимается. Отнюдь не очевидно, как правильно строить прототипы для глубоких иерархий (капитан - это своего рода пират, пират - это своего рода человек, человек - своего рода вещь ...) в готовом виде JavaScript.
  • Нет инкапсуляции вообще; каждое свойство каждого объекта получается до конструкции for-in и может быть изменено по желанию любой частью программы.
  • Невозможно аннотировать любые ограничения на хранение; любая переменная может содержать любое значение.

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

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

  • Код может модифицироваться на основе пользовательского ввода с помощью таких средств, как eval или добавления новых блоков script в динамический DOM-браузер. Любой инструмент статического анализа может даже не знать, какой код составляет программу!

  • И так далее.

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

  • Они пишут тестовые примеры для каждого идентификатора, когда-либо использовавшегося в программе. В мире, где опечатки молча игнорируются, это необходимо. Это стоимость.
  • Они пишут код на языках с проверкой типов и компилируют их в JavaScript, например TypeScript.
  • Они используют фреймворки, которые поощряют программирование в стиле, более поддающемся анализу, более поддающемуся модуляции и с меньшей вероятностью вызывают общие ошибки.
  • У них хорошая дисциплина в отношении соглашений об именах, разделении обязанностей, о том, что представляет собой открытая поверхность данного объекта и т. д. Опять же, это стоимость; эти задачи будут выполняться компилятором на типичном статически типизированном языке.

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

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

ответил Eric Lippert 17 TueEurope/Moscow2013-12-17T20:43:42+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 20:43:42 +0400 2013, 20:43:42
57

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

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

Другие считают, что это ограничивает их творческую выразительность, замедляет развитие и вводит работу, которую должен выполнять компилятор (например, через вывод типа), или вообще не нужен (они просто не забудут придерживаться строк). Одна из проблем заключается в том, что люди довольно плохо предсказывают, какие ошибки они будут делать: почти все переоценивают свои способности, часто грубо. Более коварно, проблема постепенно ухудшается, чем больше ваша кодовая база - большинство людей могут, по сути, помнить, что имя клиента - это строка, но добавить к нему еще 78 объектов, все с идентификаторами, некоторые с именами, а некоторые с последовательным «числа», некоторые из которых действительно являются числовыми (требуют вычисления для их выполнения), но другие из них требуют, чтобы буквы были сохранены, и через некоторое время может быть довольно сложно запомнить, действительно ли поле, которое вы читаете, оценивать значение int или нет.

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

ответил Kilian Foth 17 TueEurope/Moscow2013-12-17T14:22:25+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 14:22:25 +0400 2013, 14:22:25
30

Почему бы вам не попросить автора этой презентации? Это его требование, в конце концов, он должен поддержать это.

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

Верно, что проекты, написанные на динамических языках, как правило, меньше проектов, написанных на статически типизированных языках, но это красная селедка: большинство проектов, написанных на статически типизированных языках, как правило, написаны на таких языках, как Java или C, которые не очень выразительны. В то время как большинство проектов, написанных на динамических языках, как правило, написаны на очень выразительных языках, таких как Scheme, CommonLisp, Clojure, Smalltalk, Ruby, Python.

Итак, причина, по которой эти проекты меньше, заключается не в том, что вы не можете писать большие проекты на языках dynamic , потому что вам не нужно для написания больших проектов в выразительных языках â € | просто требуется гораздо меньше строк кода, а тем более сложность - сделать то же самое на более выразительном языке.

Проекты, написанные в Haskell, например, также имеют тенденцию быть довольно маленькими. Не потому, что вы не можете писать большие системы в Haskell, а просто потому, что у вас нет .

Но давайте по крайней мере взглянем на то, что система статического типа может предложить для написания больших систем: система типов запрещает вам писать определенные программы. Это его работа. Вы пишете программу, представляете ее для проверки типа, а контролер типа говорит: «Нет, вы не можете написать это, извините». И, в частности, системы типов разработаны таким образом, что средство проверки типов не позволяет писать «плохие» программы. Программы с ошибками. Итак, в этом смысле да, система статического типа помогает в разработке больших систем.

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

IOW: целью системы типов является ограничение выразительности.

Но что, если одна из тех отвергнутых программ фактически решает нашу проблему в элегантном, легко поддерживаемом режиме? Тогда мы не можем записать эту программу.

Я бы сказал, что это в основном «притворство»: статически типизированные языки ограничивают вас от написания плохих программ за счет того, что иногда также мешают вам писать хорошие программы. Динамические языки не мешают вам писать хорошие программы за счет того, чтобы не мешать вам писать плохие программы.

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

ответил Jörg W Mittag 17 TueEurope/Moscow2013-12-17T15:54:47+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 15:54:47 +0400 2013, 15:54:47
11

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

ответил MikeFHay 17 TueEurope/Moscow2013-12-17T17:19:45+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 17:19:45 +0400 2013, 17:19:45
6

Рассмотрим большую базу кода, включая привязки базы данных и богатую testuite и позвольте мне выделить несколько преимуществ статических языков по динамическим языкам. (Некоторые примеры могут быть идиосинкразическими, а не применяются к любому статическому или динамическому языку.)

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

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

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

Конечно, есть компромисс: пока можно много разоблачить информации в системе типов и, таким образом, возможность писать надежных программ, было бы сложно совместить это с гибкий ~ API.

Вот несколько примеров информации, которую можно кодировать в типе Система:

â € " Корректность верности компилятор может гарантировать, что значение   передан «только для чтения» на процедуру.

â € " Схема базы данных компилятор может гарантировать, что код привязывает   программа в базу данных соответствует определению базы данных. Эта   очень полезно при изменении этого определения. (Коммунальных!)

â € " Системные ресурсы компилятор может гарантировать, что код с использованием   система resourcce только делает это, когда ресурс находится в правильном   государство. Например, можно закодировать атрибут close   или open файла в системе типов.

¹ Не полезно различать компилятор и   интерпретатор здесь, если такая разница существует.

ответил user40989 17 TueEurope/Moscow2013-12-17T18:14:20+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 18:14:20 +0400 2013, 18:14:20
3

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

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

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

ответил meriton 17 TueEurope/Moscow2013-12-17T20:32:32+04:00Europe/Moscow12bEurope/MoscowTue, 17 Dec 2013 20:32:32 +0400 2013, 20:32:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132