Дело не в гомоиконности (2012)

Делоневгомоиконности2012

Я никогда толком не понимал, что должно означать «гомоиконичность». Люди часто говорят что-то вроде «синтаксис использует одну из базовых структур данных языка». Это ошибка категории: синтаксис – это не структура данных, это просто представление данных в виде текста. Или вы слышите « синтаксис языка такой же, как синтаксис его структур данных ». Но S-выражения не «принадлежат» Lisp; нет причин, по которым Perl, Haskell или JavaScript не могли иметь библиотеки S-выражений. И каждый синтаксический анализатор генерирует структуру данных, поэтому, если у вас есть синтаксический анализатор Python в Python , то является ли Python гомоиконным? JavaScript ?

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

Чего ждать?

Эти концепции трудно объяснить традиционной терминологией, потому что различие между чтением и синтаксическим анализом просто не существует для языков без макросов.

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

Почти во всех языках, не относящихся к Lispy, интерфейс каждого интерпретатора и компилятора выглядит примерно одинаково:

traditional parsing pipeline

Возьмите текст, пропустите его через синтаксический анализатор, и вы получите AST. Но это не так, когда у вас есть макросы. Вы просто не можете создать AST без предварительного расширения макросов. Таким образом, интерфейс языка Lispy обычно больше похож на:

macro pipeline

Что это промежуточное синтаксическое дерево? Это почти полностью поверхностное понимание вашей программы: оно в основном выполняет сопоставление парных символов для создания дерева, представляющего структуру вложенности поверхности текста. Это далеко не AST, но этого достаточно, чтобы система расширения макросов выполняла свою работу.

Разбор и чтение: вид расширителя макросов

Если вы видите этот оператор посередине программы на JavaScript:

 для  ( позволять ключ в  obj  )   { Распечатать  (ключ ;  }   

вы точно знаете, что это ForInStatement , как определено спецификация (я использую let потому что… ES6, вот почему). Если вы знаете грамматику JavaScript, вы знаете всю структуру оператора. Но в Scheme мы могли бы реализовать для как макрос. Когда расширитель макросов обнаруживает:

   (  для  ( ключ  obj  )   (Распечатать ключ ))   

он ничего не знает о содержимом выражения. Все, что он знает, это определение макроса для . Но это все, что ему нужно знать! Расширитель просто берет два поддерева, (клавиша obj) и (клавиша печати) и передает их в качестве аргументов для макроса .

Анализ и чтение: вид макроса

Вот простой для макрос, записанный в Ракетка :

   (  определение-синтаксис-правило   (для  (Икс  e1  )   e2  )   (для каждого  (  λ   (  Икс)   e2  )   e1  ))   

Этот макрос работает по сопоставлению с образцом : ожидается два поддеревья, первое из которых может быть разбитым на два узла идентификатора x и e1 , и он расширяется в для каждого выражение. Поэтому, когда модуль расширения вызывает макрос из приведенного выше примера, результат расширения будет следующим:

   ( для каждого  (  λ   (ключ )   (Распечатать ключ  ))   obj  )   

Сила скобок

Если вы когда-нибудь задумывались, почему чудаки Lisp так необъяснимо в круглых скобках, вот в чем суть. Скобки позволяют модулю расширения однозначно понять аргументы макроса, потому что всегда ясно, где аргументы начинаются и заканчиваются . Он знает это, и ему не нужно ничего понимать о том, что будет делать определение макроса. Представьте, что вы пытаетесь определить расширитель макросов для языка с синтаксисом, подобным JavaScript. Что делать расширителю, когда он видит:

 
  quux   (  бормотать  ,   flarg  )     {  foo  :   3  }   ворчание   /   вибл   / я  

Сколько аргументов делает quux брать? Аргумент в фигурных скобках - это оператор блока или литерал объекта? Является ли вещь в конце арифметическим выражением или литералом регулярного выражения? Это все вопросы, на которые нельзя ответить в JavaScript, не зная вашего контекста синтаксического анализа, а макросы скрывают контекст синтаксического анализа.

Ничего из этого не является сказать, что невозможно разработать систему макросов для языков с синтаксисом, отличным от Lispy. Я хочу сказать, что сила макросов Lisp (Scheme, Racket, Clojure, ...) проистекает не из того, что они каким-то образом связаны с центральной структурой данных языка, а, скорее, из-за способности расширителя разбивать вызов макроса на отдельные аргументы. а затем пусть макрос выполняет всю работу по синтаксическому анализу этих аргументов. Другими словами, гомоиконность не имеет значения, читать - это .

Leave a comment

Your email address will not be published. Required fields are marked *