#clojure #namespaces #protocols
#clojure #пространства имен #протоколы
Вопрос:
В настоящее время я реализую несколько типов с тем же протоколом, что и основа, и хотел бы уменьшить количество файлов, которые нужно было бы включить в пространство имен, чтобы их использовать. Чтобы привести пример:
basetype.clj
(defprotocol Base
(do-something [obj]))
(deftype Basetype [obj]
...
Base
(do-something [obj] something-something))
second_type.clj
(ns project.second-type
(:use project.basetype))
(deftype DifferentType [obj]
...
Base
(do-something [obj] something-slightly-different))
Чего я добился, так это того, что все, созданное для Basetype
, также работает с DifferentType
. Однако я хотел бы получить доступ к функции, do-something
просто включив second_type.clj
в пространство имен вместо включения обоих. Есть ли способ добиться этого?
Комментарии:
1. вы можете сделать следующее для повторного экспорта
do-something
изsecond_type
: в конце seond_type.clj вы(ns-unmap 'project.second-type 'do-something)
и затем(intern 'project.second-type 'do-something a/do-something)
. затем это действие будет отправлено в другое пространство имен, для которого требуется второй тип2. Clojure осуждает даже видимость наследования реализации: потребители работают по протоколам и не заинтересованы в конкретном типе. Они требуют basetype и вызывают простые старые функции, которые defprotocol создал в basetype ns. Существует другая теория, что все это ерунда; вызывающие устройства должны использовать точечную нотацию Java interop (.methodName obj), поэтому им не требуется базовый тип ns. Но это рискованно — вы отказались от FP для OO и потеряли возможность обернуть созданные defprotocol fns своими собственными fns, которые выполняют больше протоколирования или проверки типов.
Ответ №1:
как я упоминал в своем комментарии, вы должны иметь возможность повторно экспортировать любые значения из любого пространства имен.
вы могли бы создать простую функцию, подобную этой:
(defn reexport [from-ns name]
(ns-unmap *ns* name)
(intern *ns* name @(ns-resolve from-ns name)))
(reexport 'project.basetype 'do-something)
или повторно экспортировать все значения из данного ns:
(defn reexport-all [from-ns]
(run! (partial reexport from-ns) (map first (ns-publics from-ns))))
(reexport-all 'project.basetype)
в repl:
user> (ns x)
nil
x> (defn f [v] (inc v))
#'x/f
x> (ns y)
nil
y> (user/reexport 'x 'f)
#'y/f
y> (in-ns 'user)
#namespace[user]
user> (y/f 10)
11
user> (in-ns 'y)
#namespace[y]
y> (defn f2 [v] (dec v))
#'y/f2
y> (ns z)
nil
z> (user/reexport-all 'y)
nil
z> (in-ns 'user)
#namespace[user]
user> (z/f 1)
2
user> (z/f2 1)
0