Почему интерпретированные langs в основном ducktyped, тогда как скомпилированные имеют сильную типизацию?

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

Ответ 1

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

Примеры:

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

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

  • Язык программирования Standard ML статически типизирован, и в нем было много компиляторов с собственным кодом, одним из лучших и наиболее активно поддерживаемых MLton, но одной из наиболее полезных реализаций является интерпретатор Moscow 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.

Вкратце:

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

  • Скомпилированный vs интерпретируется как реализация.

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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

int a

в С++ компилятор выдает код, который резервирует четыре байта памяти, а затем назначает локальный символ "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, выделяет новый буфер для символов, а затем назначает 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 байтов в буфер с четырьмя байтами и вызывает страдания.

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

:)

-nelson

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

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

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

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

Ответ 6

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

  • Проверка статической ошибки.
  • Производительность

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

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

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

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

Ответ 7

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

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

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

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

Ответ 8

Догадка:

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

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

Ответ 9

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

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