在构建公共消费的clojure库时,如何组织函数名称?

时间:2011-07-30 04:13:08

标签: namespaces 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 Wall,请注意您提出的解决方案并未满足我的需求。让我们假设存在以下代码。

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中的“using”函数,则不允许public.clj将这些变量提供给用户文件somerandomproject / core.clj。

2 个答案:

答案 0 :(得分:9)

您可能会发现查看像CompojureLamina这样的图书馆组织其“公共”API的方式很有意思。

Lamina拥有像lamina.api这样的“公共”命名空间,用于从lamina.core.pipeline这样的“内部”命名空间(或使用Zach的potemkin库中的import-fn)别名。通过一些文档,这可以清楚地描述面向公众的ns与可能发生变化的内部结构。我发现这个策略的主要缺点是import-fn使得从使用函数到其实现中走路(在emacs中)变得更加困难。或者要知道使用clojure.repl / source的函数。

像Compojure这样的库使用私有变量来分离函数的公共/私有部分(例如,参见compojure.core)。 “私有”功能的主要缺点是您以后可能会发现要暴露它们或者它使测试更复杂。如果您控制代码库,我发现私有方面并不重要。通过使用#'foo.core / my-function来引用私有函数,可以轻松解决测试问题。

一般来说,我倾向于使用更像Compojure的风格而不是Lamina的东西,但听起来你更喜欢像Lamina这样的东西。

答案 1 :(得分:2)

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

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

它将名称空间中的公共项导入当前名称空间。但是,首选方法是在命名空间声明中使用:use directive

执行此操作
(ns (:use (mylib.a :only [+])
          (mylib.b :only [-])))