Почему интерпретируемые langs в основном утилизируются, а компилируемые имеют строгую типизацию? - PullRequest
43 голосов
/ 18 декабря 2008

Я просто не знаю, есть ли техническая причина для этого? Сложнее ли реализовать компилятор для языка со слабой типизацией? Что это?

Ответы [ 9 ]

196 голосов
/ 18 декабря 2008

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

Примеры:

  • Язык программирования Scheme динамически типизирован (так называемый утиный тип) и имеет множество десятков интерпретируемых реализаций, а также несколько хороших компиляторов с нативным кодом, включая Larceny, Gambit и PLT Scheme (которая включает в себя * 1015). * оба интерпретатор и JIT-компилятор, выполняющие плавные переходы).

  • Язык программирования Haskell статически типизирован; две наиболее известные реализации - интерпретатор HUGS и компилятор GHC . Есть несколько других достойных реализаций, равномерно распределенных между компиляцией в нативный код (yhc) и интерпретацией (Helium).

  • Язык программирования Standard ML имеет статическую типизацию и имеет множество компиляторов с нативным кодом, одним из лучших и наиболее активно поддерживаемых является MLton , но одним из самых полезных Реализация интерпретатора Москва ML

  • Язык программирования Objective Caml статически типизирован. Он поставляется только с одной реализацией (от INRIA во Франции), но эта реализация включает и интерпретатор и компилятор собственного кода.

  • Язык программирования Pascal является статически типизированным, но стал популярным в 1970-х годах из-за превосходной реализации, созданной в UCSD, основанной на интерпретаторе P-кода. В последующие годы стали доступны прекрасные компиляторы собственного кода, такие как компилятор IBM Pascal / VS для компьютеров серии 370.

  • Язык программирования C статически типизирован, и сегодня почти все реализации компилируются, но в 1980-х годах те из нас, кому посчастливилось использовать Sabre C, использовали интерпретатор.

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

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

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

  • В некоторых интерпретируемых языках многие фундаментальные операции настолько дороги, что дополнительные накладные расходы на проверку типов во время выполнения не имеют значения. Хорошим примером является PostScript: если вы собираетесь убегайте и растеризуйте кривые Безье по капле шляпы, вы не станете отказываться от проверки тега типа здесь или там.

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

Строгая типизация обычно означает, что в системе типов нет лазеек , тогда как слабая типизация означает, что система типов может быть подорвана (аннулируя любые гарантии) , Термины часто используются неправильно для обозначения статической и динамической типизации. Чтобы увидеть разницу, подумайте о C: язык проверяется на тип во время компиляции (статическая типизация), но существует множество лазеек; вы можете в значительной степени привести значение любого типа к другому типу того же размера - в частности, вы можете свободно приводить типы указателей. Паскаль был языком, который предназначался для строгой типизации, но, как известно, имел непредвиденную лазейку: вариант записи без тега.

Реализации строго типизированных языков часто приобретают лазейки со временем, обычно, так что часть системы времени выполнения может быть реализована на языке высокого уровня. Например, в Objective Caml есть функция с именем Obj.magic, которая во время выполнения просто возвращает свой аргумент, но во время компиляции преобразует значение любого типа в значение любого другого типа. Мой любимый пример - Modula-3, дизайнеры которого назвали свою конструкцию для литья типов LOOPHOLE.

В итоге:

  • Статический и динамический язык .

  • Скомпилировано и интерпретировано как реализация .

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

8 голосов
/ 18 декабря 2008

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

Однако, с поздним связыванием, вы должны искать метод, который похож на метод, который вызвал код клиента. И, конечно, со многими, многими вызовами методов в программе, это то, что делает динамические языки «медленными».

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

4 голосов
/ 18 декабря 2008

Это в значительной степени потому, что люди, которые пишут и используют интерпретированные языки, обычно предпочитают ducktyping, а люди, которые разрабатывают и используют скомпилированные языки, предпочитают строгую явную типизацию. (Я думаю, что причина консенсуса для этого была бы где-то в районе 90% для предотвращения ошибок и 10% для производительности.) Для большинства программ, написанных сегодня, разница в скорости была бы незначительной. Microsoft Word работает с p-кодом (не скомпилировано) в течение - каких 15 лет?

Лучший пример, который я могу придумать. Классическая Visual Basic (VB6 / VBA / и т. Д.) Одна и та же программа может быть написана на VB и работать с одинаковыми результатами и сопоставимой скоростью, либо скомпилирована, либо интерпретирована. Более того, у вас есть возможность объявления типа (фактически объявления переменных) или нет. Большинство людей предпочитают объявления типов, обычно для предотвращения ошибок. Я никогда не слышал и не читал нигде, чтобы использовать объявления типа для скорости. И это касается как минимум двух степеней в аппаратной скорости и емкости.

В последнее время Google получает много внимания из-за своей работы над JIT-компилятором для javascript, который не потребует изменений в языке или потребует дополнительного внимания со стороны программиста. В этом случае единственным преимуществом будет скорость.

3 голосов
/ 18 декабря 2008

Поскольку скомпилированные языки должны учитывать объем используемой памяти при компиляции.

Когда вы видите что-то вроде:

int a

в C ++, компилятор выплевывает код, который резервирует четыре байта памяти, а затем назначает локальный символ «a» для указания на эту память. Если у вас был типизированный язык сценариев, такой как javascript, интерпретатор за кадром выделяет требуемую память. Вы можете сделать:

var a = 10;  // a is probably a four byte int here
a = "hello world"; // now a is a 12 byte char array

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

int a = 10; // we now have four bytes on the stack.
a = "hello world"; // wtf? we cant push 12 bytes into a four byte variable! Throw an error!

Таким образом, компилятор останавливает компиляцию этого кода, чтобы процессор не слепо записывал 12 байтов в четырехбайтовый буфер и вызывал несчастные случаи.

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

:)

-nelson

РЕДАКТИРОВАТЬ в ответ на комментарий

Я мало что знаю о Python, поэтому не могу много говорить об этом. Но свободная типизация значительно замедляет время выполнения. Каждая инструкция, которую вызывает интерпретатор (VM), должна вычисляться и, если необходимо, приводить переменную к ожидаемому типу. Если у вас есть:

mov a, 10
mov b, "34"
div a, b

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

3 голосов
/ 18 декабря 2008

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

2 голосов
/ 18 декабря 2008

Существует две основные причины использовать статическую типизацию вместо утки:

  1. Проверка статической ошибки.
  2. Performance

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

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

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

Это объясняет, почему существует несколько динамически типизированных, скомпилированных языков.

1 голос
/ 18 декабря 2008

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

Возьмем, к примеру, следующий код в Perl:

$x=1;
$x="hello";
print $x;

Очевидно, что компилятору довольно сложно определить, какой тип $ x действительно имеет в данный момент времени. Во время заявления на печать, работа должна быть сделана, чтобы понять это. В статически типизированном языке тип полностью известен, поэтому производительность во время выполнения может быть увеличена.

0 голосов
/ 18 декабря 2008

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

Иногда в игре гораздо больше, чем просто печатать. Взять, к примеру, ActionScript. В версии 3.0 введена более строгая типизация, но опять же ECMAScript позволяет вам изменять классы так, как вы считаете нужным во время выполнения, а ActionScript поддерживает динамические классы. Очень аккуратно, но тот факт, что они заявляют, что динамические классы не должны использоваться в "стандартных" сборках, означает, что это не-нет, когда вам нужно быть осторожным.

0 голосов
/ 18 декабря 2008

Догадка:

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

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

...