Почему динамическая типизация так часто ассоциируется с интерпретируемыми языками?

StackOverflow https://stackoverflow.com/questions/1393883

Вопрос

Простой вопрос , ребята:Я много программирую (профессионально и лично) на скомпилированных языках, таких как C ++ / Java, и на интерпретируемых языках, таких как Python / Javascript.Лично я нахожу, что мой код почти всегда более надежен, когда я программирую на языках со статической типизацией.Однако почти каждый интерпретируемый язык, с которым я сталкиваюсь, использует динамическую типизацию (PHP, Perl, Python и т.д.).Я знаю, почему скомпилированные языки используют статическую типизацию (большую часть времени), но я не могу понять причину отвращения к статической типизации в интерпретируемом языковом дизайне.

Почему такое резкое разъединение?Является ли это частью природы интерпретируемых языков?ООП?

Это было полезно?

Решение

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

Я думаю, что здесь есть ошибочное предположение.Авторы PHP, Perl, Python, Ruby, Lua и т.д. не разрабатывали "интерпретируемые языки", они разрабатывали динамические языки и реализовывали их с помощью интерпретаторов.Они сделали это потому, что интерпретаторы гораздо проще писать, чем компиляторы.

Первая реализация Java была интерпретирована, и это статически типизированный язык.Интерпретаторы действительно существуют для статических языков:У Haskell и OCaml есть интерпретаторы, и раньше был популярный интерпретатор для C, но это было давным-давно.Они популярны, потому что позволяют REPL ( ПОВТОРЯТЬ ), что может облегчить разработку.

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

Чтобы учесть мнения других:

  • дламблин говорит:"Я никогда не считал, что в сравнении компиляции и интерпретации есть что-то особенное, что предполагает динамическую типизацию, а не статическую". Что ж, здесь вы сильно ошибаетесь.Компиляция динамических языков очень сложна.Существует в основном eval инструкция для рассмотрения, которая широко используется в Javascript и Ruby.phc компилирует PHP заранее, но нам все еще нужен интерпретатор во время выполнения для обработки evals. eval также не может быть проанализирован статически в оптимизирующем компиляторе, хотя существует классная техника если вам не нужна основательность.

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

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

  • Самый правильный ответ на данный момент - это ответ Ivo Wetzel.Однако моменты, которые он описывает, могут быть обработаны во время выполнения в компиляторе, и существует множество компиляторов для Lisp и Scheme, которые имеют этот тип динамической привязки.Но, да, это сложно.

Другие советы

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

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

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

Представьте себе следующий сценарий (на Python):

import random
foo = 1

def doSomeStuffWithFoo():
    global foo
    foo = random.randint(0, 1)

def asign():
    global foo
    if foo == 1:
        return 20
    else:
        return "Test"


def toBeStaticallyAnalyzed():
    myValue = asign()

    # A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption
    myValue += 20


doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;)
toBeStaticallyAnalyzed()

Как вы, надеюсь, можете видеть, компилятор не имел бы никакого смысла в этой ситуации.На самом деле это могло бы предупредить вас о возможности того, что "myValue" может быть чем-то иным, чем Числом.Но тогда в JavaScript это привело бы к сбою, потому что, если "myValue" является строкой, 20 также было бы неявно преобразовано в строку, следовательно, ошибки не возникло бы.Таким образом, вы можете получить тысячи бесполезных предупреждений повсюду, и я не думаю, что это входит в намерения компилятора.

Гибкость всегда имеет свою цену, вам нужно глубже взглянуть на свою программу или более тщательно ее запрограммировать, другими словами, вы являетесь КОМПИЛЯТОРОМ в ситуациях, подобных описанной выше.

Итак, ваше решение как компилятора?- Исправьте это с помощью команды "попробуй:кроме" :)

Компиляторы + Статические типы = эффективный машинный код
Компиляторы + динамические типы = неэффективный машинный код

Рассмотрим следующий псевдокод:

function foo(a, b) {
    return a+b
}

Статический язык сможет узнать (путем объявления или вывода), что a и b являются целыми числами, и скомпилирует до

%reg = addi a,b

или что-то подобное, во всяком случае.

Компилятор для динамического языка должен был бы выдавать код для
1.Проверьте их типы a и b
2.обрабатывайте каждый случай или комбинацию случаев

%reg1 = typeof a
beq %reg1, int, a_int_case
beq %reg1, float, a_float_case
beq %reg1, string, a_string_case

label a_int_case
%reg1 = typeof b
beq %reg1, int, a_int_b_int_case
beq %reg1, float, a_int_b_float_case
beq %reg1, string, a_int_b_string_case

label a_int_b_int_case
%out = addi a,b
goto done

label a_int_b_float_case
%tmp = mkfloat a
%out = addf %tmp,b
goto done

... Etc. I can't finish

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

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

(Компиляторы Jit-in-time на самом деле имеют информацию о типе и могут компилироваться вплоть до одного оператора.На самом деле они содержат больше информации, чем системы статического типа, и теоретически могут работать даже лучше.Весь ассемблер моделируется;Любое сходство с реальным кодом, который мог бы выполняться на реальной машине, является чисто случайным.)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если программист захочет, он всегда может утверждать, что переменная содержит значение заданного типа (например, при присваивании).Нет никакой причины встраивать это в язык.Конечно, это не тот вид контроля, который у вас есть для скомпилированных языков.

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

С другой стороны.Знаете ли вы какой-нибудь динамически типизированный скомпилированный (статически, не JIT) язык?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top