Let Over Lambda - 50 Years of Lisp / Let Over Lambda - 50 лет Lisp-у: Глава 8 - Лисп, переходящий в Форт, переходящий в Лисп
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
8.1 Причудливая Архитектура Charlz_Klug 11.03.15 в 13:46 |
|
|
Эта глава - кульминация многих, увиденных ранее в этой книге макро техник. Используя абстракции из написанных нами макросов мы создадим реализацию одного из моих любимых языков программирования: forth. И хотя эта реализация воплощает многие идеи форта, она очень отличается от форта и является лисповой. Несмотря на то, что код приведённый в этой главе можно использовать в других интересных целях, его главным назначением является обучение основам и концепциям метапрограммирования форта для лисп аудитории и использование в роли платформы для дискуссий по центральной теме этой книги - создание и использование дуализма синтаксиса в макросах. Charlz_Klug 11.03.15 в 14:04 Эта глава - кульминация многих, увиденных ранее в этой книге макро техник. Используя абстракции из написанных нами макросов мы создадим реализацию одного из моих самых любимых языков программирования: forth. И хотя эта реализация воплощает многие идеи форта, она очень отличается от форта и является лисповой. Несмотря на то, что код приведённый в этой главе можно использовать в других интересных целях, его главным назначением является обучение основам и концепциям метапрограммирования форта для лисп аудитории и использование в роли платформы для дискуссий по центральной теме этой книги - создание и использование дуализма синтаксиса в макросах. Charlz_Klug 21.10.15 в 10:46 |
||
|
Форт обладает богатой, захватывающей историей развития, в отличие от многих других языков, за исключением лиспа и я рад, что открыл форт для себя. По этой и другим причинам эта глава посвящена с любовью моему отцу Брайану Хойту, познакомившему меня с фортом и с программированием на компьютере. Эта глава частично вдохновлена [THREADING-LISP] и исследованием Генри Бейкера [LINEAR-LISP] [LINEAR-LISP-AND-FORTH]. Charlz_Klug 11.03.15 в 14:34 |
||
|
Форт был первым языком программирования, который был создан и развивался без сильной правительственной, академической или корпоративной помощи - или по-крайней мере первый развившийся подобным образом язык. Вместо того, чтобы появиться в результате нужд большой организации, форт был создан независимо от всего Чаком Муром около 1968 года для решения его собственных вычислительных задач в астрономии, проектировании устройств и многого другого. После чего форт был распространён, реализован и улучшен силами сообщества энтузиастов [EVOLUTION-FORTH-HOPL2]. В отличие от патронажа MIT (и позже DARPA) над ранними лиспами и COMMON LISP, IBM-овского FORTRAN-а и AT&T-шного юникс языка C. Charlz_Klug 11.03.15 в 14:45 Форт был первым языком программирования, который был создан и развивался без сильной правительственной, академической или корпоративной помощи - или по-крайней мере первый развившийся подобным образом язык. Вместо того, чтобы появиться в результате нужд большой организации, форт был создан независимо от всего Чаком Муром около 1968 года для решения его собственных вычислительных задач в астрономии, проектировании устройств и многого другого. После чего форт был распространён, реализован и улучшен силами многочисленного сообщества энтузиастов [EVOLUTION-FORTH-HOPL2]. В отличие от патронажа MIT (и позже DARPA) над ранними лиспами и COMMON LISP, IBM-овского FORTRAN-а и AT&T-шного юникс языка C. Charlz_Klug 21.10.15 в 10:51 |
||
|
В следствии происхождения и отличающейся философии роли компьютерного программного обеспечения и компьютерного материального обеспечения, форт стал иным. Даже больше чем лисп форт выглядит странным. Но как и в лиспе, странность форта обладает своей причиной: он был спроектирован больше для ума, чем для стиля. Форт странен своей архитектурой, и эта архитектура имеет отношение к макросам. Charlz_Klug 11.03.15 в 15:01 В следствии происхождения и, в целом, другой философии роли компьютерного программного обеспечения и аппаратного обеспечения, форт стал иным. Даже больше чем лисп форт выглядит странным. Но как и в лиспе, странность форта обладает своей причиной: он был спроектирован больше для ума, чем для стиля. Форт странен своей архитектурой, и эта архитектура имеет отношение к макросам. Charlz_Klug 21.10.15 в 10:54 |
||
|
Сегодня форт чаще всего встречается в так называемых встраиваемых платформах - компьютерах с сильно ограниченными ресурсами. Это свидетельствует об архитектуре форта, как языке, который можно полностью реализовать на почти всех, когда либо созданных программируемых компьютерных системах. Форт спроектирован так, чтобы быть как можно более простым для реализации и экспериментов с ним. На самом деле создание клона форта настолько тривиально, что реализация форто-подобного стекового языка или нескольких языков является обязательным обрядом посвящения для программистов интересующихся архитектурой языков программирования. Некоторые стековые языки программирования восходят к форту, одними из интересных языков являются PostScript и Joy. Charlz_Klug 12.03.15 в 09:55 |
||
|
Часто важные решения при реализации форта основываются на непосредственных ресурсах компьютера на котором будет реализовываться форт. Форт программисты разработали набор абстрактных регистров, которые должны отображаться либо в настоящие регистры, либо располагаться в памяти или, возможно, реализуются какими-то другими способами. Но что если мы реализуем форт на лиспе, среде с неограниченным потенциалом и малыми ограничениями? Прежде чем начать отображать абстрактные регистры форта в лисп код, мы сделаем отступление. Как бы выглядел форт если бы у Чака была лисп машина? Charlz_Klug 12.03.15 в 10:03 Часто важные решения при реализации форта основываются на непосредственных ресурсах компьютера на котором будет реализовываться форт. Форт программисты разработали набор абстрактных регистров, которые должны отображаться либо в настоящие регистры, либо располагаться в памяти или, возможно, реализуются какими-то другими способами. Но что если мы реализуем форт на лиспе, среде с неограниченным потенциалом и малыми ограничениями? Прежде чем начать отображать абстрактные регистры форта в лисп код, мы сделаем отступление. Как бы выглядел форт если бы у Чака была лисп машина? Чем подгонять форт к особенностям отдельной машины, настоящей или виртуальной, мы рассмотрим минимальный набор абстрактных регистров форта, оптимизированных для упрощения реализации при реализации на лиспе. Charlz_Klug 12.03.15 в 11:25 |
||
|
Листинг 8.1: FORTH-REGISTERS Charlz_Klug 12.03.15 в 11:38 |
||
|
Вместо того, чтобы искать оптимальный набор абстрактных концепций, воспользуемся опытом Чака, создавшего десятки различных реализаций форта на многих архитектурах. Это то, что делает форт настолько замечательным. Как и лисп, форт предоставляет огромный простор в архитектуре языка и также, как и лисп, форт не столько язык программирования или набор стандартов, сколько строительный материал и набор знаний о том, что работает или не работает. Charlz_Klug 12.03.15 в 11:56 |
||
|
Переменная forth-registers - это список символов, представляющих абстрактные регистры нашей форт машины. Конечно, лисп не думает терминами регистров и целыми числами, а думает переменными и символами. Может показаться странным начинать нашу разработку форт среды всего лишь списком имён переменных, но этот факт всегда является первым шагом в реализации форт системы. Создание форта - это гениальный процесс самозагрузки красивей и продуманней которого может быть только лисп. Небольшое описание этого процесса последует позже. Charlz_Klug 12.03.15 в 12:11 Переменная forth-registers - это список символов, представляющих абстрактные регистры нашей форт машины. Конечно, лисп не думает терминами регистров и целыми числами, а думает переменными и символами. Может показаться странным начинать нашу разработку форт среды всего лишь списком имён переменных, но этот факт всегда является первым шагом в реализации форт системы. Создание форта - это гениальный процесс самопостроения красивей и продуманней которого может быть только лисп. Небольшое описание этого процесса последует позже. Charlz_Klug 21.10.15 в 11:02 |
||
|
Одной из характерных особенностей форта является его прямой доступ к структуре стековых данных, используемый вашей программой и для передачи параметров подпрограммам и для отслеживания процесса выполнения программы через эти подпрограммы. Особенно интересен форт ещё и тем, что, в отличие от большинства языков программирования, разделяет эти два применения структуры стековых данных на два стека, которыми вы можете управлять непосредственно сами 1. В типичной C реализации параметры вызова функции и, так называемого адреса возврата, сохраняются в единственном кадре стека размером в переменную для каждого вызова функции. В форте они представляют из себя два различных стека под названием стек параметра и стек возврата, которые представлены нами как наши абстрактные регистры pstack и rstack. Мы используем макросы COMMON LISP push и pop для обозначения этих стеков, реализованных с помощью связанного списка cons ячеек взамен структур из массива данных, используемых в большинстве фортов. Charlz_Klug 12.03.15 в 13:24 |
||
|
1 Большинство языков вообще не позволяют управлять стеком. Charlz_Klug 12.03.15 в 13:25 |
||
|
Listing 8.2: FORTH-WORD Charlz_Klug 12.03.15 в 13:25 Листинг 8.2: FORTH-WORD Charlz_Klug 16.03.15 в 06:38 |
+2 | |
|
Абстрактный регистр pc - это сокращение от program counter (счётчик программы), указатель на исполняемый в данный момент код. Что такое форт код, как мы можем ссылаться на него и для чего служат абстрактные регистры compiling и dtable будет описано позже. Charlz_Klug 13.03.15 в 09:53 |
||
|
Другим строительным блоком форта является концепция словаря. Словарь форта - это единый связанный список форт слов, аналогичных лисп функциям 2. Слова представлены лисп структурой. Структуры - это эффективные слот-ориентированные структуры данных, обычно реализуемые как векторы. Слот name предназначен для символа, используемого для поиска слова в словаре. Имейте в виду, что словарь форта составлен не в алфавитном, а в хронологическом порядке. Когда мы добавляем новые слова, то мы добавляем их в конец словаря, таким образом при проходе по словарю последние определённые слова будут рассматриваться первыми. Последний элемент нашего словаря всегда хранится в абстрактном регистре dict. Для прохода по словарю мы начинаем с dict и следуем по указателю prev в структуре слова, которое указывает либо на предыдущее определённое слово, либо на nil если текущее слово было последним словом 3. Charlz_Klug 13.03.15 в 10:05 |
||
|
2 Иначе говоря, они вовсе не функции, но скорее процедуры. Charlz_Klug 13.03.15 в 11:45 |
||
|
3 Да, иногда лисп программисты используют альтернативы cons ячейкам для связанных списков. Charlz_Klug 13.03.15 в 11:46 |
||
|
Дано w, искомое слово, и last, словарь, по-которому выполняется поиск, forth-lookup будет возвращать структуру форт слова или nil в зависимости от того, было ли найдено слово w в словаре или нет. Вместо eq было использовано слово eql, в отличие от лиспа, форт позволяет в качестве названий слова использовать цифры и другие не-символьные знаки. Charlz_Klug 13.03.15 в 12:05 |
||
|
Листинг 8.3:FORTH-LOOKUP Charlz_Klug 13.03.15 в 12:06 |
||
|
Слот immediate нашего форт слова - это флаг, обозначающий признак безотлагательности слова. Безотлагательность - это концепция метапрограммирования форта, которую мы скоро будем серьёзно изучать. Ну а пока мы сделаем грубую аналогию с лиспом: безотлагательные слова похожи на лисп макросы тем, что это форт функции, выполняемые во время компиляции, а не во время выполнения. Что? Предполагалось что только в лиспе есть макросы. Правда в том, что макросистема COMMON LISP мощнее всех других макросистем - включая лучшие форт реализации - но форт обладает способностью расширения, превосходящей почти все остальные языки. Как и в лиспе, эта способность является результатом философии архитектуры: если это хорошо для автора реализации языка, то это достаточно хорошо и для разработчика приложений на этом языке. Как и лисп, форт не признаёт понятия примитива. Вместо этого предоставляется набор мета-примитивов, которые можно комбинировать для построения того языка, который хотите видеть вы, программист. Как и в лиспе, но не в Блаб языках, расширение языка новыми способами через применение макросов не только возможно, но и поощряется. Как и лисп, форт создан не из соображений стиля, но из соображений мощи. Charlz_Klug 13.03.15 в 13:02 Слот immediate нашего форт слова - это флаг, обозначающий признак незамедлительности слова. Незамедлительность - это концепция метапрограммирования форта, которую мы скоро будем серьёзно изучать. Ну а пока мы сделаем грубую аналогию с лиспом: незамедлительные слова похожи на лисп макросы тем, что это форт функции, выполняемые во время компиляции, а не во время выполнения. Что? Предполагалось что только в лиспе есть макросы. Правда в том, что макросистема COMMON LISP мощнее всех других макросистем - включая лучшие форт реализации - но форт обладает способностью расширения, превосходящей почти все остальные языки. Как и в лиспе, эта способность является результатом философии архитектуры: если это хорошо для автора реализации языка, то это достаточно хорошо и для разработчика приложений на этом языке. Как и лисп, форт не признаёт понятия примитива. Вместо этого предоставляется набор мета-примитивов, которые можно комбинировать для построения того языка, который хотите видеть вы, программист. Как и в лиспе, но не в Блаб языках, расширение языка новыми способами через применение макросов не только возможно, но и поощряется. Как и лисп, форт создан не из соображений стиля, но из соображений мощи. alex_fff 30.07.15 в 20:51 |
||
|
8.2 Код, Работающий Через Cons Charlz_Klug 13.03.15 в 13:10 8.2 Cons Треды Кода Charlz_Klug 13.03.15 в 13:17 8.2 Cons Шитый Код alex_fff 30.07.15 в 20:51 |
||
|
В предыдущем разделе мы сфокусировались на абстрактных регистрах. Эти регистры находятся в центре фокуса, благодаря им философия форта настолько фундаментальна, но на самом деле эти регистры всего лишь компоненты более важной концепции: абстрактная машина. Charlz_Klug 13.03.15 в 13:16 В предыдущем разделе мы сфокусировались на абстрактных регистрах. Эти регистры находятся в центре фокуса, благодаря им философия форта настолько фундаментальна, но на самом деле эти регистры всего лишь компоненты более важной концепции: абстрактная машина. Возможно наиболее отличительная черта различных форт систем - это их реализации треда кода. То, что в форте обозначается под тредом кода - очень отличается от традиционного понимания процессов в разделённой памяти с вытесняющей многозадачностью 4. Форт треды не имеют ничего общего с параллелизмом. Форт треды - это каркас для компиляции кода и метапрограммирования. Charlz_Klug 13.03.15 в 13:26 В предыдущем разделе мы сфокусировались на абстрактных регистрах. Эти регистры находятся в центре фокуса, благодаря им философия форта настолько фундаментальна, но на самом деле эти регистры всего лишь компоненты более важной концепции: абстрактная машина. Возможно наиболее отличительная черта различных форт систем - это их реализации шитого кода. То, что в форте обозначается под шитым кодом - очень отличается от традиционного понимания нитей процессов в разделённой памяти с вытесняющей многозадачностью 4. Форт нити не имеют ничего общего с параллелизмом. Форт нити - это основа для разговора о компиляции кода и метапрограммировании. alex_fff 30.07.15 в 20:56 |
||
|
4 Эта книга не рекомендует использовать эти типы тредов из за соображений безопасности и надёжности. Charlz_Klug 13.03.15 в 13:28 4 Эта книга не рекомендует использовать эти типы нитей из за соображений безопасности и надёжности. alex_fff 30.07.15 в 20:59 |
||
|
И если лисп даёт доступ к дереву структуры данных 5 символов из которых собрана в памяти и скомпилирована ваша программа, то в форте нет символьных манипуляций. Вместо этого форт даёт доступ к процессу сборка - запуск кода в памяти. Для посторонних наиболее примечательными особенностями форта являются стеки и постфиксная нотация, но, на самом деле изюминкой форта являются треды. Форт также завязан на стеки, как и лисп на списки. Они просто созданы для того, чтобы быть наиболее удобными структурами данных для решения задач мета-программирования - то, для чего предназначены форт и лисп. Charlz_Klug 16.03.15 в 09:03 И если лисп даёт доступ к дереву структуры данных 5 символов из которых собрана в памяти и скомпилирована ваша программа, то в форте нет символьных манипуляций. Вместо этого форт даёт доступ к процессу сборка - тред - код в памяти. Для посторонних наиболее примечательными особенностями форта являются стеки и постфиксная нотация, но, на самом деле изюминкой форта являются треды. Форт также завязан на стеки, как и лисп на списки. Они просто созданы для того, чтобы быть наиболее удобными структурами данных для решения задач мета-программирования - то, для чего предназначены форт и лисп. Charlz_Klug 16.03.15 в 09:09 И если лисп даёт доступ к дереву структуры данных 5 символов из которых собрана в памяти и скомпилирована ваша программа, то в форте нет символьных манипуляций. Вместо этого форт даёт доступ к процессу сборки - сшивания - кода в памяти. Для посторонних наиболее примечательными особенностями форта являются стеки и постфиксная нотация, но, на самом деле изюминкой форта являются нити. Форт также завязан на стеки, как и лисп на списки. Они просто созданы для того, чтобы быть наиболее удобными структурами данных для решения задач мета-программирования - то, для чего предназначены форт и лисп. alex_fff 30.07.15 в 21:04 |
||
|
5 На самом деле - направленный ацикличный граф. Charlz_Klug 16.03.15 в 09:03 5 На самом деле - ориентированный ацикличный граф. alex_fff 30.07.15 в 21:01 |
||
|
Классический стиль использования тредов известен как код с косвенным применением тредов, но более современные разновидности форта реализованы с кодом Charlz_Klug 16.03.15 в 15:04 Классический стиль использования тредов известен как код с косвенным применением тредов, но более современные разновидности форта реализованы для кода с непосредственным использованием тредов. Разница заключается в уровне косвенности. Низкоуровневая эффективность косвенности зависит от процессора и мы не будем углубляться в детали. По форт тредам есть немало хороших руководств [STARTING-FORTH] [MOVING-FORTH]. В памяти компьютера эти оба стиля тредов выглядят как смежные ячейки, в которых целочисленные машинные слова представляют указатели. Внутренний интерпретатор - маленький, компактный машинный код - обычно предназначается для процессора, выполняющего свою важную работу: следовать по указателям этих форт тредов, с последующей интерпретацией их значений. По-умолчанию, при встрече ячейки используется такое поведение: поместить текущий счётчик расположения программы в стек возврата, после чего изменить указатель счётчика программы на то, что указывает содержимое ячейки. Когда внутренний интерпретатор достигнет конца треда, он извлечёт значение из стека возврата и продолжит исполнение с прежнего места - там где оно остановилось. Charlz_Klug 16.03.15 в 21:10 Классический стиль сшивания известен как косвенное сшивание, но более современные разновидности форта реализованы с прямым сшиванием. Разница заключается в уровне косвенности. Низкоуровневая эффективность реализации косвенности зависит от процессора, и мы не будем углубляться в детали. По форт сшиваниям есть немало хороших руководств [STARTING-FORTH] [MOVING-FORTH]. В памяти компьютера эти оба стиля сшивания реализованы как смежные ячейки, в которых целочисленные машинные слова представляют указатели. Внутренний интерпретатор - маленький, компактный машинный код - обычно предназначается для процессора, выполняющего свою важную работу: следовать по указателям этих форт нитей, с последующей интерпретацией их значений. По-умолчанию, при встрече ячейки используется такое поведение: поместить текущий указатель расположения программы в стек возврата, после чего изменить указатель расположения программы на то, что указывает содержимое ячейки. Когда внутренний интерпретатор достигнет конца нити, он извлечёт значение из стека возврата и продолжит исполнение с прежнего места - там где оно остановилось. alex_fff 30.07.15 в 21:24 Классический стиль сшивания известен как косвенное сшивание, но более современные разновидности форта реализованы с прямым сшиванием. Разница заключается в уровне косвенности. Низкоуровневая эффективность реализации косвенности зависит от процессора, и мы не будем углубляться в детали. По форт сшиваниям есть немало хороших руководств [STARTING-FORTH] [MOVING-FORTH]. В памяти компьютера эти оба стиля сшивания реализованы как смежные ячейки, в которых целочисленные машинные слова представляют указатели. Внутренний интерпретатор - маленький, компактный машинный код - обычно предназначается для процессора, выполняющего свою важную работу: следовать по указателям этих форт нитей, с последующей интерпретацией их значений. По-умолчанию, при встрече ячейки используется такое поведение: поместить текущий указатель расположения программы в стек возврата, после чего изменить указатель расположения программы на то, что указывает содержимое ячейки. Когда внутренний интерпретатор достигнет конца нити, он извлечёт значение из стека возврата и продолжит исполнение с прежнего места - там где оно остановилось 6. Charlz_Klug 21.10.15 в 12:10 |
||
|
6 В большинстве фортов, конец тредов обозначается форт словом exit. Charlz_Klug 16.03.15 в 21:11 6 В большинстве фортов, конец нити обозначается форт словом exit. alex_fff 30.07.15 в 21:24 |
||
|
Как вы можете себе представить, такой тип программного хранилища предназначен для чрезвычайно маленьких программ. Скомпилированное форт слово - это всего лишь последовательный массив целых чисел, большинство из которых представляют указатели на другие слова. Это всегда было одним из преимуществ форта. Поскольку прозрачность тредов программы заключается в памяти, форт позволяет хорошо контролировать многие компромиссы, на которые приходится идти при программировании, включая один из наиболее важных: Скорость выполнения против размера программы. Код с тредами позволяет нам оптимизировать наши абстракции настолько близко к проблеме, насколько это возможно, в результате получая экстремально быстрые, маленькие программы. Но, лисп макросы предназначены для гораздо большего, чем просто эффективность, то же самое можно сказать и по отношению к форт тредам. Как и большинство лисп программистов, форт программисты склонны считать себя реализаторами нового, а не просто пользователями. Форт и лисп - оба предназначены для контроля - пишите свои собственные правила. Charlz_Klug 16.03.15 в 21:28 Как вы можете себе представить, такой тип программного хранилища предназначен для чрезвычайно маленьких программ. Скомпилированное форт слово - это всего лишь последовательный массив целых чисел, большинство из которых представляют указатели на другие слова. Это всегда было одним из преимуществ форта. Из-за прозрачности сшивания программы в памяти, форт позволяет хорошо контролировать многие компромиссы, на которые приходится идти при программировании, включая один из наиболее важных: Скорость выполнения против размера программы. Шитый код позволяет нам оптимизировать наши абстракции настолько близко к проблеме, насколько это возможно, в результате получая экстремально быстрые, маленькие программы. Но, лисп макросы предназначены для гораздо большего, чем просто эффективность, то же самое можно сказать и по отношению к форт нитям. Как и большинство лисп программистов, форт программисты склонны считать себя реализаторами нового, а не просто пользователями. Форт и лисп - оба предназначены для контроля - пишите свои собственные правила. alex_fff 30.07.15 в 21:28 |
||
|
Существует, по-крайней мере, два общих типа техник форт тредов: код с тредами на лексемах и код с тредами на подпрограммах. Они представляют два полюса при решении компромисса между скоростью и размером. Иногда эти техники реализации тредов сосуществуют с косвенным и непосредственным тредами в одном форте. Треды на лексемах включают в себя добавление другого уровня косвенности через использование целых чисел, даже меньших чем указатели на существующие слова в треде. На другом конце спектра располагаются треды на подпрограммах. Этот тип тредов становится популярным и лучшие, современные форт компиляторы частично используют треды на подпрограммах. Вместо следующих подряд указателей на слова, по которым будет следовать внутренний интерпретатор, треды на подпрограммах содержат встроенные машинные инструкции, вызывающие эти указатели. В тредах на подпрограммах исчезает внутренний интерпретатор - он уже реализован оборудованием (или виртуальной машиной). Треды на подпрограммах обычно считаются непрозрачным блоком, которыми может управлять только специальный, не программируемый компилятор. Если же в этот код внесены различные оптимизации, то эти непрозрачные блоки становятся совсем непохожими на однообразные треды, основанные на ячейках. Почти все не-форт компиляторы в результате работы выдают код, использующий треды на подпрограммах и не считаются с тем, что вы можете захотеть сделать что-то другое, что подводит нас к такому своеобразному определению: Charlz_Klug 17.03.15 в 07:16 Существует, по-крайней мере, два общих типа техник форт сшивания: шитый код на лексемах и шитый код на подпрограммах. Они представляют противоположные подходы при решении компромисса между скоростью и размером. Иногда эти техники сшивания сосуществуют с косвенным и прямым шитым кодом в одном форте. Сшивание на лексемах включает в себя добавление другого уровня косвенности через использование целых чисел, даже меньших чем указатели на существующие слова в нити. На другом конце спектра располагается сшивание на подпрограммах. Этот тип шитого кода становится популярным и лучшие, современные форт компиляторы частично используют сшивание на подпрограммах. Вместо следующих подряд указателей на слова, по которым будет следовать внутренний интерпретатор, шитый код на подпрограммах содержит встроенные машинные инструкции, вызывающие эти указатели. В шитом коде на подпрограммах исчезает внутренний интерпретатор - он уже реализован оборудованием (или виртуальной машиной). Шитый код на подпрограммах обычно считается непрозрачным блоком, которым может управлять только специальный, не программируемый компилятор. Если же в этот код внесены различные оптимизации, то эти непрозрачные блоки становятся совсем непохожими на единообразные нити, основанные на ячейках. Почти все не-форт компиляторы в результате работы выдают шитый код на подпрограммах и не считаются с тем, что вы можете захотеть сделать что-то другое, что подводит нас к такому своеобразному определению: alex_fff 30.07.15 в 21:38 |
||
|
Flub (Флаб) - это язык, работающий только с тредами на подпрограммах или язык, реализация которого предоставляет только треды на подпрограммах. Charlz_Klug 17.03.15 в 08:22 Flub (Флаб) - это язык, работающий только с шитым кодом на подпрограммах или язык, реализация которого предоставляет только шитый код на подпрограммах. alex_fff 30.07.15 в 21:40 |
||
|
Например, C - это Флаб потому, что позволяет программистам создавать функции - непрозрачные блоки кода, использующего треды на процедурах. Конечно, мы можем реализовать на C внутренний интерпретатор для обработки косвенных тредов 7 и, с помощью этой программы, самозагрузиться в стековый язык, но, тогда получается, что мы уже не программируем на C. Почти все Блаб языки - Флабы. Форт - как мы это только что выяснили - не Флаб. Как мы уже увидели, форт даёт программистам/создателям языков много возможности для контроля компиляции их программ. Charlz_Klug 17.03.15 в 09:57 Например, C - это Флаб потому, что единственное что он позволяет программистам - это создавать функции - непрозрачные блоки кода, использующего треды на процедурах. Конечно, мы можем реализовать на C внутренний интерпретатор для обработки косвенных тредов 7 и, с помощью этой программы, самозагрузиться в стековый язык, но, тогда получается, что мы уже не программируем на C. Почти все Блаб языки - Флабы. Форт - как мы это только что выяснили - не Флаб. Как мы уже увидели, форт даёт программистам/создателям языков много возможности для контроля компиляции их программ. Charlz_Klug 17.03.15 в 09:59 Например, C - это Флаб потому, что единственное что он позволяет программистам - это создавать функции - непрозрачные блоки шитого кода на подпрограммах. Конечно, мы можем реализовать на C внутренний интерпретатор для обработки косвенно шитого кода 7 и, с помощью этой программы, самозагрузиться в стековый язык, но, тогда получается, что мы уже не программируем на C. Почти все Блаб языки - Флабы. Форт - как мы это только что выяснили - не Флаб. Как мы уже увидели, форт даёт программистам/создателям языков много возможности для контроля компиляции их программ. alex_fff 30.07.15 в 21:44 Например, C - это Флаб потому, что единственное что он позволяет программистам - это создавать функции - непрозрачные блоки шитого кода на подпрограммах. Конечно, мы можем реализовать на C внутренний интерпретатор для обработки косвенно шитого кода 7 и, с помощью этой программы, самостоятельно выстроить стековый язык, но, тогда получается, что мы уже не программируем на C. Почти все Блаб языки - Флабы. Форт - как мы это только что выяснили - не Флаб. Позже мы увидим что форт даёт программистам/создателям языков много возможности для контроля компиляции их программ. Charlz_Klug 21.10.15 в 12:22 |
||
|
7 Впрочем, можно реализовать работу с непосредственными тредами, но это значительно сложнее. Charlz_Klug 17.03.15 в 12:45 7 Впрочем, можно реализовать работу с прямым шитым кодом, но это значительно сложнее. alex_fff 30.07.15 в 21:44 |
||
|
Является ли лисп Флабом? Интересно, но лисп, возможно, был первым не Флаб языком программирования, но потом постепенно стал в основном Флабом. Хотя это не строго требуется стандартом, основные COMMON LISP компиляторы только компилируют функции в блоки непрозрачного машинного кода и, таким образом, они являются Флабами. Но в самых первых версиях лиспа функции сохранялись как списки - странная разновидность тредов, не сильно отличающаяся от форт тредов. Такой подход позволял выполнять некоторые очень умные трюки во время выполнения программы, включая создание цикличного кода, но, был безнадёжно неэффективным. В отличие от фортовских разновидностей тредов - эффективно реализованных на почти всех архитектурах - такое внутреннее представление лисп функций было недопустимым и лисп был модифицирован для того, чтобы генерировать (чрезвычайно) эффективный код. Соответственно, для метапрограммистов, большинство реализаций COMMON LISP-а являются Флабами. Charlz_Klug 17.03.15 в 13:05 Является ли лисп Флабом? Интересно, но лисп, возможно, был первым не Флаб языком программирования, но потом постепенно стал в основном Флабом. Хотя это не строго требуется стандартом, основные COMMON LISP компиляторы только компилируют функции в блоки непрозрачного машинного кода и, таким образом, они являются Флабами. Но в самых первых версиях лиспа функции сохранялись как списки - странная разновидность сшивания кода, не сильно отличающаяся от форт нити. Такой подход позволял выполнять некоторые очень умные трюки во время выполнения программы, включая создание цикличного кода, но, был безнадёжно неэффективным. В отличие от фортовских разновидностей сшивания - эффективно реализованных на почти всех архитектурах - такое внутреннее представление лисп функций было недопустимым и лисп был модифицирован для того, чтобы генерировать (чрезвычайно) эффективный код. Соответственно, для метапрограммистов, большинство реализаций COMMON LISP-а являются Флабами. alex_fff 30.07.15 в 21:47 |
||
|
Но есть разница между свойствами, которые невозможно добавить в язык и свойствами, которые мы можем добавить с помощью макросов. С помощью макросов мы можем расширять язык любыми способами и он по-прежнему будет оставаться лиспом. В COMMON LISP-е нет форт нитей из-за тех же причин, по которым нет продолжений и первоклассных макросов: они сознательно не реализованы в языке для того, чтобы писатели макросов реализовали их так, как им нужно. Одна из наиболее важных целей этой главы и кода в том, чтобы показать, что лисп хоть и является Флабом, но может трансформироваться в не-Флаб языки с помощью макросов. Не Блаб подразумевает, не Флаб, или другими словами, если вы не можете преобразовать язык в не Флаб, то он должен быть Блабом. Однако, обратное неверно. Не Флаб языки, такие как форт продолжают оставаться Блабом и самый короткий путь сделать их не Блабом - это реализовать с их помощью лисп среду - после чего вам остаётся программировать на лиспе. Charlz_Klug 17.03.15 в 14:09 Но есть разница между свойствами, которые невозможно добавить в язык и свойствами, которые мы можем добавить с помощью макросов. С помощью макросов мы можем расширять язык любыми способами и он по-прежнему будет оставаться лиспом. В COMMON LISP-е нет форт нитей из-за тех же причин, по которым нет продолжений и первоклассных макросов: они сознательно не реализованы в языке для того, чтобы писатели макросов реализовали их так, как им нужно. Одна из наиболее важных целей самой главы и её кода в том, чтобы показать, что лисп хоть и является Флабом, но может трансформироваться в не-Флаб языки с помощью макросов. Не Блаб подразумевает, не Флаб, или другими словами, если вы не можете преобразовать язык в не Флаб, то он должен быть Блабом. Однако, обратное неверно. Не Флаб языки, такие как форт продолжают оставаться Блабом и самый короткий путь сделать их не Блабом - это реализовать с их помощью лисп среду - после чего вам остаётся программировать на лиспе. Charlz_Klug 21.10.15 в 12:30 |
||
|
Листинг 8.4: FORTH-INNER-INTERPRETER Charlz_Klug 19.03.15 в 14:30 |
||
|
Вместо того, чтобы использовать последовательные ячейки памяти для представления косвенных/непосредственных тредов, наш форт воспользуется преимуществом лисповской динамической типизации и структурой списка из cons ячеек. Мы называем такой код, кодом с тредами на основе cons ячеек. Макрос forth-inner-interpreter расширяется в код, способный следовать по тредам, состоящим из cons ячеек связанных списков. Может показаться странным начинать программировать логику нашей форт среды именно так - с помощью макросов, которые должны раскрываться в некоторое, ещё недостаточно известное выражение - но, на самом деле - это предпочтительный шаблон лисп программирования. Поскольку макросы позволяют нам начинать программировать везде где мы хотим, почему бы не начать с действительно интересного, основного механизма программы? Этот механизм будет оказывать наибольшее влияние на окончательную архитектуру программы. Charlz_Klug 20.03.15 в 17:45 Вместо того, чтобы использовать последовательные ячейки памяти для представления косвенных/непосредственных нитей, наш форт воспользуется преимуществом лисповской динамической типизации и структурой списка из cons ячеек. Мы называем такой код cons шитым кодом. Макрос forth-inner-interpreter расширяется в код, способный следовать по нитям, состоящим из cons ячеек связанных списков. Может показаться странным начинать программировать логику нашей форт среды именно так - с помощью макросов, которые должны раскрываться в некоторое, ещё недостаточно известное выражение - но, на самом деле - это предпочтительный шаблон лисп программирования. Поскольку макросы позволяют нам начинать программировать везде где мы хотим, почему бы не начать с действительно интересного, основного механизма программы? Этот механизм будет оказывать наибольшее влияние на окончательную архитектуру программы. alex_fff 30.07.15 в 21:51 |
||
|
Определение forth-inner-interpreter представляет собой четкое описание того, что мы подразумеваем под cons тредами. Car каждой cons ячейки указывает либо на функцию, либо на другую cons ячейку или на atom. Когда встречается функция, она выполняется. Заметьте, что изменение pc регистра остается на усмотрение функции. Если в треде найдена cons ячейка, то предполагается указание на вызов подпрограммы – вызов слова. Наш внутренний интерпретатор поместит точку возобновления из pc в стек возврата и перейдет к этому новому треду. Если встретится какой-то другой atom, он будет просто помещен в стек параметров и выполнение продолжится в следующей ячейке нашего треда. Внутренний интерпретатор завершит работу, когда дойдет до конца треда и не останется других тредов для возобновления в его стеке возврата. alex_fff 03.06.15 в 15:13 Определение forth-inner-interpreter представляет собой четкое описание того, что мы подразумеваем под cons тредами. Car каждой cons ячейки указывает либо на функцию, другую cons ячейку, либо на другой лисп atom. Функции исполняются в таком порядке, в каком они встречаются. Заметьте, что изменение pc регистра остается на усмотрение самой функции. Если в треде найдена cons ячейка, то предполагается указание на вызов подпрограммы – вызов слова. Наш внутренний интерпретатор поместит точку возобновления из pc в стек возврата и перейдет к этому новому треду. Если встретится какой-то другой атом, то он будет просто помещен в стек параметров и выполнение продолжится со следующей ячейки нашего треда. Внутренний интерпретатор завершит работу, когда дойдет до конца треда и не останется других тредов для возобновления в его стеке возврата. Charlz_Klug 11.06.15 в 11:11 Определение forth-inner-interpreter представляет собой четкое описание того, что мы подразумеваем под cons шитым кодом. Car каждой cons ячейки указывает либо на функцию, другую cons ячейку, либо на другой лисп atom. Функции исполняются в таком порядке, в каком они встречаются. Заметьте, что изменение pc регистра остается на усмотрение самой функции. Если в нити найдена cons ячейка, то предполагается указание на вызов подпрограммы – вызов слова. Наш внутренний интерпретатор поместит точку возобновления из pc в стек возврата и перейдет к этой новой нити. Если встретится какой-то другой атом, то он будет просто помещен в стек параметров и выполнение продолжится со следующей ячейки нашей нити. Внутренний интерпретатор завершит работу, когда дойдет до конца нити и не останется других нитей для возобновления в его стеке возврата. alex_fff 04.08.15 в 20:13 |
||
|
Листинг 8.5: PRIM-FORMS alex_fff 03.06.15 в 15:14 |
||
|
Но конечно же функции не могут изменить pc переменную, если они не определены в ее лексической видимости, поэтому обратимся к другой технике макросов: вместо использования defun мы создаем похожий интерфейс, который делает нечто совершенно отличное. Def-forth-naked-prim очень похож на создание функций с помощью defun за исключением того, что он выполняет push предоставленных пользователем форм в список, хранимый в forth-prim-forth. Наш итоговый макрос будет использовать эти формы, чтобы определить примитивы форта в их лексической видимости. Так как эти формы всегда будут разворачиваться в одно окружение, мы без ограничений можем писать код, использующий все наши абстрактные регистры фортат подобные pc, pstack и т.д. alex_fff 06.06.15 в 14:30 Но конечно же функции не могут изменить pc переменную, если они не определены в ее лексической области видимости 8, поэтому обратимся к другой технике макросов: вместо использования defun мы создаем похожий интерфейс, который делает нечто совершенно иное. Def-forth-naked-prim очень похож на создание функций с помощью defun за исключением того, что код, в который он расширяется, вставляет формы, определённые пользователем, в список, хранимый в forth-prim-forth. Наш итоговый макрос будет использовать эти формы, для того чтобы определять примитивы форта в лексической области видимости. Так как эти формы всегда будут разворачиваться в эту среду, то мы можем без ограничений писать код, использующий все наши абстрактные регистры форта, такие как pc, pstack и т.д. Charlz_Klug 11.06.15 в 11:22 |
||
|
Есть исключения. Смотри Анафорические макросы. alex_fff 06.06.15 в 14:32 8 Есть исключения. Смотри Анафорические макросы. Charlz_Klug 21.10.15 в 12:38 |
||
|
Примитивы, определенные с помощью def-forth-naked-prim не будут устанавливать переменную pc на следующую ячейку в треде. Для большинства примитивов нам следует использовать def-forth-prim для выполнения обычного изменения. Оба этих макроса ожидают, что первым аргументом будет символ для ссылки на данный примитив, вторым будет булево значение, указывающее, будет ли данный примитив безотлагательным. Остаток аргументов - лисп формы, которые будут вычисляться при выполнении примитива. alex_fff 08.06.15 в 14:31 Примитивы, определенные с помощью def-forth-naked-prim не будут устанавливать переменную pc на следующую ячейку в треде. Для большинства примитивов нам следует использовать def-forth-prim для выполнения обычного обновления. Оба этих макроса ожидают, что первым аргументом будет символ для ссылки на данный примитив, вторым будет булево значение, указывающее, будет ли данный примитив безотлагательным. Остаток аргументов - лисп формы, которые будут вычисляться при выполнении примитива. Charlz_Klug 11.06.15 в 11:26 Примитивы, определенные с помощью def-forth-naked-prim не будут устанавливать переменную pc на следующую ячейку в нити. Для большинства примитивов нам следует использовать def-forth-prim для выполнения обычного обновления. Оба этих макроса ожидают, что первым аргументом будет символ для ссылки на данный примитив, вторым будет булево значение, указывающее, будет ли данный примитив незамедлительным. Остаток аргументов - лисп формы, которые будут вычисляться при выполнении примитива. alex_fff 04.08.15 в 20:14 |
||
|
Листинг 8.6: BASIC-PR.IM-FORMS alex_fff 08.06.15 в 14:32 |
||
|
Восемь простых примитивов - ни один из них не является изолированным (naked) или безотлагательным - представлены. Nop - фиктивная инструкция, которая ничего не делает ("нет операции" ("no operaiton")). Примитив * является операцией умножения: он достает два верхних элемента из стека параметров, перемножает их, затем помещает результат обратно. Dup - краткое название для "дубликат" ("duplicate"), он помещает верхний элемент стека снова в стек, создавая два идентичных значения. Swap меняет местами два элемена стека параметров, используя очень полезный COMMON LISP макрос: rotatef. Это не случайно, что форт также имеет (оперирующие стеком) механизмы вращения. Print вынимает элемент стека параметров и печатает его. >r перемещает значение из стека параметров в стек возврата, r> делает обратное. alex_fff 11.06.15 в 09:42 Восемь простых примитивов - ни один из них не является изолированным (naked) или безотлагательным - будут описаны прямо сейчас. Nop - фиктивная инструкция, которая ничего не делает ("нет операции" ("no operaiton")). Примитив * является операцией умножения: он достает два верхних элемента из стека параметров, перемножает их, затем помещает результат обратно. Dup - краткое название для "дубликат" ("duplicate"), он помещает верхний элемент стека снова в стек, создавая два идентичных значения. Swap меняет местами два элемента стека параметров, используя очень полезный COMMON LISP макрос: rotatef. Это не случайно, что форт также имеет (оперирующие стеком) механизмы замены. Print вынимает элемент стека параметров и печатает его. >r перемещает значение из стека параметров в стек возврата, r> делает обратную операцию. Charlz_Klug 11.06.15 в 11:32 Восемь простых примитивов - ни один из них не является изолированным (naked) или незамедлительным - будут описаны прямо сейчас. Nop - фиктивная инструкция, которая ничего не делает ("нет операции" ("no operaiton")). Примитив * является операцией умножения: он достает два верхних элемента из стека параметров, перемножает их, затем помещает результат обратно. Dup - краткое название для "дубликат" ("duplicate"), он помещает верхний элемент стека снова в стек, создавая два идентичных значения. Swap меняет местами два элемента стека параметров, используя очень полезный COMMON LISP макрос: rotatef. Это не случайно, что форт также имеет (оперирующие стеком) механизмы замены. Print вынимает элемент стека параметров и печатает его. >r перемещает значение из стека параметров в стек возврата, r> делает обратную операцию. alex_fff 04.08.15 в 20:16 |
||
|
Нарушает ли имя * наше важное правило захвата переменых из раздела 3.5 Нежелательный Захват, которое запрещает переопределять функции, определенные самим COMMON LISP? Нет, потому что, на самом деле, мы не используем этот символ для связывания с какой-то функцией - это просто первый элемент одного из списков в foth-prim-forms. Мы не сделали ничего неправильного. Символы независимы от функций или макросов, на которые они иногда указывают. Мы можем использовать любые символы где угодно, до тех пор, пока мы уверены, что не нарушаем наши важные правила захвата переменных. Это учитывается только при написании лиспа; мы же пишем форт. alex_fff 11.06.15 в 10:13 Нарушает ли имя * наше важное правило захвата переменых из раздела 3.5 Нежелательный Захват, которое запрещает переопределять функции, определенные самим COMMON LISP? Нет, потому что, на самом деле, мы не импользуем этот символ для связывания с какой-то функцией - это просто первый элемент одного из списков в foth-prim-forms. Мы не сделали ничего неправильного. Символы независимы от функций или макросов, которых они иногда обозначают. Мы можем использовать любые символы где угодно, до тех пор, пока мы уверены, что не нарушаем наши важные правила захвата переменных. Они вступают в игру только при написании лиспа; мы же пишем форт. Charlz_Klug 11.06.15 в 11:36 |
||
|
8.3 Двойственность Синтаксиса, Определённая Charlz_Klug 11.06.15 в 13:12 |
||
|
Если вы ещё ничего не запомнили из этой книги, то запомните заголовок этого раздела. Здесь мы, наконец, определяем и объясняем, уже затронутую ранее, концепцию: двойственность синтаксиса. Подразумевается что ещё до чтения этой главы вы прочитали хотя бы три первых глав, главу 6, Анафорические Макросы и предыдущие разделы этой главы. Charlz_Klug 12.06.15 в 20:45 |
||
|
Большинству программистов очевидно из опыта, что программировать на лиспе более производительно и в итоге более естественно, чем программировать на Блаб языке, но гораздо труднее ответить на вопрос почему это так. Хоть и правда, что лисп черпает удивительную силу выразительности из макросов - и мы видели много интересных примеров в этой книге и в других источниках - все объяснения кажутся неудовлетворительными. В чем настоящее преимущество макросов? Частичное объяснение конечно включает лаконичность, делающее ваши программы короче. Вот его определение: alex_fff 13.06.15 в 19:24 |
||
|
Пусть L язык программирования, F свойство (feature) этого языка программирования, и A произвольная (arbitrary) программа на L. F обеспечивает лаконичность L, если A короче, чем была бы в версии L без F. alex_fff 13.06.15 в 19:40 |
||
|
Свойства лаконичности определяют базис и разумность теории лаконичности: alex_fff 16.06.15 в 15:47 Свойства лаконичности определяют базис и рациональность теории лаконичности: Charlz_Klug 02.07.15 в 22:25 |
||
|
Трудозатраты, необходимые для написания программы обратно пропорциональны количеству свойств лаконичности, доступных в используемом языке программирования. alex_fff 16.06.15 в 15:51 |
