Я не мог понять использование функции reify в Clojure. Для чего он используется в Clojure?

Не могли бы вы привести примеры?

33
Ertuğrul Çetin 5 Май 2016 в 21:59

2 ответа

Лучший ответ

Макрос reify позволяет создать анонимный класс, расширяющий класс java.lang.Object и / или реализующий указанные интерфейсы / протоколы. API-документы не описывают четко цель, а скорее предоставляют технические подробности того, что делает этот макрос. Документация по взаимодействию Java содержит краткое описание цели:

Начиная с Clojure 1.2, reify также доступен для реализации интерфейсов.

Еще больше информации можно найти в документах по типам данных n, где вы можете найти очень подробное описание того, что он делает. и как он сравнивается с proxy:

В то время как deftype и defrecord определяют именованные типы, reify определяет как анонимный тип, так и создает экземпляр этого типа. Вариант использования - это когда вам нужна разовая реализация одного или нескольких протоколов или интерфейсов и вы хотите воспользоваться преимуществами локального контекста. В этом отношении это вариант использования, аналогичный прокси или анонимным внутренним классам в Java.

Тела методов reify являются лексическими замыканиями и могут относиться к окружающей локальной области видимости. reify отличается от прокси тем, что:

Поддерживаются только протоколы или интерфейсы, а не конкретный суперкласс. Тела методов - это настоящие методы результирующего класса, а не внешние fns. Вызов методов в экземпляре осуществляется напрямую, без использования поиска по карте. Нет поддержки динамической замены методов в карте методов. В результате производительность выше, чем у прокси, как при построении, так и при вызове. reify предпочтительнее прокси во всех случаях, когда его ограничения не являются запретительными.

22
Piotrek Bzdyl 6 Май 2016 в 05:23

reify для defrecord то же, что fn для defn.
"Ах да ... так что же reify"

Проще говоря, протоколы - это списки функций, которые должен поддерживать тип данных, записи - это типы данных, а реификации - это анонимные типы данных.

Возможно, это многословно, но reify нельзя понять конкретно без понимания протоколов и типов / записей: протоколы - это способ использовать одно и то же имя для такой функции, как conj, которая на самом деле действует по-разному когда заданы разные аргументы ((conj [:b :c] :a) => [:b :c :a], но (conj '(:b :c) :a) => (:a :b :c)). Записи подобны объектам или типам (но они действуют как карты, делая их более привлекательными).

В более фундаментальном плане цель состоит в том, чтобы решить «проблему выражения», то есть получить возможность беспрепятственно добавлять новые типы данных, которые работают с существующими функциями, и новые функции, которые без проблем работают с существующими данными .

Итак, однажды вы говорите себе: «Я, ты должен узнать, что значит быть уткой!» так что вы пишете протокол:

(defprotocol Quacks
  (quack [_] "should say something in ducky fashion"))

Но это все слишком абстрактно, так что вы " реальный , если хотите":

(def donald (reify Quacks
                   (quack [_] "Quacks and says I'm Donald")))

Теперь, наконец, вы можете испытать свое творение:

(quack donald) => "Quacks and says I'm Donald"

Тогда вы вспоминаете о Даффи:

(def daffy (reify Quacks
                  (quack [_] (str "Quacks and says I'm Daffy"))))

(quack daffy) => "Quacks and says I'm Daffy"

Но к тому времени, когда вы вспомните о Хьюи, вы поймете свою ошибку и определите, что такое утка, многоразовым способом:

(defrecord Duck [name]
  Quacks
  (quack [_] (str "Quacks and says I'm " name)))

И сделать new уток (есть несколько способов сделать это):

(def huey (->Duck "Huey"))
(def duey (Duck. "Duey"))
(def louie (new Duck "Louie"))

(quack huey) => "Quacks and says I'm Huey"

Помните, что записи действуют как карты (спасибо протоколам!):

(:name huey) => "Huey"

Но затем вы помните, что утки должны крякать и ходить, поэтому вы пишете другой протокол:

(defprotocol Walks
  (walk [_] "should walk like a duck"))

И расширите определение утки

(extend-type Duck
  Walks
  (walk [_] "waddle waddle"))

(walk louie) => "waddle waddle"

Теперь мы можем расширить другие типы для реализации того же протокола (научить ту же функцию работать с другими вещами):

Допустим, мы хотим, чтобы программисты тоже крякали :-)

(defrecord Programmer [] Quacks
  (quack [_] "Monads are simply monoids in a category of endofunctors..."))

(quack (Programmer.)) => "Monads are simply monoids in a category of endofunctors..."

Я рекомендую это отличное объяснение протоколов, объяснение reify и глава о протоколах в Clojure для храбрых и истинных.

Отказ от ответственности: это сделано только для того, чтобы дать начальное понимание того, что такое протоколы, а не не передовой практики их использования. «Псс! Я ответил на этот вопрос в основном, чтобы научиться, потому что до вчерашнего дня я никогда не писал свой собственный протокол / интерфейс!»

Так что, хотя я надеюсь, что это поможет кому-то другому в обучении, я искренне приветствую критику или предложения редактирования! ».

61
Adam Lee 19 Мар 2018 в 14:48