переопределение функций в clojure - PullRequest
4 голосов
/ 05 января 2012

Я хотел бы переопределить некоторые функции в моей программе при запуске в соответствии с некоторыми метаданными этих функций.

Я новичок в clojure, поэтому я хотел бы узнать, каков идиоматический способ сделать это.

Что я хотел бы сделать, так это использовать кеш (например, memcache) для кеширования результатов некоторых функций (результатов базы данных). Подобным образом, как memoize или contrib core.cache, но я бы хотел прозрачно переопределить оригинальные функции для остальной части программы в соответствии с метаданными, которые определяют политику кэширования.

Библиотеки Java обычно используют аннотации и генерацию кода для достижения этой цели. Но я забочусь о том, что такое идиоматический способ решения этой проблемы?

Я изучил несколько вариантов в Интернете, но они не кажутся слишком удовлетворительными. Связывание не то, что я хочу, потому что оно работает только в текущем потоке. Другие варианты, кажется, используют некоторые внутренние функции Java, которые я хотел бы избежать, или связывание ns и переопределение функций с помощью eval.

Я понимаю, что могу перечислить потенциальные функции в одном пространстве имен с помощью (keys (ns-publics 'foo)), но еще не изучил, как перечислять непубличные функции и как перечислять доступные пространства имен (загруженные в настоящее время?) - может быть есть хук загрузки пространства имен, который я могу использовать?

EDIT: Это небольшой пример того, что я имею в виду. Wrap - это функция, которая выполняет кэширование в соответствии с метаданными origs. Кэширование и метаданные отсутствуют в примере, и wrap, и orig находятся в одном и том же пространстве имен.

(defn orig []
    "OK")
(defn orig2 []
    "RES 2")

(defn wrap [f & args]
    (let [res (apply f args)]
        println "wrap" f args "=" res
        res))

(set! orig (wrap orig))
(set! orig2 (wrap orig2))

После оценки последних двух форм orig и orig2 должны быть переопределены для использования упакованных версий. К сожалению, я получаю следующую ошибку в REPL:

java.lang.IllegalStateException: Невозможно изменить / установить корневую привязку: orig с набором (NO_SOURCE_FILE: 0)

Ответы [ 3 ]

8 голосов
/ 05 января 2012

Вы можете снова использовать def/defn, и это изменит определение функции (технически оно напишет новое определение, которое использует то же имя).

Так что вы можете просто сделать:

(def orig (wrap orig))
(def orig2 (wrap orig2))

Кроме того, если я понимаю ваше намерение: wrap должен возвращать функцию, а не результат.

(defn wrap [f]
  (fn [& args]
    (let [res (apply f args)]
      (println "wrap" f args "=" res)
      res)))

Если вы посмотрите на стандартную функцию memoize, она работает именно так.

4 голосов
/ 05 января 2012

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

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

(defn cache-all ...)
(defn cache-some ...)
(defn cache-none ...)

 (let [policy (get-current-policy)]
     (cond (= policy :all) (def cacher cache-all)
           (= policy :some) (def cacher cache-some)
           ...
           ))

если вам действительно нужно определить функцию на основе нового ввода, тогда eval - это идиоматический подход.

1 голос
/ 05 января 2012

Просто передайте соответствующую функцию в качестве переменной в код, который выполняет работу.

, например

user=> (defn strategy-1 [input] (+ input 3))
#'user/strategy-1
user=> (defn strategy-2 [input] (- input 3))
#'user/strategy-2
user=> (defn fun-user [fn input] (fn input))
#'user/fun-user
user=> (fun-user strategy-1 5)  
8
user=> (fun-user strategy-2 5)
2

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...