在他的Clojure Style Guide中,作者写了以下内容,
Prefer using :require :refer :all over :use in ns macro
他没有解释为什么这是一个好主意。在那儿 避免使用的一个很好的理由:在ns宏中使用?
答案 0 :(得分:5)
我建议你看一下导致这条规则的original discussion。可以找到核心理由here。我将在这里讨论讨论的开始:
之前有人讨论过ns的复杂性 宏。特别是:使用导致所有变量被引用 默认情况下被广泛认为是不幸的,与之区别 use和require是新手的概念开销的来源 语言。我们无法改变这样一个事实:使用指的是一切 默认情况下不会破坏大量现有代码。但它会 可以增强:需要支持引用指定的变量, 让我们可以自由地弃用或阻止使用:use。
而不是这种形式:
(ns mork.test (:use [mork.stats :only [summarize-group]] [mork.utils :only [strip resolve-fn]] [clojure.test]) (:require [mork.view :as view] [clojure.java.io :as io] [cheshire.core :as json]) (:import (java.io PushbackReader))
我们可以用这个:
(ns mork.test (:require [mork.stats :refer [summarize-group]] [mork.utils :refer [strip resolve-fn]] [clojure.test :refer :all] [mork.view :as view] [clojure.java.io :as io] [cheshire.core :as json]) (:import (java.io PushbackReader))
在之前的主题中已经同意保持:import as a 明确的概念:要求是可取的,我同意我们 不应该在Clojure变量和Java类之间进行混淆。
在引用命名空间中的所有变量时,极少数情况下 可接受的,(特别是在编写测试命名空间时) 合理地引入所有的clojure.test和命名空间 测试)所以我们应该支持:参考:所有这样的情况,如 只要它不是默认值。
答案 1 :(得分:3)
我的印象是有两个原因。 1)这样做,你更有可能避免任何名称冲突和/或意外的黯然失色。 2)这些实践使代码中使用的函数和变量的最终来源更加透明。第一个非常实用,Chiron的回答提供了很好的演示。
第二个更精细,但如果你使用了很多库,那就非常重要了。假设你这样做:
(ns your-namespace
(:use [library-one]
[library-two]
[library-three]
[library-four]
[library-five]))
然后在某个地方,稍后在您的代码中,您调用:
(important-function some-arg some-other-arg)
如果在important-function
中定义了library-three
,那么您(或任何想要了解您的代码的人)必须深入挖掘每个库的代码以找出它做了什么 - 因为没有什么可以表明哪个库是它的来源!嗯,实际上,如果你有一个可用的REPL,你应该能够做到这一点:
your-namespace> `important-function
> library-three/important-function
但是这种隐藏的信息可能不是对其他人(包括你未来的自己)造成伤害的最好的事情,他们可能希望了解你的代码。另一方面,:require
d库总是带有前缀。相反,您的代码看起来像:
(ns your-namespace
(:require [library-one]
[library-two]
[library-three]
[library-four]
[library-five]))
...
(library-three/important-function some arg some-other-arg)
这清楚地说明了定义important-function
的位置。请注意,这应该等同于指定:refer :all
。虽然:refer :all
可以向其他人表明您特别考虑了哪些变量引用,并且有意识地决定将所有变量都包含在该库中。
如果你不担心名字冲突......你可能应该这么做。但是,如果你仍然没有,并且想要明确vars的来源,那么你总是可以:use
与:only
进行交流。例如:
(ns your-namespace
(:use [library-one :only []]
[library-two :only []]
[library-three :only [important-function]]
[library-four :only []]
[library-five :only []]))
...
(important-function some arg some-other-arg)
显然,如果您:use
库中的空白:only
向量,而不是:use
,那么我只想与前面的示例相结合。执行:require
可能看起来很冗长,但您可以通过使用:as
别名来缩短它。片段示例:
(ns your-namespace
(:require [library-three :as L3]))
(L3/important-function some-arg some-other-arg)
另请参阅有关Clojure中的库和命名空间的this SO问题和this guide。
答案 2 :(得分:1)
因为使用:require
,您将清楚地指示哪个命名空间拥有一个函数。
(:require [clojure.json :as json])
(:require [clojure.xml :as xml])
(json/read "file")
(xml/read "file")
如果是:use
(read "file")
您将如何知道哪些名称空间拥有read
功能?如果我正在维护您的代码,我必须检查ns
宏并开始在每个read
限定名称空间中搜索use
函数。
答案 3 :(得分:1)
关注点分离。 require
允许您加载名称空间,refer
让您定义如何在这些名称空间中引用变量。 use
完成了两个问题。
答案 4 :(得分:0)
使用:require
,您只有一个位置可以定义命名空间导入,您可以轻松地从引用所有函数切换到引用一些函数或只需要命名空间(或者根据需要混合搭配)。
(:require [foo.bar]
[foo.baz :refer :all :as baz]
[foo.foobar :as fb :refer [bazbaz]])