Homoiconicity, Как это работает?

Может кто-то предлагать статьи, которые объясняют понятие Homoiconicity, особенно с помощью Clojure. Почему случается так, что Clojure является гомографическим, но его твердое, чтобы сделать это на других языках, таких как Java?

38
задан Brian Carper 19 February 2010 в 23:42
поделиться

5 ответов

Прежде чем я перейду к некоторым вещам, на которые я хотел бы добавить еще один ответ, вот еще одно упоминание - часть, связанная с гомиконностью, довольно короткая, но Рич Хикки все объясняет! На Channel 9 есть это красивое видео , в котором Рич Хики и Брайан Бекман говорят о Clojure.Понятно, что основное внимание уделяется параллелизму, но гомоиконность получает свой собственный (короткий) момент экранного времени, в течение которого Рич красиво объясняет взаимодействие между и (функцией, которая преобразует конкретный синтаксис, записанный программистом во внутреннее представление, построенное из списков и т. д.) и eval . У него есть эта красивая диаграмма, показывающая, как eval никогда даже не знает, что код, который он оценивает, исходит из read , работающего с текстовым файлом ... Артур уже объяснил суть этого, но эй , все равно посмотрите, это очень хорошее видео!


Заявление об отказе от ответственности: я буду упоминать Java и Python в качестве примеров под следующей горизонтальной полосой. Я хочу прояснить, что это всего лишь грубый набросок того, почему, на мой взгляд, может быть сложно создать гомоиконную Java или Python с поддержкой макросов в стиле Lisp; Однако это всего лишь академическое упражнение, и я не хочу рассматривать вопрос о том, есть ли вообще какая-то причина попробовать. Кроме того, я не хочу подразумевать, что синтаксис языка с макросами стиля Lisp должен содержать явные разделители для древовидных структур ; Дилан (Лисп без паренов?), По-видимому, дает контрпример. Наконец, я использую выражение Макросы стиля Lisp , потому что я исследую только макросы стиля Lisp. В языке Forth, например, есть другое средство макросов, которое я действительно не понимаю, за исключением того, что я знаю его, чтобы включить ужасно круто выглядящий код. Очевидно, расширения синтаксиса могут быть реализованы разными способами. С этим в стороне ...


Я хотел бы обратиться ко второй части вашего вопроса - почему большинство языков программирования не считаются гомоиконными? В процессе мне придется затронуть семантику Лиспа, но поскольку Нильс уже предоставил ссылки на хорошие источники информации о самом термине «гомоиконный», а Артур описал цикл чтения -> макроса, раскрыть -> компиляция, в Clojure я буду опираться на это в дальнейшем.Для начала позвольте мне процитировать отрывок из Алана Кея (взят из статьи в Википедии, которая также ссылается на первоисточник):

[...] Интерактивный LISP [...] и TRAC [...] оба являются «гомоиконными» в том смысле, что их внутренние и внешние репрезентации по существу одинаковы.

(Эти [...] биты скрывают большой объем текста, но суть остается неизменной.)

Теперь давайте зададимся вопросом: что такое внутреннее представление Java в Java? . .. Ну, это даже не имеет смысла. Компилятор Java действительно имеет определенное внутреннее представление Java, а именно абстрактное синтаксическое дерево; чтобы построить «гомоиконную Java», нам нужно было бы сделать это представление AST объектом первого класса в Java и разработать синтаксис, который позволил бы нам напрямую писать AST. Это могло оказаться довольно сложно.

Python предоставляет пример негомоиконного языка, который интересен тем, что в настоящее время он поставляется с набором инструментов для обработки AST в виде модуля ast . В документации для этого модуля явно указано, что AST Python могут меняться между выпусками, что может или не может обескураживать; тем не менее, я полагаю, что трудолюбивый программист мог бы взять модуль ast , разработать синтаксис (возможно, основанный на S-выражении, возможно, основанный на XML) для непосредственного описания Python AST и построить синтаксический анализатор для этого синтаксиса в обычном Python, используя ast , тем самым сделав твердый первый шаг к созданию гомоиконного языка с семантикой Python.(Я полагаю, что некоторое время назад я наткнулся на диалект Lisp, компилируемый в байт-код Python ... Интересно, может ли он делать что-то подобное на каком-то уровне?)

Даже тогда остается проблема извлечения конкретных преимуществ из такого рода гомоиконности. Это рассматривается как полезное свойство членов семейства языков Lisp, поскольку позволяет нам писать программы, которые пишут дополнительные программы, среди которых макросы являются наиболее заметными. Теперь, , хотя макросы разрешены одним способом благодаря тому факту, что так легко манипулировать внутренним представлением кода Лиспа в Лиспе, они также включены не менее важным способом моделью выполнения Лиспа : Программа на Лиспе - это просто набор форм Лиспа; они обрабатываются лисповой функцией eval , которая отвечает за определение значений выражений и вызывает соответствующие побочные эффекты в нужное время; семантика Lisp в точности совпадает с семантикой eval . Вопрос о том, как все работает внутри, чтобы сохранить эту семантическую иллюзию при разумной скорости, является деталью реализации; Система на Лиспе обязана предоставить программисту функцию eval и действовать так, как если бы программы на Лиспе обрабатывались этой функцией.

