为何clojure.set/union函数允许非集合类型数据作为输入?
Great question! This is such a common gotcha when you're first exploring Clojure's set utilities—let's unpack what's happening here.
Why No Type Error?
Clojure leans heavily into duck typing: instead of enforcing strict type checks upfront, it cares whether an object supports the operations it needs (like count or conj in this case). The clojure.set/union documentation states it expects input sets, but the implementation doesn't explicitly validate that you're passing sets. Since lists support both count and conj, the function just proceeds with whatever you give it.
Why That Specific Output?
Let's look at how clojure.set/union works under the hood for two arguments:
([s1 s2] (if (< (count s1) (count s2)) (reduce conj s2 s1) (reduce conj s1 s2)))
In your example, you're passing two lists: '(1 2 3) and '(2 3 4). Both have a count of 3, so it runs (reduce conj '(1 2 3) '(2 3 4)).
Here's the step-by-step breakdown:
reduceuses theconjfunction, starting with the initial list'(1 2 3), and processes each element from the second list:conj '(1 2 3) 2→(2 1 2 3)(sinceconjadds elements to the front of a list)conj (2 1 2 3) 3→(3 2 1 2 3)conj (3 2 1 2 3) 4→(4 3 2 1 2 3)
That's exactly the output you saw! Since you're using lists instead of sets, conj doesn't handle deduplication—sets automatically ignore duplicate elements when you conj them, but lists don't.
The Correct Usage (For Union Semantics)
To get the actual set union behavior you expect, make sure to pass sets instead of lists:
user=> (clojure.set/union #{1 2 3} #{2 3 4}) #{1 2 3 4}
If you're working with sequences and want to treat them as sets, convert them first:
user=> (clojure.set/union (set '(1 2 3)) (set '(2 3 4))) #{1 2 3 4}
Enforcing Input Types (Optional)
If you want to avoid this kind of surprise in your code, add explicit checks to ensure inputs are sets. For example:
(defn safe-union [& colls] (assert (every? set? colls) "All inputs must be sets") (apply clojure.set/union colls))
Now calling (safe-union '(1 2 3) '(2 3 4)) will throw an assertion error, matching your initial expectation of a type warning.
内容的提问来源于stack exchange,提问作者Old Panda

