Действительно ли гомоконичность нужна для макросов? - PullRequest
8 голосов
/ 05 февраля 2012

2012-02-04 спонсируется словом "homoiconicity" http://en.wikipedia.org/wiki/Homoiconicity.

Справочная информация: я собираюсь выбрать, какую книгу о Clojure купить- либо «Clojure in Action» , либо (поступающий в конце апреля) «Программирование Clojure» (вы можете прочитать его через O'Reilly Rough Cuts , половина страницвиден).Что меня поразило, так это то, что в обеих книгах это свойство - гомойконичность - получило такое большое внимание.

Поскольку корни Clojure основаны на Лиспе, я сослался на «мертвую сексуальную» книгу это действительно так важно.Хорошо, я вижу макросы, объясненные подробно, но я не уловил упомянутый акцент.Теперь сравните с этим (цитата из «Clojure в действии»)

Эта гомойконичность также делает возможной макросистему Clojure.

Она выглядит почти без макросовбыло бы невозможноДаже утверждение Википедии (ссылка выше) более сбалансировано, но ни один из этих источников (*) не учитывает человеческий фактор в пользу английского синтаксиса.

Если я не ошибаюсь, макросинтаксис (подобный Lisp) мог бы быть возможенв C # (например), только с большим усилием команды C #.Но это стоимость команды дизайнеров, а не пользователей (?).Второе - привычки имеют значение.Если в реальной жизни вы думаете «a + b», а в компьютерном мире вы постоянно переводите его на «+ ab», производительность страдает (я могу убедиться в этом сам, когда перешел от функторов C ++ к лямбдам C #).

Эта похвала, что программист Clojure пишет программы почти напрямую как AST, пугает меня как чтение, что «благодаря написанию кода непосредственно в шестнадцатеричном коде вы не только изучаете шестнадцатеричную систему наизусть, но вы ближе к машине».

Подводя итог - я люблю метапрограммирование, я люблю макросы, похожие на Лисп (хотя я не Лиспер), но я вижу здесь две вещи - макросы и гомойконичность.Первый, без сомнения, великолепен, второй - не так много (как я понимаю), потому что он отвечает потребностям компьютера в компьютерах, и должен быть наоборот.

Вопрос

Является ли гомоконичность действительно полезной для людей (конечных пользователей языка) или она практически полезна для разработчиков языков?Примеры очень приветствуются!

Или, на всякий случай, я перефразирую - если предположить, что в данном языке есть макросы Lisp, «добавление» гомоиконичности улучшит производительность конечного пользователя?Expresiveness?Или совсем наоборот?

(*) Я не могу быть на 100% уверен, потому что я вижу только часть книг Clojure, и я не читаю их, просто оцениваю их для покупки.

Обновление

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

Ответы [ 5 ]

10 голосов
/ 05 февраля 2012

Нет, вам не обязательно нужна гомоконичность для создания мощной, похожей на Лисп, макросистемы. Гомоиконичность просто делает такую ​​систему намного проще в использовании; без него вам понадобится другой синтаксис для выражения AST, которые макросы генерируют при оценке. На гомойконическом языке макросистема выглядит естественной, поскольку программы и метапрограммы выглядят одинаково.

9 голосов
/ 05 февраля 2012

Гомоиконичность строго не требуется для макросов (пример: препроцессор C / C ++ реализует макросистему времени компиляции).

Однако, гомоционичность делает макросы намного более эффективными и простыми в использовании:

  • Вы избегаете необходимости разбора синтаксиса: в гомоиконическом языке, таком как Clojure, исходный код можно напрямую использовать в качестве абстрактного синтаксического дерева компилятора.Меньше накладных расходов на компилятор, меньше концептуальных накладных расходов на пользователя.
  • Генерация кода проще - вам просто нужно собрать правильную структуру данных, а не тщательно генерировать текстовый исходный код в форме, которая может быть успешно скомпилирована.В общем, генерация текстового кода может быть довольно сложной задачей для языков, которые не были разработаны для этого (необходимо учитывать множество вещей, таких как генерация имен символов, импорт библиотек, правила цитирования, нужен ли компилятору исходный файл для записи вдиск и т. д.)
  • Ваш язык макросов такой же, как ваш исходный язык.Нет необходимости изучать отдельный язык или набор конструкций для макросов.
  • ИМХО гомогенность делает макросы намного проще, элегантнее и удобочитаемее.Во многих случаях ваш макрос в конечном итоге выглядит как шаблон для правильного кода.

В качестве небольшого примера приведем макрос, который добавляет в Clojure цикл «for» в стиле Java / C #.

(defmacro for-loop [[sym init check change :as params] & steps]
 `(loop [~sym ~init value# nil]
    (if ~check
      (let [new-value# (do ~@steps)]
        (recur ~change new-value#))
      value#)))

;; Usage:
(for-loop [i 0 (< i 10) (inc i)] 
  (println i))

И это все - новая языковая конструкция, добавленная в шесть строк кода.Вы можете ясно видеть гомоционичность в действии - вся форма "петли" - это просто цитируемая структура данных Clojure.Параметры init, check и change также представляют собой гомо-логические структуры данных clojure, передаваемые в макрос, которые представляют выражения управления циклом.Не было необходимости выполнять какой-либо синтаксический анализ для поддержки моего нового синтаксиса.

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

5 голосов
/ 05 февраля 2012

Homoiconicity - плохо определенное понятие.

Что отличает Lisp, так это то, что он имеет представление данных исходного кода, и оценщик принимает его в качестве входных данных. Обрабатывать исходный код как данные легко, поскольку применяются все обычные функции Lisp.

Что это обеспечивает:

  • чтение текстового источника и создание его структур данных
  • обработка источника как данных
  • печать «источника как данных» в виде текста, включая такие вещи, как красивая печать кода

Макросы - это функции, которые преобразуют исходный код. Таким образом, все, что вам нужно, - это некоторое представление исходного кода и этап выполнения / компиляции / интерпретации языка программирования, на котором происходит этот этап преобразования.

То, как работает Lisp, делает его удобным, поскольку исходный код представляет собой структуру данных, которой можно манипулировать с помощью множества встроенных функций и средств. Обратите внимание, что более сложные преобразования, когда нужно понимать синтаксис языка Lisp, также нелегко сделать в Lisp - хотя инструменты были реализованы (например, так называемые обходчики кода ).

Есть также Лиспы, которые имеют синтаксис, который не основан на s-выражениях. Примером является RLISP, язык реализации Системы компьютерной алгебры REDUCE . RLISP также поддерживает макросы.

Можно определить аналогичную схему, основанную на строках и манипулировании строками. Вы также можете определить систему макросов на основе некоторого типа AST.

Пример языков с такими макросами:

  1. Дилан. Смотри: http://opendylan.org/books/dpg/macros.html
  2. Julia. Смотри: https://en.wikipedia.org/wiki/Julia_(programming_language)
  3. Nemere. Смотри: https://github.com/rsdn/nemerle/wiki/Macros-tutorial
3 голосов
/ 05 февраля 2012

Представьте, что вы используете устаревшую методологию языка веб-шаблонов, используя <% code %> для использования встроенного Java в Python.

Тогда языком макросов будет Java, а целевым языком будет Python. Вызов макроса будет означать выход в режим <% code %>. Вы могли бы даже спроектировать такую ​​систему для поддержки параметров, eval строк и тому подобное. Это было бы действительно ужасно (вроде как старый JSP или PHP-код).

Страшная мысль: могут ли некоторые пользователи jsp найти такую ​​систему более удобной, чем система lisp? Будет ли этот монстр улучшен, если бы макро / целевой язык были одинаковыми?

Так что, конечно, это можно сделать. Люди сделали несколько удивительных вещей с помощью шаблонов C ++. Джей Фриман (Jay Freeman) написал программу на C ++ со временем компиляции Аккермана и постоянным временем выполнения, чтобы продемонстрировать, что шаблоны C ++ могут делать такие вещи (предположительно потому, что примеры Фибоначчи не взрывают достаточно компиляторов?).

Что касается гомойконичности: она не решает все. Даже при этом часто возникает некоторая неловкость: кавычки, квази-кавычки и т. Д. Вам нужно каким-то образом переключаться между «это код макроса» и «это код целевого языка». Это неотъемлемая проблема, которую должны решить разработчики языка. Вместо того, чтобы писать язык в виде структур данных или программного кода, вы можете думать только о неудобствах, связанных с использованием строк с помощью метода eval. Исходный код программы похож на строку. Строка - это действительно ужасная структура данных. И струны внутри струн еще хуже. Посмотрите любой код Quine (возможно, не лиспа). О них можно думать как о гомойконичности, сделанной ужасно неправильно.

2 голосов
/ 05 февраля 2012

Хотя вам нужен язык для макросов, он не обязательно должен быть тем же языком, на котором написан остальной код. все, что вам нужно, это "iconicity" *, часть "homo" является необязательной.

В Ocaml макросы написаны на другом языке, который называется camlp4

выглядит примерно так :

#load "pa_extend.cmo";

value gram = Grammar.gcreate (Plexer.gmake ());
value exp = Grammar.Entry.create gram "expression";

EXTEND
  exp:
  [ "SUM"
    [ x = exp; "+"; y = exp -> Op "+" x y
     | x = exp; "-"; y = exp -> Op "-" x y]
  | "PROD"
    [ x = exp; "*"; y = exp -> Op "*" x y
    | x = exp; "/"; y = exp -> Op "/" x y]
  | "ATOM"
    [ x = INT -> Int (int_of_string x)
    | x = LIDENT -> Var x
    | "("; x = exp; ")" -> x ] ]
  ;
END;

, где язык, который он трансформирует, выглядит следующим образом:

let rec search ?(x=0) ?(y=0) f accu = match x, y with
    9, y -> search ~x:0 ~y:(y+1) f accu (* Next row *)
  | 0, 9 -> f accu                      (* Found a solution *)
  | x, y ->
      if m.(y).[x] <> '0' then search ~x:(x+1) ~y f accu else
        fold (fun accu n ->
                let n = Char.chr (n + 48) in
                if invalid x y n then accu else
                  (m.(y).[x] <- n;
                   let accu = search ~x:(x+1) ~y f accu in
                   m.(y).[x] <- '0';
                   accu)) accu 1 10

, как вы можете видеть, в Ocaml нет гомиоконичности, и он имеет высоко развитую макро-подобную систему.

  • это не настоящий термин, я придумал его в надежде, что кто-то улыбнется :) 1020
...