在Clojure中如何覆盖'require'函数?

5

是否有可能覆盖“require”命令,以便在本地机器上找不到某个资源时尝试下载该资源。例如:

(require 'examples.introduction) 
; if not found => download from the net
;   (url: http://media.pragprog.com/titles/shcloj/code/examples/introduction.clj)
2个回答

4
您可以重写require函数,当然,如果要求的命名空间在类路径上不可用,则覆盖的变体可以下载内容。目前似乎无法重写(ns ...)表单中:require的工作方式,因为ns的处理方式。请注意,如果您想将新路径(包括新的jar)放置在类路径上,则这种“下载require”并不是非常有用,因为类路径注入在Clojure中不可靠(由于JVM问题)。虽然有clojure.core/add-classpath,但它自始至终被标记为已弃用,强烈不建议使用,不能保证它会对您起作用,而且这种情况很可能不会很快改变。另一方面,如果您想将新源文件放在已经存在于类路径上的目录中,那应该可以正常工作。如果您确实想尝试覆盖require,并且您有一个foo命名空间,那么您可以执行以下操作:
(ns foo
  (:refer-clojure :exclude [require])
  ; other stuff; any :requires here will work as usual!
  )

然后,根据需要使用clojure.core/require定义自己的require:

(defn require [ns-symbol]
  (do-stuff-to-obtain-the-namespace))

clojure.contrib.find-namespaces 命名空间可能有助于查找类路径上可用的内容。(或者您可以使用 the-ns 函数,在通过 clojure.core/require 要求命名空间的初始尝试之后,看看它是否会抛出异常。)

请注意,您可能首先想到的 binding 方法 ((binding [require ...] ...)) 不起作用,因为 require 通常解析为在 clojure.core 命名空间中内部化的 Var,以及名称以 clojure 开头的命名空间的 Vars 当前由编译器直接链接(意味着在运行时不执行任何实际的 Var 查找,因此对这些 Vars 的重新绑定对代码没有影响)。

ns 表单中的 (:refer-clojure :exclude [require]) 防止 require 解析为 clojure.core/require,并使您可以在自己的命名空间中定义一个该名称的 Var。如上所述,如果您键入完全限定符号,则仍然可以访问 clojure.core/require Var。


有趣。我在想,使用一个不同的函数名称,比如“enhanced-require”,可能会更好,以避免使用命名空间的解决方法。(我是个彻头彻尾的新手,如果我说了些无意义的话,请纠正我。:)) - StackedCrooked
相反,这似乎是更好的方法。不过,实际上最好的方法是让Leiningen / Maven获取任何库依赖项,对于像随书源代码这样的东西,只需手动下载即可... :-) 这并不会削弱编写一个自己完成此操作的时髦require的乐趣! - Michał Marczyk

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接