Я буду уверен, что мои знания о Lisp чрезвычайно минимальны. Однако я очень заинтересован в языке и планирую начать серьезно изучать его в ближайшем будущем. Мое понимание этих проблем, несомненно, ошибочно, поэтому, если я говорю что-либо, что явно не правильно, прокомментируйте и исправьте меня, а не downvoting.
Поистине гомоиконические и самомодифицируемые языки
Я ищу примеры языков программирования, которые поддерживают как Homoiconicity (код имеет то же представление, что и данные), и неограниченная самостоятельная модификация (Неограниченное значение, которое вы можете изменить каждый аспект вашего текущего кода, не просто испускать новый код или менять указатели на функции/делегаты.)
Есть только три примера, которые я нашел до сих пор, которые соответствуют этим критериям:
- Машинный код. Homoiconic в том, что все - это число. Неограниченно модифицируемый, поскольку он включает указатели, которые могут использоваться для управления любым адресом памяти независимо от того, хранит ли этот адрес код или данные.
- Malbolge. Те же рассуждения, что и машинный код. Каждая инструкция изменяет себя после выполнения
- ДНК. Не язык программирования, но все же интересный. Это не самомодификация в том же смысле, что и машинный код; Если фактические инструкции + данные изменены на месте. Однако он сам реплицируется и может мутировать/развиваться в соответствии с ним в предыдущем состоянии (с побочными эффектами, такими как излучение, время от времени зависящее от него). В любом случае это косвенный способ самостоятельной модификации. Короче говоря, ДНК может самостоятельно модифицироваться, но она делает это, воспроизводя себя в ней entirity вместе с соответствующими мутациями. Физическая последовательность ДНК является "неизменной".
Почему Lisp отсутствует в этом списке
Lisp не входит в этот список, потому что мне кажется, что Lisp только почти гомоиконический и поддерживает только ограниченную модификацию. Вы можете сделать что-то вроде
(+ 1 2 3)
который будет делать то же самое, что
(eval '(+ 1 2 3))
В первой версии (+ 1 2 3)
находится необработанный код, тогда как во второй версии это данные. Предполагая правду этого утверждения, можно утверждать, что Lisp даже не гомиконов. Код имеет то же представление, что и данные в том смысле, что они оба являются списками/деревьями/S-выражениями. Но тот факт, что вы должны явно указать, какой из этих списков/деревьев/S-выражений является кодом и которые являются данными для меня, кажется, говорит, что Lisp в конце концов не является homiconic. Представления очень похожи, но они отличаются крошечными деталями, которые вы должны фактически сказать, имеете ли вы дело с кодом или данными. Это ни в коем случае не плохо (на самом деле все остальное было бы безумием), но в нем подчеркивается разница между Lisp и машинным кодом. В машинный код вам не нужно явно указывать, какие числа являются инструкциями, которые являются указателями, а какие - данными. Все просто число, пока на самом деле не требуется интерпретация, и в какой момент это может быть любая из этих вещей.
Это еще более сильный случай против неограниченной самомодификации. Конечно, вы можете взять список, который представляет собой некоторый код и манипулировать им. Например, изменение
'(+ 1 2 3)
к
'(+ 1 4 3)
И затем вы запускаете его через eval
. Но когда вы это делаете, вы просто компилируете код и запускаете его. Вы не изменяете существующий код, вы просто испускаете и запускаете новый код. С# может делать то же самое с деревьями выражений, даже если он менее удобен (что возникает из-за того, что код С# имеет другое представление в его AST, а не Lisp, который является его собственным AST). Можете ли вы на самом деле взять весь исходный файл и начать изменять весь исходный файл по мере его запуска, с изменениями, внесенными в исходный файл с эффектами реального времени на поведение программы?
Если есть какой-то способ сделать это, Lisp не является ни homiconic, ни self-модификацией. (Чтобы отложить аргумент по определениям, Lisp не является гомоиконическим или самомодифицирующимся в той же степени, что и машинный код.)
Способы сделать Lisp Homoiconic/неограниченно самомодифицируемым
Я вижу 3 возможных способа сделать Lisp гомоциконным/самомодифицируемым как машинный код.
- Архитектура Non-Von-Neumann.Если кто-то может изобрести какую-то удивительную гипотетическую машину, где представление программ самого низкого уровня - это АСТ, который может быть выполнен непосредственно (дальнейшая компиляция не нужна). На такой машине AST будет представлять как исполняемые инструкции, так и данные. К сожалению, проблема не будет решена, потому что AST по-прежнему должен быть либо кодом, либо данными. Присутствие функции eval не меняет этого. В машинный код вы можете переключаться между кодом и данными столько, сколько хотите. Принимая во внимание, что с eval и Lisp после того, как вы "обнулили" некоторый список из данных в код и выполнили его, нет способа вернуть этот список обратно. Фактически, этот список ушел навсегда и был заменен его значением. Нам не хватало бы чего-то важного, что обычно бывает указателем.
- Список ярлыков. Если требовалось, чтобы в каждом списке также была уникальная метка, можно было бы сделать косвенную самостоятельную модификацию, запустив функции против списка с указанной меткой. В сочетании с продолжениями это, наконец, позволит самомодифицировать код в том же смысле, что и машинный код. Этикетки эквивалентны адресам памяти машинного кода. В качестве примера рассмотрим программу Lisp, где верхний node AST имеет метку "main". Внутри main вы могли бы затем выполнить функцию, которая берет метку, Integer, Atom и копирует атом в список с меткой, которая соответствует той, которая указана в функции, в индексе, указанном Integer. Затем просто позвоните с текущим продолжением на главном. Там вы идете, самостоятельно изменяя код.
- Lisp Макросы. Я не нашел времени, чтобы понять макросы Lisp, и они могут действительно делать именно то, о чем я думаю.
Точка 1. в сочетании с 2. создаст полностью самомодифицирующийся Lisp. При условии, что описанная магическая машина Lisp может быть произведена. 2. сам по себе может создать самомодифицирующийся Lisp, однако реализация на архитектуре фон Неймана может быть чрезвычайно неэффективной.
Вопросы
- Существуют ли какие-либо языки, кроме машинного кода, dna и malbolge, которые могут делать полную модификацию самостоятельно и являются гомомиковидными?
- (не беспокойтесь, если вы указали tl; dr в приведенном выше тексте). Действительно ли Lisp homoiconic + самомодификация? Если вы так говорите, можете ли вы точно указать, где в моем аргументе я сбился с пути?
Приложение
Языки с неограниченной самостоятельной модификацией, но без гомиконичности
- Ассамблея. Код использует слова в отличие от чисел, поэтому теряет гомиконус, но у него все еще есть указатели, которые сохраняют полный контроль над памятью и допускают неограниченную само модификацию.
- Любой язык, на котором используются исходные указатели. Например, C/С++/Objective C. Тот же аргумент, что и Assembly,
- Языки JIT, содержащие виртуальные указатели. Например, С#/.NET работает в небезопасном контексте. Тот же аргумент, что и Assembly.
Другие понятия и языки, которые могут быть как-то релевантными/интересными: Lisp, Ruby, Snobol, Forth, и он компилирует метапрограммирование времени, Smalltalk и его отражение, нетипизированное лямбда-исчисление с его собственностью, что все является функцией (из чего следует, что если предположить, что мы могли бы изобрести машину, которая непосредственно выполняет лямбда-исчисление, лямбда-исчисление будет гомоиконическим, а машинный код фон Неймана не будет работать на указанной машине. [И теорема Гёдельса была бы исполняемой. Ха-ха, страшная мысль: P])