Может ли компилируемый язык быть гомойным?
По определению слово homoiconic означает:
То же представление кода и данных
В LISP это означает, что вы могли бы иметь цитируемый список и оценить его, поэтому (car list)
будет функция и (cdr list)
аргументы. Это может происходить как во время компиляции, так и во время выполнения, однако для этого требуется интерпретатор.
Возможно ли, что скомпилированные языки без интерпретатора времени компиляции также могут быть гомоиконическими? Или понятие гомойконичности ограничено переводчиками?
10 ответов
"Homoiconic" - это своего рода нечеткая конструкция. "Код - это данные" немного понятнее.
В любом случае, первое предложение в Википедии для Homoiconic не так уж и плохо. Это говорит о том, что язык должен иметь представление источника, используя его структуры данных. Если мы забываем "строки" в качестве исходного представления (это тривиально и не очень полезно иметь полезную концепцию "гомо-звонное"), тогда в Лиспе есть списки, символы, числа, строки и т. Д., Которые используются для представления исходного кода. Интерфейс функции EVAL определяет тип представления источника, на котором работает язык. В этом случае, Лисп, это не строки. EVAL ожидает, что обычное разнообразие структур данных и правила оценки Lisp определяют, что строка оценивает себя (и, следовательно, не будет интерпретироваться как выражение программы, а только как строковые данные). Число также оценивает себя. Список (грех 3.0) - это список символа и числа. Правила оценки говорят, что этот список с символом, обозначающим функцию в качестве первого объекта, будет оцениваться как приложение функции. Существует несколько таких правил оценки для данных, специальных операторов, приложений макросов и приложений функций. Вот и все.
Чтобы было понятно: в Лиспе функция EVAL определена поверх структур данных Лисп. Он ожидает структуру данных, оценивает ее в соответствии со своими правилами оценки и возвращает результат - снова используя свои структуры данных.
Это соответствует определению homoiconic: исходный код имеет нативное представление с использованием типов данных Lisp.
Теперь интересная часть заключается в следующем: не важно, как реализовано EVAL. Все, что имеет значение, - это то, что он принимает исходный код с использованием структур данных Lisp, что он выполняет код и возвращает результат.
Так что вполне законно, что EVAL использует компилятор.
(EVAL code) = (run (compile-expression code))
Вот как работает несколько систем Lisp, у некоторых даже нет Интерпретатора.
Итак, "Homoiconic" говорит, что код SOURCE имеет представление данных. Это НЕ говорит, что во время выполнения этот исходный код должен интерпретироваться или что выполнение основано на этом исходном коде.
Если код скомпилирован, ни компилятор, ни интерпретатор не требуются во время выполнения. Они понадобятся только в том случае, если программа захочет оценить или скомпилировать код во время выполнения - что часто не требуется.
Lisp также предоставляет примитивную функцию READ, которая переводит внешнее представление данных (S-выражения) во внутреннее представление данных (данные Lisp). Таким образом, его также можно использовать для перевода внешнего представления исходного кода во внутреннее представление исходного кода. Lisp не использует специальный синтаксический анализатор для исходного кода - поскольку код является данными, существует только READ.
Да. LISP может быть скомпилирован в собственный двоичный файл
Мне кажется странным вопрос:
Во-первых, гомойконическая часть - это представленный интерфейс для программиста. Суть языков в том, что они абстрагируют функциональность более низкого уровня, которая сохраняет ту же семантику, что и представление более высокого уровня (хотя и другим способом).
Точка машинного кода DSM является хорошей точкой, но она обеспечивает:
- Представленный синтаксис и семантика гомоичны
- Перевод в форму более низкого уровня (машинный код или интерпретированный или иным образом) не удаляет никакой оригинальной семантики.
почему реализация нижнего уровня имеет значение здесь?
Также:
скомпилированные языки без интерпретатора времени компиляции
Без какой-либо программы, которая его интерпретирует, она должна была бы быть родной для ЦП, поэтому родной язык ЦП должен был бы быть гомо-звуковым (или виртуальной машиной, выполняющей код).
Языки без интерпретации во время компиляции... были бы довольно ограничены... так как они не будут компилироваться вообще.
Но я не эксперт, и, возможно, упускаю суть.
Сам машинный код гомоичен, так что да.
Данные или инструкции - это просто вопрос семантики (и, возможно, сегмента памяти, в котором они лежат).
В самой буквальной форме C гомоичен. Вы можете получить доступ к представлению функции, используя &functionName
и выполнить данные, используя somePtrCastToFnPtr(SomeArgs)
, Однако это на уровне машинного кода, и без какой-либо поддержки библиотеки вам будет очень трудно работать. Какой-то встраиваемый компилятор (кажется, я помню, что LLVM может сделать это) сделает его более практичным.
Лисп нормально скомпилирован. Были реализации с JIT-компиляторами вместо интерпретаторов.
Следовательно, нет необходимости иметь интерпретатор (в смысле "не компилятор") для языков кода-данных.
Компиляция - это просто оптимизированная интерпретация. Интерпретатор берет кусок данных, представляющих код, а затем "делает" этот код: значение кода превращается в пути выполнения и поток данных через кишки интерпретатора. Компилятор берет те же данные, переводит их в другую форму и затем передает их другому интерпретатору: одному, реализованному в кремнии (ЦП) или, возможно, поддельному (виртуальная машина).
Вот почему некоторые реализации Lisp могут не иметь интерпретаторов. Функция EVAL может скомпилировать код и затем перейти к нему. EVAL и COMPILE не обязательно должны иметь разные режимы работы. (Clozure, Corman Lisp, SBCL являются примерами Lisps "только для компилятора".)
Часть данных в начале является ключом к тому, что язык гомоичен, а не оптимизировано ли выполнение кода путем компиляции. "Код - это данные" означает "исходный код - это данные", а не "исполняемый код - это данные". (Конечно, исполняемый код - это данные, но под данными мы подразумеваем подавляющее большинство предпочтительного представления кода, которым мы хотим манипулировать.)
Да; вам просто нужно вставить копию компилятора в языковую среду выполнения. Chez Scheme - один из многих хороших компиляторов, которые делают именно это.
Проблема заключается в том, что многие процессоры разделяют области команд и данных и активно препятствуют программам изменять свой собственный код. Этот вид кода раньше назывался "вырожденным кодом" и считался очень плохой вещью.
У интерпретаторов (и виртуальных машин) такой проблемы нет, так как они могут обрабатывать всю программу как данные, а интерпретатором является только "код".
Языки, построенные на основе виртуальных машин (.net clr, jre ect), могут использовать передовые технологии, которые позволяют генерировать код на лету. Одним из них является ткачество IL. Хотя, это не так ясно, как eval из ECMAScript/Lisp/Scheme ect, но он может в некоторой степени подражать такому поведению.
Для примеров проверьте Castle DynamicProxy и для более интерактивного примера проверьте LinqPAD, F# Interactive, Scala Interactive.