Каково резюме различий в поведении связывания между Rebol 2 и 3?
Текущая углубленная документация по целям привязки переменных Rebol 2. Может ли кто-нибудь представить сводку различий между Rebol 2 и 3?
1 ответ
Где-то нет резюме, так что давайте рассмотрим основы, возможно, немного более неформально, чем Bindology. Пусть Ладислав напишет новую версию своего трактата для R3 и Red. Мы просто рассмотрим основные различия в порядке важности.
Контексты объектов и функций
Вот большая разница.
В R2 было в основном два вида контекстов: обычные контексты объектов и system/words
, У обоих были статические привязки, что означает, что когда-то bind
функция была запущена, привязка слова указывала на конкретный объект с реальным указателем.
system/words
контекст мог быть расширен во время выполнения, чтобы включить новые слова, но все остальные объекты не были. В функциях использовались обычные контексты объектов, с некоторыми хакерами для переключения блоков значений при рекурсивном вызове функции.
self
слово было просто обычным словом, которое произошло с первым в контекстах объекта, с отображением взлома, чтобы не показывать первое слово в контексте; В контекстах функций не было этого слова, поэтому они не отображали первое обычное слово должным образом.
В R3 почти все по-другому.
В R3 также есть два вида контекстов: обычный и локальный в стеке. Регулярные контексты используются объектами, модулями, связующими циклами, use
в основном все, кроме функций, и они расширяются как system/words
был (да, "был", мы к этому вернемся). Старые объекты фиксированной длины исчезли. Функции используют локальные контексты стека, которые (за исключением ошибок, которые мы еще не видели) не должны расширяться, потому что это может испортить кадры стека. Как со старым system/words
Вы не можете уменьшить контексты, потому что удаление слов из контекста нарушило бы любые привязки этих слов.
Если вы хотите добавить слова в обычный контекст, вы можете использовать bind/new
, bind/set
, resolve/extend
или же append
или другие функции, которые вызывают их, в зависимости от того, какое поведение вам нужно. Это новое поведение для bind
а также append
функции в R3.
Привязки слов к обычному и локальному стекам являются статическими, как и раньше. Поиск значения это другое дело. Для обычного контекста поиск значения довольно прямой, осуществляется путем простого перенаправления указателя на статический блок слотов значений. Для локальных стековых контекстов блок значений связан с фреймом стека и ссылается оттуда, поэтому, чтобы найти правильный фрейм, вы должны выполнить обход стека, который равен O(глубина стека). Подробности смотрите в баге № 1946 - позже мы выясним, почему.
Ах, и self
это больше не обычное слово, это обязательный трюк, ключевое слово. Когда вы связываете блоки слов с контекстами объекта или модуля, он связывает ключевое слово self
который оценивает быть ссылкой на контекст. Тем не менее, есть внутренний флаг, который может быть установлен, который говорит, что контекст "самоотвержен", что превращает это в self
Ключевое слово выключено. Когда это ключевое слово выключено, вы можете использовать слово self
как поле в вашем контексте. Обвязочные петли, use
и контексты функций устанавливают флаг самоотверженности для своих контекстов, а selfless?
функция проверяет это.
Эта модель была доработана и задокументирована в довольно сложной войне с пламенем CureCode, так же, как модель R2 была задокументирована войной пламени списка рассылки REBOL в 1999-2000 годах.:-)
Функции против замыканий
Когда я говорил о контекстах локальной функции стека выше, я имел в виду контексты, используемые function!
функции типа. R3 имеет много типов функций, но большинство из них так или иначе являются нативными функциями, и нативные функции не используют эти локальные стековые контексты (хотя они получают стековые фреймы). Единственные типы функций, которые предназначены для кода Rebol: function!
и новый closure!
тип. Закрытия сильно отличаются от обычных функций.
Когда вы создаете function!
Вы создаете функцию. Он создает локальный контекст стека, привязывает к нему тело кода и связывает тело кода и спецификацию. Когда вы вызываете функцию, она создает кадр стека со ссылкой на контекст функции и запускает блок кода. Если у него есть слова доступа в контексте функции, он просматривает стек, чтобы найти правильный кадр, а затем получает значения оттуда. Довольно просто.
Когда вы создаете closure!
с другой стороны, вы создаете конструктор функций. Он устанавливает спецификацию и функциональное тело почти так же, как function!
, но когда вы вызываете замыкание, оно создает новый обычный самоотверженный контекст, а затем bind/copy
тела, изменяя все ссылки на контекст функции, чтобы они были ссылками на новый регулярный контекст в копии. Затем, когда он выполняет копирование тела, все ссылки на закрывающие слова являются такими же статическими, как и в контекстах объектов.
Другое различие между ними состоит в том, как они ведут себя до запуска функции, во время выполнения функции и после завершения работы функции.
В R2 function!
контексты по-прежнему существуют, когда функция не запущена, но блок значений вызова верхнего уровня функции также сохраняется. Только рекурсивные вызовы получают блоки новых значений, вызов верхнего уровня сохраняет блок постоянных значений. Как я уже сказал, хакерство. Хуже того, блок значений верхнего уровня не очищается при возврате функции, поэтому вам лучше убедиться, что вы не ссылаетесь на что-то чувствительное или что вы хотите перезапускаться при возврате функции (используйте also
функция для очистки, вот для чего я это сделал).
В R3 function!
контексты все еще существуют, когда функция не запущена, но блок значений вообще не существует. Все вызовы функций действуют как рекурсивные вызовы в R2, за исключением того, что они лучше спроектированы таким образом, что вместо этого ссылаются на кадр стека. Область этого стекового фрейма является динамической (если вам нужна история этого, вы можете следить за фэном Lisp), так что пока функция работает в текущем стеке (да, "текущий", мы вернемся к этому), вы можно использовать одно из его слов, чтобы получить значения самого последнего вызова этой функции. Как только все вложенные вызовы функции вернутся, в области не будет никаких значений, и вы просто вызовете ошибку (неправильную, но мы исправим ее).
Есть также бесполезное ограничение на привязку к функциональному слову, выходящему за пределы области действия, которое скоро будет исправлено в моем списке задач. Смотрите ошибку #1893 для деталей.
За closure!
функции, до того, как замыкание выполняется, контекст вообще не существует. Как только замыкание начинает выполняться, контекст создается и существует постоянно. Если вы вызываете замыкание снова или рекурсивно, создается другой постоянный контекст. Любое слово, которое вытекает из замыкания, относится только к контексту, созданному во время этого конкретного выполнения замыкания.
Вы не можете связать слово с функцией или контекстом закрытия в R3, когда функция или замыкание не запущены. Для функций это проблема безопасности. Для замыканий это вопрос определения.
Замыкания считались настолько полезными, что мы с Ладиславом портировали их на R2, независимо в разное время, что приводило к схожему коду, как ни странно. Я думаю, что версия Ладислава предшествовала R3, и послужила вдохновением для R3 closure!
тип; моя версия была основана на тестировании внешнего поведения этого типа и попытке реплицировать его в R2 для R2/Forward, поэтому забавно, что решение для closure
в итоге оказалось так похоже на оригинал Ладислава, который я не видел намного позже. Моя версия была включена в R2, начиная с 2.7.7, так как closure
, to-closure
а также closure?
функции и closure!
слову присваивается то же значение типа, что и function!
в R2.
Глобальные и локальные контексты
Здесь вещи становятся действительно интересными.
В Bindology было довольно большое количество статей, в которых говорилось о различии между "глобальным" контекстом (который оказался system/words
) и "локальные" контексты, довольно важное различие для R2. В R3 это различие не имеет значения.
В R3 system/words
ушел Нет единого "глобального" контекста. Все регулярные контексты являются "локальными" в том смысле, в каком они были в R2, что делает это значение "локальным" бесполезным. Для R3 нам нужен новый набор терминов.
Для R3 единственное различие, которое имеет значение, состоит в том, являются ли контексты относительными к задаче, поэтому единственное полезное значение для "глобальных" контекстов - это те, которые не относятся непосредственно к задаче, а "локальные" контексты - это те, которые относятся к задаче., "Задача" в этом случае будет task!
тип, который в основном является потоком ОС в текущей модели.
В R3 на данный момент единственными объектами, относящимися к задаче (едва), являются переменные стека, что означает, что контексты функций, относящихся к стеку, также должны относиться к задачам. Вот почему обход стека необходим, потому что в противном случае нам нужно было бы сохранять и поддерживать указатели TLS в каждом отдельном контексте функции. Все обычные контексты являются глобальными.
Еще одна вещь, которую следует учитывать, заключается в том, что согласно плану (который в основном пока не реализован), пользовательский контекст system/contexts/user
а также system
само по себе также должно быть относительным к задаче, поэтому даже по стандартам R3 они будут считаться "местными". И с тех пор system/contexts/user
в основном самое близкое, что R3 имеет к R2 system/words
это означает, что то, что сценарии считают своим "глобальным" контекстом, фактически должно быть локальным в R3.
R3 имеет пару системных глобальных контекстов, называемых sys
а также lib
хотя они используются совершенно иначе, чем глобальный контекст R2. Кроме того, все контексты модуля являются глобальными.
Возможно (и распространено), что существуют глобально определяемые контексты, на которые ссылаются только из локальных корневых ссылок задачи, так что эти контексты становятся косвенно локально зависимыми от задачи. Это то, что обычно происходит при связывании циклов, use
замыкания или закрытые модули вызываются из "пользовательского кода", что в основном означает немодульные сценарии, которые связаны с system/contexts/user
, Технически, это также относится и к функциям, вызываемым из модулей (поскольку функции являются локальными в стеке), но эти ссылки часто в конечном итоге присваиваются словам модуля, которые являются глобальными.
Нет, у нас пока нет синхронизации. Тем не менее, это модель, которую дизайн R3 должен в конечном итоге иметь, и частично уже. См. Статью по связыванию модулей для более подробной информации.
В качестве бонуса, R3 теперь имеет реальную таблицу символов вместо использования system/words
в качестве специальной таблицы символов. Это означает, что ограничение по слову R2, используемое для довольно быстрого попадания, фактически исчезло в R3. Я не знаю ни одного приложения, которое достигло нового предела или даже определило, насколько оно велико, хотя оно явно превышает много миллионов различных символов. Мы должны проверить источник, чтобы выяснить это, теперь, когда у нас есть доступ к нему.
ЗАГРУЗИТЬ и ИСПОЛЬЗОВАТЬ
Незначительные детали. use
функция инициализирует свои слова с none
вместо того, чтобы оставить их неустановленными. И поскольку нет "глобального" контекста, как в R2, load
не обязательно связывать слова вообще. Какой контекст load
привязка к зависит от обстоятельств, упомянутых в статье, связанной с модулем, хотя, если вы не укажете иное, это явно привязывает слова к system/contexts/user
, И оба теперь являются мезонинными функциями.
Орфография и псевдонимы
R3 в основном совпадает с R2, привязки слов по умолчанию не чувствительны к регистру. Сами слова сохраняют регистр, и если вы сравните их, используя чувствительные к регистру методы, вы увидите различия между словами, которые отличаются только регистром.
Однако в контексте объекта или функции, когда слово отображается в слот значения, тогда другое слово связывается с этим контекстом или просматривается во время выполнения, слова, которые отличаются только регистром, фактически считаются одним и тем же словом и отображаются на одно и то же. слот значения.
Однако было обнаружено, что явно созданные псевдонимы, сделанные с alias
функция, в которой написание слов с псевдонимами отличалось не так, как в каждом конкретном случае, что резко ухудшало контексты объекта и функции. В R2 они решили эти проблемы в system/words
, который сделал alias
просто слишком неловко, чтобы использовать что-либо кроме демо, а не активно опасно.
Из-за этого мы удалили внешне видимые alias
функционировать в целом. Внутреннее средство псевдонима все еще работает, потому что оно только псевдонимами слов, которые обычно считаются эквивалентными для поиска контекста, что означает, что контексты не нарушаются. Но теперь мы рекомендуем локализацию и другие приемы, которые alias
был использован для демонстраций, если никогда на практике, не было сделано, используя старомодный метод присвоения значений другому слову с новым написанием.
Типы слов
issue!
тип теперь слово типа. Вы можете связать это. До сих пор никто не воспользовался возможностью связать проблемы, вместо этого просто используя увеличенную скорость операций.
Вот и все, для преднамеренных изменений. Большинство остальных различий могут быть побочными эффектами вышеупомянутого, или даже ошибками или еще не реализованными функциями. В R3 может даже быть некоторое поведение, подобное R2, которое также является результатом ошибок или еще не реализованных функций. Если вы сомневаетесь, спросите сначала.