Действительно ли Ruby является интерпретируемым языком, если все его реализации скомпилированы в байт-код?

В выбранном ответе на этот вопрос о Blue Ruby Чак говорит:

Все текущие реализации Ruby скомпилированы в байт-код. Вопреки утверждениям SAP, начиная с Ruby 1.9, сама MRI включает в себя компилятор байт-кода, хотя возможность сохранения скомпилированного байт-кода на диск исчезла где-то в процессе слияния виртуальной машины YARV. JRuby скомпилирован в файлы Java .class. У меня мало подробностей о MagLev, но, кажется, можно с уверенностью сказать, что он пойдет по этому пути.

Я запутался в этой проблеме компиляции / интерпретации в отношении Ruby.

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

Но если все реализации Ruby теперь скомпилированы, будет ли справедливо сказать, что Ruby является интерпретируемым языком? Или я что-то недопонимаю?

7 ответов

Решение

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

В настоящее время почти каждый язык "компилируется", если считать байт-код компилируемым. Даже Emacs Lisp компилируется. Ruby был особым случаем, потому что до недавнего времени он не был скомпилирован в байт-код.

Я думаю, что вы правы, ставя под сомнение полезность описания языков как "скомпилированных" и "интерпретированных". Однако одно полезное различие заключается в том, создает ли язык машинный код (например, ассемблер x86) непосредственно из пользовательского кода. C, C++, многие Lisps и Java с включенным JIT делают, но Ruby, Python и Perl этого не делают.

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

Действительно тонкий вопрос... Раньше считалось, что "интерпретируемые" языки были проанализированы и преобразованы в промежуточную форму, которая выполнялась быстрее, но "машина", выполняющая их, была довольно специфичной для языка программой. Вместо этого "скомпилированные" языки были переведены в инструкции машинного кода, поддерживаемые компьютером, на котором они выполнялись. Раннее различие было очень основным - статический и динамический объем. В статически типизированном языке ссылка на переменную может быть в значительной степени разрешена по адресу памяти в нескольких машинных инструкциях - вы точно знали, где в кадре вызова указана переменная. В динамически типизированных языках вы должны были искать (вверх по списку А или вверх по вызывающему кадру) ссылку. С появлением объектно-ориентированного программирования не непосредственный характер ссылки расширился до многих других понятий - классов (типов), методов (функций), даже синтаксической интерпретации (встроенных DSL, таких как regex).

Различие, по сути, восходящее к концу 70-х годов, было не столько между компилируемыми и интерпретируемыми языками, сколько в том, запускались ли они в компилированной или интерпретируемой среде. Например, Паскаль (первый язык высокого уровня, который я изучил) работал в UC Berkeley сначала на интерпретаторе pxp Билла Джоя, а затем на компиляторе он написал pcc. Один и тот же язык, доступный как в скомпилированной, так и в интерпретируемой среде.

Некоторые языки более динамичны, чем другие, значение чего-либо - типа, метода, переменной - зависит от среды выполнения. Это означает, что скомпилированный или нет существенный механизм времени выполнения, связанный с выполнением программы. Forth, Smalltalk, NeWs, Lisp, все были примерами этого. Первоначально эти языки требовали так много механизмов для выполнения (по сравнению с C или Fortran), что они были естественными для интерпретации.

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

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

Современные языки объединяют все эти инновации. Некоторые имеют динамическую открытую природу традиционных интерпретируемых языков типа "ты не знаешь, что получишь до времени выполнения" (ruby, lisp, smalltalk, python, perl(!)), Некоторые пытаются иметь строгую спецификацию, позволяющую глубокое обнаружение статических ошибок на основе типов традиционных скомпилированных языков (java, scala). Все компилируются в фактические машинно-независимые представления (JVM), чтобы получить семантику однократной записи в любом месте.

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

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

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

Байт-код сам по себе является лишь промежуточным этапом между литеральным кодом, написанным пользователем и виртуальной машиной, хотя он все еще должен интерпретироваться виртуальной машиной (как это делается с Java в JVM и PHP с кешем кода операции).

Вы можете запускать программы Ruby в интерактивном режиме, используя irb, Interactive Ruby Shell. Хотя он может генерировать промежуточный байт-код, он, конечно, не является "компилятором" в традиционном смысле.

Возможно, это немного не по теме, но...

Iron Ruby является реализацией ruby ​​на основе.net и поэтому обычно компилируется в байтовый код, а затем JIT компилируется в машинный язык во время выполнения (т.е. не интерпретируется). Также (по крайней мере, с другими языками.net, поэтому я предполагаю, что с ruby) ngen можно использовать для заблаговременной генерации скомпилированного собственного двоичного кода, так что это фактически версия ruby-кода, скомпилированная машинным кодом.

Что касается информации, которую я получил от RubyConf 2011 в Шанхае, Matz разрабатывает "MRuby" (расшифровывается как Ruby Matz) для работы на встроенных устройствах. По словам Матца, MRuby предоставит возможность компилировать код ruby ​​в машинный код для повышения скорости и уменьшения использования (ограниченных) ресурсов на встроенных устройствах. Таким образом, существуют различные виды реализации Ruby, и определенно не все они просто интерпретируются во время выполнения.

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