Как установить параметры конфигурации в библиотеке clojure?

Я пишу библиотеку Clojure, и мне интересно, как лучше всего устанавливать параметры конфигурации библиотеки.

Многие библиотеки (например, в clojure-contrib) используют параметр глобального уровня, такой как *buffer-size*, который пользователь может установить, вызвав для них set!. Но это не кажется мне лучшим способом, поскольку он создает глобальное состояние и есть вероятность конфликта имен.

Другой способ — передавать параметры при каждом вызове функции, которая от них зависит. Если параметров много, то можно использовать их карту вместо передачи отдельных.

В качестве примера предположим, что я пишу кэш-библиотеку.

Используя первый подход, у меня есть глобальные параметры, такие как *cache-size*, *expiry-time*, *cache-dir* и т. Д. Пользователь set! использует их (или нет, и пусть они будут по умолчанию) и вызывает такие функции, как (set-in-cache id obj) и (get-from-cache id).

Используя второй подход, пользователь сначала создает карту параметров и передает ее каждому вызову.

(def cache-parameters {:cache-size 1000 
                       :expiry-time: 1440 
                       :cache-dir "c:\\cache"})
(set-in-cache cache-parameters id obj)
(get-from-cache cache-parameters id)

Итак, какой способ предпочтительнее в Clojure и почему?


person Abhinav Sarkar    schedule 23.07.2010    source источник


Ответы (1)


На самом деле вы не можете set! такие вещи, как c.c.io в *buffer-size*, если вы не установите для них локальную привязку потока с помощью binding, with-bindings и т. д. Существует всего несколько переменных, для которых локальные привязки потока устанавливаются механизмами Clojure более низкого уровня, такими как *warn-on-reflection* и *read-eval*, сделать их set! способными на верхнем уровне; пользовательские переменные не set!-способны на верхнем уровне. Корневая привязка Var может быть изменена, например. alter-var-root, intern, def, .bindRoot ..., но их следует использовать с осторожностью.

Что касается части вопроса о перепривязке Vars и явных параметров: использование явных параметров почти всегда допустимо и обычно предпочтительнее просто из-за повышенной ремонтопригодности функций, которые четко отображают все части данных, от которых они зависят. При этом, если какая-то часть конфигурации, вероятно, будет установлена ​​​​один раз, а затем будет использоваться практически каждым вызовом функции в приложении / библиотеке после этого, это может привести к тому, что более разумный код будет определять Var с наушниками, хорошо документировать его и помещать конфигурацию в нем (и это может быть одним из редких случаев, когда изменение корневой привязки Var за пределами формы, которая определяет, может быть в порядке).

Подводя итог, используйте свое лучшее суждение, если вы не уверены - ошибитесь в сторону явной передачи параметров.

person Michał Marczyk    schedule 23.07.2010
comment
Последний бит — это точно то, что я сделал в clj-github и gotmilk. Удивительно знать, что я, возможно, сделал что-то правильно! - person Rayne; 23.07.2010
comment
it might make for saner code to define an earmuffed Var ... Можете ли вы привести пример? - person Abhinav Sarkar; 23.07.2010
comment
@Rayne: ;-) @abhin4v: На самом деле *buffer-size*, о котором вы упоминаете в вопросе, является хорошим примером использования Var для хранения разумного значения по умолчанию, которое иногда может быть изменено клиентским кодом. Что касается перепривязки корневого значения Var, Congomongo (библиотека Clojure MongoDB) использует alter-var-root для Var с именем *mongo-config* (чье назначение соответствует названию). - person Michał Marczyk; 24.07.2010
comment
Спасибо. Я решил свою проблему, используя resolve. Пользователь библиотеки должен будет def указать конкретную переменную, а моя библиотека просто resolve ее. - person Abhinav Sarkar; 24.07.2010
comment
@AbhinavSarkar Никогда не видел, чтобы библиотека использовала это. Это сработало для вас? Как вы справлялись с проблемами пространства имен и решали их? - person Didier A.; 28.11.2015