我同意允许 ns 别名重新定义(并通过警告来阻止我自损)将是一个很好的增强功能。
如果推荐的增强功能被 Clojure.core 成员接受到 Jira 上,
则附加的补丁会将别名错误降级为警告。
From 906ddea7ba4c051c10e7ab57473e0bdf0b855e02 Mon Sep 17 00:00:00 2001
From: Timothy Pratley <[email protected]>
Date: Mon, 26 Sep 2022 11:36:26 -0700
Subject: [PATCH] [CLJ-pending] allow ns alias redefinition
Loading a namespace at the REPL previously would fail if an alias was
redefined. This change makes redefinition a warning instead of an error.
The intention is to allow users to change their ns definitions and still
be able to reload their code in the REPL. It does mean that people could
erroneously have duplicate aliases defined, but they will receive a
warning in such cases. Additionally users will need to remember to
reload the entire file if they expect the alias change to have any
effect in the file they are working in, or reload the part that they
would like to use the new alias.
Previous discussion here:
https://ask.clojure.org/index.php/12235/can-alias-already-exists-in-namespace-be-fixed
---
src/jvm/clojure/lang/Namespace.java | 11 ++++++-----
test/clojure/test_clojure/repl.clj | 14 +++++++++++---
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/src/jvm/clojure/lang/Namespace.java b/src/jvm/clojure/lang/Namespace.java
index 35981577..19f29c94 100644
--- a/src/jvm/clojure/lang/Namespace.java
+++ b/src/jvm/clojure/lang/Namespace.java
@@ -242,26 +242,27 @@ public Namespace lookupAlias(Symbol alias){
IPersistentMap map = getAliases();
return (Namespace) map.valAt(alias);
}
public void addAlias(Symbol alias, Namespace ns){
if (alias == null || ns == null)
throw new NullPointerException("Expecting Symbol + Namespace");
IPersistentMap map = getAliases();
- while(!map.containsKey(alias))
+ Object found = map.valAt(alias);
+ if (found != null && found != ns)
+ RT.errPrintWriter().println("WARNING: Alias " + alias + " already exists in namespace "
+ + name + ", being replaced by " + ns);
+ while(found != ns)
{
IPersistentMap newMap = map.assoc(alias, ns);
aliases.compareAndSet(map, newMap);
map = getAliases();
+ found = map.valAt(alias);
}
- // you can rebind an alias, but only to the initially-aliased namespace.
- if(!map.valAt(alias).equals(ns))
- throw new IllegalStateException("Alias " + alias + " already exists in namespace "
- + name + ", aliasing " + map.valAt(alias));
}
public void removeAlias(Symbol alias) {
IPersistentMap map = getAliases();
while(map.containsKey(alias))
{
IPersistentMap newMap = map.without(alias);
aliases.compareAndSet(map, newMap);
diff --git a/test/clojure/test_clojure/repl.clj b/test/clojure/test_clojure/repl.clj
index c7a0c41b..6e3efea8 100644
--- a/test/clojure/test_clojure/repl.clj
+++ b/test/clojure/test_clojure/repl.clj
@@ -1,12 +1,12 @@
(ns clojure.test-clojure.repl
(:use clojure.test
clojure.repl
- [clojure.test-helper :only [platform-newlines]]
+ [clojure.test-helper :only [platform-newlines should-print-err-message]]
clojure.test-clojure.repl.example)
(:require [clojure.string :as str]))
(deftest test-doc
(testing "with namespaces"
(is (= "clojure.pprint"
(second (str/split-lines (with-out-str (doc clojure.pprint)))))))
(testing "with special cases"
@@ -42,20 +42,28 @@
(is (= [] (apropos "nothing-has-this-name"))))
(testing "with a symbol"
(is (some #{'clojure.core/defmacro} (apropos 'defmacro)))
(is (some #{'clojure.core/defmacro} (apropos 'efmac)))
(is (= [] (apropos 'nothing-has-this-name)))))
-(defmacro call-ns
+(defmacro call-ns
"Call ns with a unique namespace name. Return the result of calling ns"
[] `(ns a#))
-(defmacro call-ns-sym
+(defmacro call-ns-sym
"Call ns wih a unique namespace name. Return the namespace symbol."
[] `(do (ns a#) 'a#))
(deftest test-dynamic-ns
(testing "a call to ns returns nil"
(is (= nil (call-ns))))
(testing "requiring a dynamically created ns should not throw an exception"
(is (= nil (let [a (call-ns-sym)] (require a))))))
+
+(deftest test-redefine-alias
+ (testing "Alias redefinition is allowed for easier REPL interaction, but raises a warning."
+ (should-print-err-message
+ #"WARNING: Alias set already exists in namespace .*, being replaced by clojure.set\r?\n"
+ (do
+ (require '[clojure.pprint :as set])
+ (require '[clojure.set :as set])))))
--
2.37.3