В современных системах Lisp часть контракта eval заключается в том, что он выполняет дополнительную фазу предварительной обработки, во время которой макросы раскрываются перед оценкой кода (или компиляцией и запуском, в зависимости от обстоятельств). быть).Это конкретное средство не является необходимой частью системы Lisp, но его очень легко подключить к этой модели выполнения! Кроме того, мне интересно, не является ли это единственной моделью выполнения, которая делает макро-преобразования типа Lisp управляемыми, что означало бы, что любой язык, стремящийся включить макросы в стиле Lisp, должен был бы принять аналогичную модель выполнения. Моя интуиция подсказывает мне, что это действительно так.

Конечно, если язык записан в нотации, непосредственно параллельной его AST, и использует Lisp-подобную модель выполнения с функцией / объектом оценивающего средства, следует задаться вопросом, не является ли это случайно другим диалектом Lisp ... даже если его синтаксис распараллеливания AST основан на XML. дрожь

24
ответ дан 27 November 2019 в 03:52
поделиться

Когда я изучал Лисп, идея гомоиконичности обрела смысл, когда я узнал, что Лисп "компилируется" в две фазы, чтение и компиляция, и код представлен одной и той же структурой данных для обеих:

  • сначала вы думаете о s-выражении в своей голове
  • затем вы вводите s-выражение в виде символов в файл
  • затем читатель переводит символы в файле в s-выражения. Он не компилирует программу, а просто строит структуры данных из символов - это часть фазы чтения.
  • затем читатель просматривает каждое из выражений и решает, является ли оно макросом, и если да, то запускает макрос для создания другого s-выражения. таким образом, на данном этапе мы перешли от s-выражений к символам и s-выражениям, затем от s-выражений к другим s-выражениям.
  • Эти s-выражения затем компилируются в файлы .class, которые могут быть запущены jvm - это вторая фаза "компиляции".

Таким образом, весь путь от вашего мозга до файла .class проходит в виде s-выражений. вы даже пишете s-выражения, которые пишут s-выражения. поэтому вы можете сказать, что "код - это данные" или "код - это данные", потому что так звучит лучше.

7
ответ дан 27 November 2019 в 03:52
поделиться

Это кажется почти очевидным, но первые источники могут быть такими:

http://en.wikipedia.org/wiki/Homoiconicity

http://c2.com/cgi/wiki?DefinitionOfHomoiconic

Гомоиконичность объясняется в общих чертах, и вы также можете найти исходные источники. Поскольку это объясняется на примере Lisp, это не так далеко от Clojure.

0
ответ дан 27 November 2019 в 03:52
поделиться

AES сильно отличается, AES создает SPN, сеть подстановок. Он генерирует s-боксы (поля подстановки) на основе полиномиальных функций, сгенерированных во время шифрования. Он выполняет его через 10-14 раундов подстановки на уровне байтов и перестановки на уровне битов, битовая длина ключа определяет количество раундов и круглых ключей.

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

-121--1541464-

Перетасовка Фишера-Йейтса или как она также известна как перетасовка Кнута.

-121--2338698-

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

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

Очевидно, что код может быть представлен в виде последовательностями на текстовых языках программирования. Но в Лиспе источник может быть представлен в терминах примитивных структур данных Лиспа. Внешнее представление основано на выражениях s, которые являются простой моделью для представления иерархических данных в виде текста. Внутренняя модель представления основана на списках и т.д.

Вот что получает оценщик: внутренние представления. Не от 1 до 1 версий текстовых входных данных, но проанализированных.

Базовая модель:

  • READ переводит внешние s-выражения в данные
  • EVAL принимает формы Лиспа в виде данных Лиспа и оценивает их
  • PRINT переводит данные Лиспа во внешние s-выражения

Обратите внимание, что READ и PRINT работают для произвольных данных Лиспа, которые имеют печатное представление и считыватель, а не только для форм ЛиСпспа. Формы по определению являются допустимыми выражениями на языке программирования Lisp.

5
ответ дан 27 November 2019 в 03:52
поделиться

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

;; The simplest possible symbolic differentiator

;; Functions to create and unpack additions like (+ 1 2)
(defn make-add [ a b ] (list '+ a b))
(defn addition? [x] (and (=(count x) 3) (= (first x) '+)))
(defn add1   [x] (second x))
(defn add2   [x] (second (rest x)))

;; Similar for multiplications (* 1 2)
(defn make-mul [ a b ] (list '* a b))
(defn multiplication? [x] (and (=(count x) 3) (= (first x) '*)))
(defn mul1   [x] (second x))
(defn mul2   [x] (second (rest x)))

;; Differentiation. 
(defn deriv [exp var]
  (cond (number? exp) 0                                                              ;; d/dx c -> 0
        (symbol? exp) (if (= exp var) 1 0)                                           ;; d/dx x -> 1, d/dx y -> 0
        (addition? exp) (make-add (deriv (add1 exp) var) (deriv (add2 exp) var))     ;; d/dx a+b -> d/dx a + d/dx b
        (multiplication? exp) (make-add (make-mul (deriv (mul1 exp) var) (mul2 exp)) ;; d/dx a*b -> d/dx a * b + a * d/dx b
                                        (make-mul (mul1 exp) (deriv (mul2 exp) var)))
        :else :error))

;;an example of use: create the function x -> x^3 + 2x^2 + 1 and its derivative 
(def poly '(+ (+ (* x (* x x)) (* 2 (* x x))) 1))

(defn poly->fnform [poly] (list 'fn '[x] poly))

(def polyfn  (eval (poly->fnform poly)))
(def dpolyfn (eval (poly->fnform (deriv poly 'x))))
4
ответ дан 27 November 2019 в 03:52
поделиться