Почему Clojure лучше поддерживает горячую замену, чем другие языки JVM?

Мы можем почти мгновенно перезагрузить любую функцию и/или переменную в Clojure во время выполнения. Мы даже можем изменить сигнатуры методов. Максимум, что мы можем сделать со Scala или Java, — это использовать JRebel, который является медленным, коммерческим и ограниченным. В чем разница, которая позволяет Clojure быть таким интерактивным? Читая об этом в Slack, я нашел следующие комментарии, но я хотел бы узнать об этом больше. Ссылки на документы/статьи, разъясняющие проблему, также приветствуются (хотя и не обязательны).

Это в основном потому, что язык настроен на перезагрузку. Clojure имеет косвенное указание var для каждой функции или определения переменной верхнего уровня, которое вы можете видоизменять, поэтому вы можете переопределить только одну функцию, сохраняя при этом остальную часть вашей среды прежней, и продолжайте

.

следуя этому - есть косвенность, когда имя функции находится в коде, но для долго работающей функции, которая принимала другую функцию в качестве аргумента (например, вы передали функцию обработчика запуску процесса http-сервера), вы можете получить преимущества var косвенность вручную - путем передачи #'handler вместо обработчика, но в противном случае вы не получите перезагрузку (без перезапуска процесса, который принял этот аргумент)

.

Что-то вроде

прямая компоновка заменяет скомпилированные вызовы var прямыми вызовами (отредактированными), однако путь var все еще существует, и НОВЫЙ код все еще может вызываться через vars


person HappyFace    schedule 20.04.2018    source источник
comment
Похоже, у вас есть ответ на ваш вопрос в цитатах, которые вы разместили. Большинство Лиспов позволяют такое редактирование, насколько мне известно. Вы также можете взглянуть на smalltalk.   -  person nha    schedule 20.04.2018
comment
@nha Мне кажется, я не очень хорошо понимаю комментарии. Что такое var косвенность? Почему бы другим языкам просто не скопировать эту идею? Каковы компромиссы? Почему #'handler отличается от обработчика? Можно ли переопределить скомпилированные методы (например, из основной библиотеки) в пользовательском коде? Есть ли опасения по поводу безопасности? Что такое var пути? ...   -  person HappyFace    schedule 20.04.2018
comment
Прочтите clojure.org/reference/vars swannodette.github.io/2014/12/17/whats-in-a-var blog.cognitect.com/ blog/2016/9/15/ возможно, есть и другие, но они должны дать вам хорошее представление о том, что такое var.   -  person nha    schedule 20.04.2018
comment
JVM больше не поддерживает горячую замену на уровне байтового кода для разных языков. Однако, если язык более высокого уровня, он может внести изменения на уровне языка, которые не выглядят как неподдерживаемые изменения на уровне байт-кода.   -  person Peter Lawrey    schedule 20.04.2018
comment
Кроме того, учитывая семантику clojure, вы перезагружаете обычно чистые функции. Обычно это не так, например. Java, потому что в Java вы будете перезагружать файлы классов, что, вероятно, приведет к хаосу со структурой ваших текущих экземпляров перезагруженного класса.   -  person Jared Smith    schedule 20.04.2018


Ответы (1)


Ключ к тому, о чем вы спрашиваете, заключается в том, как Clojure идентифицирует функции и запускает их во время выполнения. Во-первых, функции Clojure определены как vars, что является именем Clojure для их корневого класса JVM, Var.

Среда выполнения Clojure поддерживает один файл ConcurrentHashMap с именем Namespaces. Эта карта имеет Symbol ключей (имя пространства имен) и Namespace значений. Каждый Namespace, в свою очередь, имеет AtomicReferenced Clojure map (называемый "сопоставлением"), который динамически типизирован, но по существу имеет Clojure Symbol ключи (имя локальной переменной) и Var значения.

Когда вы вызываете функцию Clojure, она сначала ищет, на какое пространство имен вы ссылаетесь в Namespaces, а затем ищет конкретную переменную в сопоставлениях этого пространства имен. Это делает горячую загрузку кода тривиальной — все, что вам нужно сделать, это установить новую пару <Symbol, Var> в отображениях заданного пространства имен.

Чтобы пойти на один уровень глубже, Clojure также поддерживает осведомленность о «фреймах» (то есть потоках или дополнительных привязках, которые могут временно переопределять переменные в локальной области). У них есть собственное хранилище ThreadLocal, и переменная, найденная в одном из них, будет использоваться вместо переменной, которая в настоящее время хранится в сопоставлениях пространства имен.


Подход Clojure здесь возможен, потому что он не пытается хранить функции как функции JVM, а скорее как сами объекты Java, которые хранятся в карте, к которой можно быстро получить доступ.

Clojure знает, что эти объекты на самом деле можно вызывать, проверяя, удовлетворяют ли они интерфейсу функции (IFn). Объект удовлетворяет IFn, имея метод Invoke. Это используется для множества довольно умных целей и объясняет, почему многие основные структуры данных Clojure (карты, векторы, ключевые слова и т. д.) также могут вызываться как функции.

person Venantius    schedule 21.04.2018
comment
Не замедляет ли это вызов функций? - person HappyFace; 23.04.2018
comment
@HappyFace Функции Clojure работают медленнее по сравнению с чистым эквивалентом JVM. Но, в конце концов, медленность является относительной — вещи, из-за которых программа плохо работает под нагрузкой, как правило, определяются алгоритмическими свойствами, которые плохо масштабируются, а не тем фактом, что вызов функции занимает немного больше времени (что только линейный хит производительности). - person Venantius; 23.04.2018
comment
@HappyFace Традиционный совет, когда речь идет о производительности: сначала убедитесь, что у вас есть проблема, прежде чем оптимизировать ее. Когда дело доходит до Clojure, оптимизация может означать преобразование определенных критических путей в чистую Java, но в большинстве случаев оптимизация с более высокой ценностью выполняется только при рефакторинге кода. - person Venantius; 23.04.2018
comment
просто интересно: насколько IFn#invoke медлительность устраняется JIT-компилятором? - person Erik Kaplun; 08.07.2019