Использование протокола clojure в нескольких пространствах имен

#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