Как вы организуете имена функций при создании библиотек clojure для общего пользования? - PullRequest
9 голосов
/ 30 июля 2011

Допустим, я хочу создать большую библиотеку clojure с несколькими компонентами.Как разработчик, я хотел бы хранить многие компоненты в отдельных пространствах имен, поскольку многие вспомогательные функции могут иметь похожие имена.Я не хочу делать вещи частными, так как в крайних случаях они могут быть полезны снаружи, а обходной путь за частной не очень хорош.(Другими словами, я хотел бы предложить использование кода, а не полностью предотвратить его использование.)

Однако я бы хотел, чтобы пользователи библиотеки работали в пространстве имен с объединением подмножества многих изфункции в каждой подбиблиотеке.Какой идиоматический или лучший способ сделать это?Одно из решений, которое мне приходит в голову, - написать макрос, который генерирует: требует и создает новое отображение вар, определяя заданный список имен вар (см. Первый пример кода).Есть ли компромиссы с этим методом, например, что происходит с расширяющимися типами?Есть ли лучший способ (или встроенный)?

Пример макроса (src / mylib / public.clj):

 (ns mylib.public
    (:require [mylib.a :as a])
    (:require [mylib.b :as b]))

 (transfer-to-ns [+ a/+
                  - b/-
                  cat b/cat
                  mapper a/mapper])

Опять же, чтобы уточнить, конечной целью будет иметь некоторый файл в других проектах, созданных пользователями mylib длябыть в состоянии сделать что-то вроде (src / someproject / core.clj):

 (ns someproject.core
     (:require [mylib.public :as mylib]))

 (mylib/mapper 'foo 'bar)

@ Джереми Уолл, обратите внимание, что предложенное вами решение не полностью отвечает моим потребностям.Предположим, существует следующий код:

mylib / a.clj:

 (ns mylib.a)

 (defn fa [] :a)

mylib / b.clj:

 (ns mylib.b)

 (defn fb [] :b)

mylib / public.clj:

 (ns mylib.public
     (:use [mylib.a :only [fa]])
     (:use [mylib.b :only [fb]]))

somerandomproject / core.clj: (Предположим, что пути к классам установлены правильно)

 (ns somerandomproject.core
     (:require [mylib.public :as p])

 ;; somerandomproject.core=> (p/fa)
 ;; CompilerException java.lang.RuntimeException: No such var: p/fa, compiling:     (NO_SOURCE_PATH:3) 
 ;; somerandomproject.core=> (mylib.a/fa)
 ;; :a

Если вы заметили, «использование» функций в mylib / public.clj НЕ ДОПУСКАЕТ public.cljПРЕДОСТАВИТЬ эти переменные в пользовательский файл somerandomproject / core.clj.

Ответы [ 2 ]

9 голосов
/ 07 августа 2011

Вам может быть интересно посмотреть, как библиотека типа Compojure или Lamina организует свои "публичные" API-интерфейсы.

У Lamina есть «публичные» пространства имен, такие как lamina.api , которые служат для псевдонимов (используя import-fn Заха из его библиотеки Потемкина) из «внутренних» пространств имен, таких как lamina.core.pipeline. . С небольшим количеством документов, это служит для четкого разграничения публичных ns's от вероятных для изменения внутренних органов. Я обнаружил, что основным недостатком этой стратегии является то, что import-fn значительно усложняет переход (в emacs) от использования функции к ее реализации. Или узнать, какую функцию использовать, например, clojure.repl / source.

Библиотека, подобная Compojure, использует частные переменные для разделения открытых и закрытых частей функций (см., Например, compojure.core ). Основной недостаток «приватных» функций заключается в том, что позже вы можете обнаружить, что хотите их выставить или что это затрудняет тестирование. Если вы контролируете кодовую базу, я не считаю, что частные аспекты имеют большое значение. Проблему тестирования легко обойти, используя # 'foo.core / my-function для ссылки на приватную функцию.

Обычно я склонен использовать что-то более похожее на стиль Compojure, чем стиль Ламины, но звучит так, как будто вы бы предпочли что-то более похожее на Ламину.

2 голосов
/ 30 июля 2011

Я не совсем уверен, что вы спрашиваете здесь. Я думаю, может быть, вы хотите знать, каков наилучший метод для импорта публики из пространства имен для общих служебных функций? В этом случае вам нужна функция реферала: http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/refer

(refer mylib.a :only [+])
(refer mylib.b :only [-])

Импортирует открытые элементы в пространстве имен в текущее пространство имен. Однако предпочтительным методом было бы сделать это в объявлении пространства имен с помощью директивы: use

(ns (:use (mylib.a :only [+])
          (mylib.b :only [-])))
...