2011-07-30 16 views
9

假设我想要构建一个包含多个组件的大型clojure库。作为一名开发人员,我想将许多组件保存在不同的名称空间中,因为许多助手函数可能具有相似的名称。我不一定要把事情私人化,因为他们在极端情况下可能具有外部效用,并且隐私背后的解决方案并不好。 (换句话说,我想建议代码的使用,而不是完全阻止使用。)如何在构建公共消费的clojure库时组织函数名称?

但是,我希望库的用户在一个命名空间中操作的联合的许多功能的子集每个子库。什么是地道或最好的方式来做到这一点?我想到的一个解决方案是编写一个生成以下代码的宏:通过定义给定的var名称列表来请求并创建一个新的var映射(请参阅第一个代码示例)。这种方法是否存在折衷,如扩展类型会发生什么?有没有更好的方法(或内置)?

宏示例中(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) 

@Jeremy墙,请注意您提出的解决方案并不fullfill我的需要。让我们假设存在以下代码。

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。

回答

9

你会觉得很有趣看像CompojureLamina库如何组织其“公共”的API。

椎板有像lamina.api“公共”的命名空间用来别名(使用扎克进口-FN从他的波将金号库)从像lamina.core.pipeline“内部”的命名空间功能。通过一些文档,这可以清楚地描述面向公众的ns从可能改变的内部。我发现这个策略的一个主要缺点是import-fn使得从函数的使用到执行(emacs)非常困难。或者例如,要知道使用clojure.repl/source的函数。

像Compojure这样的图书馆使用私人变量来分隔公共/私人部分的功能(例如参见compojure.core)。 “私人”功能的主要缺点是您稍后可能会发现您想要公开它们或使测试更加复杂。如果你控制了代码库,我不认为私有方面是一个大问题。测试问题很容易通过使用#'foo.core/my-function来引用私有函数。

一般来说,我倾向于使用比拉米娜更像Compojure的风格的东西,但它听起来像你更喜欢更像拉米娜的东西。

+0

好的答案。我曾尝试过compojure.core风格,但遇到了你提到的限制,所以我一直在寻找潜在的替代品,我会在接受之前调查叶片并了解它是如何工作的 – bmillare

+0

Cascalog按照类似的方式组织但不太严格 - 您需要的许多主要内容是在cascalog.api ns中或者是别名,但您也可以访问底层ns的更高级功能。 –

2

我不完全确定你在这里问什么。我想也许你想知道从共享实用程序函数的命名空间导入公共的最佳做法是什么?在这种情况下,参考功能是你在找什么,我认为:http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/refer

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

据进口大众项目的命名空间到当前的命名空间。然而,首选方法是用一个做到这一点在你的命名空间声明:使用指令

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

这个想法是为用户建立一个命名空间来要求。这不是为了开发这个clj文件,而是在他们需要或使用你创建的文件的地方开发其他clj文件。所以在我的例子中,你会在ns形式中,有类似于(:require [mylib.public:as mylib] – bmillare

+0

你提出的解决方案不满足我的需求,请参阅澄清 – bmillare

相关问题