Установка Clojure «константы» во время выполнения - PullRequest
9 голосов
/ 22 октября 2010

У меня есть программа Clojure, которую я создаю как файл JAR, используя Maven. В JAR Manifest встроен номер версии сборки, включая метку времени сборки.

Я легко могу прочитать это во время выполнения из JAR Manifest, используя следующий код:

(defn set-version
  "Set the version variable to the build number."
  []
  (def version
    (-> (str "jar:" (-> my.ns.name (.getProtectionDomain)
                                   (.getCodeSource)
                                   (.getLocation))
                    "!/META-INF/MANIFEST.MF")
      (URL.)
      (.openStream)
      (Manifest.)
      (.. getMainAttributes)
      (.getValue "Build-number"))))

но мне сказали, что это плохая карма - использовать def внутри defn.

Что такое идиоматический способ Clojure для задания константы во время выполнения? Очевидно, у меня нет информации о версии сборки для встраивания в мой код как def, но я бы хотел, чтобы он был установлен один раз (и для всех) из функции main при запуске программы. Затем он должен быть доступен как def для остальной части работающего кода.

ОБНОВЛЕНИЕ : Кстати, Clojure должен быть одним из самых крутых языков, с которыми я сталкивался довольно давно. Слава Ричу Хикки!

Ответы [ 4 ]

7 голосов
/ 24 октября 2010

Я все еще думаю, что самый чистый способ - использовать alter-var-root в методе main вашего приложения.

(declare version)

(defn -main
  [& args]
  (alter-var-root #'version (constantly (-> ...)))
  (do-stuff))

Он объявляет Var во время компиляции, устанавливает его корневое значение во время выполнения один разне требует разыменования и не связан с основным потоком.Вы не ответили на это предложение в своем предыдущем вопросе.Вы пробовали этот подход?

4 голосов
/ 22 октября 2010

Вы можете использовать динамическое связывание.

(declare *version*)

(defn start-my-program []
  (binding [*version* (read-version-from-file)]
    (main))

Теперь main, и каждая функция, которую она вызывает, будет видеть значение *version*.

2 голосов
/ 05 ноября 2010

Хотя решение kotarak работает очень хорошо, есть альтернативный подход: превратить ваш код в запомненную функцию, которая возвращает версию.Вот так:

(def get-version
 (memoize
  (fn []
    (-> (str "jar:" (-> my.ns.name (.getProtectionDomain)
            (.getCodeSource)
            (.getLocation))
         "!/META-INF/MANIFEST.MF")
    (URL.)
    (.openStream)
    (Manifest.)
    (.. getMainAttributes)
    (.getValue "Build-number")))))
1 голос
/ 22 октября 2010

Надеюсь, я не пропущу что-нибудь в этот раз.

Если версия является константой, она будет определена один раз и не будет изменена, вы можете просто удалить defn и оставить (def version ...) в покое. Я полагаю, вы не хотите этого по какой-то причине.

Если вы хотите изменить глобальные переменные как-то, я думаю, что более идиотским способом является использование некоторых конструкций параллелизма для хранения данных и доступа к ним и их безопасного изменения Например:

(def *version* (atom ""))

(defn set-version! [] (swap! *version* ...))
...