Homoiconicity, как это работает? - PullRequest
       16

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

38 голосов
/ 19 февраля 2010

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

Ответы [ 6 ]

24 голосов
/ 19 февраля 2010

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


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


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

[...] Interactive LISP [...] иTRAC [...] оба "гомоичны" в том смысле, что их внутреннее и внешнее представления по сути одинаковы.

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

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

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

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

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

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

7 голосов
/ 19 февраля 2010

Когда я изучал Lisp, идея гомоконичности имела смысл, когда я узнал, что lisp «компилируется» в два этапа: чтение и компиляция, и код представлен с одинаковой структурой данных для обоих из них:

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

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

5 голосов
/ 20 февраля 2010

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

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

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

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

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

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

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

4 голосов
/ 07 марта 2010

Вот короткая программа для символического дифференцирования. Это пример того, как 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))))
2 голосов
/ 02 марта 2018

Как указывает Райнер Йосвиг, есть веские основания сомневаться в полезности идеи гомоичоничности и в том, действительно ли Лиспс гомоичоничен.

Исходное определение гомо-конитоцитных центров по подобию между внутренним и внешним представлениями языка . Каноническим примером является Лисп с его s-выражениями.

Есть (как минимум) две проблемы с этим определением и выбором примера.

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

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

Фактически, можно даже поставить дополнительные вопросы относительно концепции гомоичоничности: для хорошо инкапсулированной машины мы не можем наблюдать ее внутреннюю работу по определению; с этой точки зрения делать любое утверждение о внутреннем представлении машины бессмысленно. В более общем смысле исходное определение имеет проблему, заключающуюся в том, что идея о наличии единого внешнего и единого внутреннего представления программы не соответствует действительности. Фактически, существует целая цепочка представлений, включая электроны в мозгу программиста, фотоны, излучаемые экраном, текст программы, машинный код и электроны, движущиеся в ЦП.

Я написал об этом более подробно в статье под названием Не говорите "Homoiconic"

0 голосов
/ 19 февраля 2010

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

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

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

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

...