Являются ли функции изменчивыми в нескольких системах диспетчеризации?
Правильно ли я понял, что в (большинстве? Некоторых?) Языках с несколькими диспетчеризацией каждый метод добавляется в функцию в определенный момент времени выполнения программы.
Можно ли тогда сделать вывод, что множественная диспетчеризация как функция заставляет функции быть изменяемыми?
Существует ли язык множественных диспетчеризаций, где все методы присоединены к (универсальной) функции вместе (во время загрузки?), Так что невозможно увидеть функцию в разных состояниях в разные моменты времени?
5 ответов
в какой-то момент времени выполнения программы.
В Common Lisp методы добавляются / заменяются при выполнении определений методов - для скомпилированной системы это обычно происходит во время загрузки скомпилированного кода - не обязательно во время выполнения программы.
Помните, что Common Lisp имеет объектную систему (CLOS, Common Lisp Object System), которая определяется его поведением. Это немного отличается от языка или языкового расширения.
Common Lisp позволяет модифицировать объектную систему во время выполнения. Например, также добавление / удаление / замена методов.
Common Lisp также может объединять более одного применимого метода в эффективный метод, который затем исполняется. Типичный пример: все применимо :before
методы и наиболее конкретный применимый первичный метод будут объединены в один эффективный метод.
В некоторых реализациях существуют расширения для CLOS, которые защищают универсальную функцию от изменений.
Более длительное рассмотрение идеи объектной системы см.: Ричард П. Габриэль, "Структура революции языка программирования".
В Common Lisp вы можете прочитать следующее из спецификации:
7.6.1 Введение в общие функции
Когда
defgeneric
форма оценивается, выполняется одно из трех действий (из-заensure-generic-function
):
- Если универсальная функция с заданным именем уже существует, существующий объект универсальной функции изменяется. Методы, определенные текущим
defgeneric
формы, и любые методы в существующей универсальной функции, которые были определены предыдущимdefgeneric
Форма удаляется. Методы, добавленные текущимdefgeneric
форма может заменить методы, определенныеdefmethod
,defclass
,define-condition
, или жеdefstruct
, Никакие другие методы в универсальной функции не затрагиваются и не заменяются.- Если заданное имя называет обычную функцию, макрос или специальный оператор, выдается ошибка.
- В противном случае создается универсальная функция с методами, указанными в определениях методов в
defgeneric
форма.
При оценке формы, определяющей метод, создается объект метода и выполняется одно из четырех действий:
- Если универсальная функция с заданным именем уже существует и если уже существует объект метода, который согласуется с новым в специализаторах и квалификаторах параметров, новый объект метода заменяет старый. Для определения одного метода, согласующегося с другим в отношении специализаторов и классификаторов параметров, см. Раздел 7.6.3 (Соглашение о специализаторах и классификаторах параметров).
- Если универсальная функция с заданным именем уже существует и если нет объекта метода, который согласуется с новым в специализаторах и квалификаторах параметров, существующий универсальный объект функции модифицируется, чтобы содержать новый объект метода.
- Если заданное имя называет обычную функцию, макрос или специальный оператор, выдается ошибка.
- В противном случае создается универсальная функция с методом, указанным в форме определения метода.
Определение ensure-generic-function
:
Если имя-функции указывает универсальную функцию, которая имеет другое значение для
:lambda-list
аргумент, и новое значение совпадает с лямбда-списками всех существующих методов или нет методов, значение изменяется; в противном случае сообщается об ошибке.Если имя-функции указывает универсальную функцию, которая имеет другое значение для
:generic-function-class
аргумент, и если новый класс универсальной функции совместим со старым,change-class
вызывается для изменения класса универсальной функции; в противном случае сообщается об ошибке.Если имя-функции указывает универсальную функцию, которая имеет другое значение для
:method-class
аргумент, значение изменяется, но любые существующие методы не изменяются.
У вас также есть add-method
а также remove-method
,
Как видите, общие функции сохраняют свою идентификацию между defmethod
определения, и даже между defgeneric
определения. Общие функции являются изменчивыми в Common Lisp.
В Julia вы можете прочитать следующее из документации:
Чтобы определить функцию с несколькими методами, достаточно просто определить функцию несколько раз с разными номерами и типами аргументов. Первое определение метода для функции создает объект функции, а последующие определения метода добавляют новые методы к существующему объекту функции.
Как видите, объекты функций изменчивы в Юлии.
Это ничего не говорит обо всех других языках множественной диспетчеризации. Вы можете изобрести язык множественной диспетчеризации прямо сейчас, просто чтобы показать, что вы можете делать это с неизменяемостью, например, добавление методов вернет новую функцию, аналогичную предыдущей функции, но с добавленным методом. Или язык, в котором функции генерируются статически во время компиляции, так что вы не можете изменить его во время выполнения, даже не добавлять и не удалять методы.
Динамичность может быть реальным активом в вашем приложении, если только для отладки. Попытка предотвратить последующее обновление, переопределение и т. Д. Функции может быть несколько недальновидной. Но если вы уверены, что хотите статическую диспетчеризацию, вы можете определить свой собственный класс универсальных функций благодаря MOP, Meta-Object Protocol, который не является частью стандарта, но все еще широко поддерживается. Это то, что обеспечивает библиотека Inlined-Generic-Function (и это возможно, потому что CLOS открыт для расширений).
Перефразируя превосходную книгу " Начало работы с Джулией", в которой есть хороший раздел об этом (выделено мной):
Мы уже видели, что функции по определению определены как общие, то есть они могут использоваться для различных типов своих аргументов. Компилятор будет генерировать отдельную версию функции каждый раз, когда она вызывается с аргументами нового типа. Конкретная версия функции для определенной комбинации типов аргументов называется методом в Julia. Чтобы определить новый метод для функции (также называемой перегрузкой), просто используйте то же имя функции, но другую сигнатуру, то есть с разными типами аргументов.
Список всех методов хранится в таблице виртуальных методов ( vtable) в самой функции; методы не принадлежат конкретному типу. Когда функция вызывается, Джулия во время выполнения выполнит поиск в этой vtable, чтобы определить, какой конкретный метод следует вызывать, основываясь на типах всех ее аргументов; это механизм множественной диспетчеризации Джулии, который не реализует ни Python, ни C++, ни Fortran. Это позволяет открывать расширения, где обычный объектно-ориентированный код заставил бы вас изменить класс или создать подкласс существующего класса и таким образом изменить вашу библиотеку. Обратите внимание, что для множественной отправки учитываются только позиционные аргументы, а не ключевые аргументы.
Для каждого из этих различных методов генерируется специализированный низкоуровневый код, ориентированный на набор команд процессора. В отличие от объектно-ориентированных (ОО) языков, vtable хранится в функции, а не в типе (или классе). В ОО-языках метод вызывается для одного объекта object.method(), который обычно называется единой диспетчеризацией. В Юлии можно сказать, что функция принадлежит нескольким типам или что функция специализирована или перегружена для разных типов. Способность Джулии компилировать код, который читает как высокоуровневый динамический язык, в машинный код, который почти полностью работает как C, определяется его способностью выполнять многократную диспетчеризацию.
Итак, я понимаю это (я могу ошибаться) так:
- Универсальная функция должна быть определена в сеансе, прежде чем вы сможете ее использовать
- Явно определенные методы для конкретных аргументов добавляются в таблицу поиска множественной отправки функции в том месте, где они определены.
- Всякий раз, когда функция вызывается с конкретными аргументами, для которых явно определенный метод не существует, конкретная версия для этих аргументов компилируется и добавляется в vtable. (однако, это не проявляется как явный метод, если вы запускаете
methods()
по названию этой функции) - Первый вызов такой функции приведет к некоторым издержкам компиляции; однако последующие вызовы будут использовать существующую скомпилированную версию *.
Я бы не сказал, что это делает функции изменяемыми, хотя это совершенно иная проблема. Вы можете подтвердить, что они неизменны, используя isimmutable()
функция на функцию "ручка".
* Я знаю, что модули могут быть предварительно скомпилированы, но я не совсем уверен, что эти скомпилированные на лету версии сохраняются между сессиями в любой форме - комментарии приветствуются:)
В Dylan методы обычно добавляются к универсальной функции во время компиляции, но они также могут быть добавлены во время выполнения (через метод add или метод remove). Однако универсальная функция может быть закрыта, что не позволяет библиотекам, отличным от той, в которой определен gf, добавлять методы. Поэтому, чтобы ответить на ваш вопрос, в Dylan универсальные функции всегда изменяемы в пределах определяющей библиотеки, но они могут быть сделаны неизменяемыми для других библиотек.