在一个名称空间下分组多个名称空间

时间:2018-04-15 07:47:52

标签: clojure

假设我有名称空间foo.car.components.enginefoo.car.components.transmissionfoo.car.components.brakes

foo.car.components.engine中有(defn engine [] ...)foo.car.components.transmission(defn transmission [] ...)foo.car.components.brakes(defn brakes [] ...)

我想在foo.car.components中提供这些内容,以便其他名称空间只需要foo.car.components才能使用enginetransmissionbrakes

以下是有效的,但我想知道是否有更清洁的方法可以做到这一点,或者它是不是很好的做法。

(ns foo.car.components
  (:require
   [foo.car.components.engine :as engine]
   [foo.car.components.transmission :as transmission]
   [foo.car.components.brakes :as brakes]))

(def engine engine/engine)
(def transmission transmission/transmission)
(def brakes brakes/brakes)

4 个答案:

答案 0 :(得分:1)

我并不知道更好的方法,但这种方式 会带来缺点,所以在决定是否要使用时要考虑这些:

  • 它不会将元信息传输到“包装器”,因此当您使用包装器时,附加到主函数的任何文档字符串/其他信息都不会显示在IDE中。

  • 同样,因为包装器没有参数列表,如果你用ctrl + q包装函数,它也不会显示主函数的可用参数列表。

话虽如此,Seesaw,一个包裹Swing的主要Clojure库does use this "technique"。如果我忘记了具有“便利包装”的函数的docs / arguments,我只需要按两次ctrl + b(在IntelliJ中),它会将我带到原始源代码,我可以查看它。具有讽刺意味的是不方便,但我猜这是其他地方方便的价格。

要解决这些错误,您可以编写一个传递元信息的函数(或包含def的宏)。考虑参数列表信息存储为元信息,这可能足以克服故障。

这个答案并没有真正回答你的问题,所以我希望其他人能够在这里提供一些见解。我认为这是相关信息。

答案 1 :(得分:0)

您可以使用:

import-vars

来自https://github.com/ztellman/potemkin

答案 2 :(得分:0)

Clojure API似乎没有提供此功能。您可以考虑https://github.com/ptaoussanis/encore寻找 defalias 。 另一方面,如果您将制动器,变速器和引擎作为公共接口,您可以单独使用它们,为什么要将它们合并?相反,您可以提供组件甚至汽车中的所有定义,这些定义将依次用于制动器,变速器和发动机。这样,就没有必要公开所有组件。

答案 3 :(得分:0)

我认为你这样做的方式是最好的方式,因为它明确了每个命名空间中defs的来源。如果您有大量具有大量功能的命名空间,您可以编写一个基本的帮助函数来为您执行此操作:

(ns foo.utils)
(defn export-refs
  [target-ns source-namespaces]
  (doseq [ns source-namespaces
          [sym f] (ns-interns ns)
          :let [existing (get (ns-interns target-ns) sym)]]
    (when (and existing (not= (var-get existing) f))
      (throw (Exception.
               (format (str "Cannot refer to symbol %s in %s from %s, because that symbol "
                            "already exists in the target namespace")
                       sym (ns-name ns) (ns-name target-ns)))))
    (intern target-ns sym f)))

(ns foo.car.components.engine)
(defn engine [] (println "engine"))

(ns foo.car.components.transmission)
(defn transmission [] (println "transmission"))

(ns foo.car.components.brakes)
(defn brakes [] (println "brakes"))

(ns foo.car.components
  (:require [foo.utils :refer [export-refs]]))
(export-refs 'foo.car.components '[foo.car.components.engine
                                   foo.car.components.transmission
                                   foo.car.components.brakes])

(ns user
  (:require [foo.car.components :refer [engine transmission brakes]]))
(engine) ;; Prints "engine"
(transmission) ;; Prints "transmission"
(brakes) ;; Prints "brakes"

是否"良好做法"或不是取决于你。显然,它具有将代码拆分为具有特定功能的较小文件的优点,同时允许仅导入单个命名空间。缺点是函数的来源有一些间接性,这将使查找函数源更加困难,并且名称冲突的风险更大。