Hamza Yerlikaya
Hamza Yerlikaya

Reputation: 49329

Avoid overriding variable names

On a particular namespace I am working on I am beginning to run out of function names. Is there a way to get a warning like the one I get if I override a symbol from another namespace if I reuse a symbol which is already bound to a function in the same namespace?

Upvotes: 5

Views: 611

Answers (3)

Michał Marczyk
Michał Marczyk

Reputation: 84341

If this is enough of a problem that you'd be willing to replace a (set of) core macro(s), you could try this approach:

(ns huge.core
  (:refer-clojure :exclude [defn]))

(defmacro defn [name & defn-tail]
  (assert (nil? (resolve name))
          (str "Attempting to redefine already defined Var "
               "#'" (.name *ns*) "/" name))
  `(clojure.core/defn ~name ~@defn-tail))

Then any attempt to redefine an existing Var with defn will fail:

user=> (defn foo [] :foo)
#'user/foo
user=> (defn foo [] :bar)
AssertionError Assert failed: Attempting to redefine already defined Var #'user/foo
(nil? (resolve name))  user/defn (NO_SOURCE_FILE:2)

You could similarly replace defmacro; in that case you would have to call clojure.core/defmacro when defining your own variant.

Plain, unadorned def is a special form and receives magic treatment from the compiler, so you could still overwrite existing Vars with it. If you would like to guard against name clashes on that flank too, you could switch to something like defvar (used to be available in clojure.contrib.def) with a similar custom assert.

Upvotes: 4

amalloy
amalloy

Reputation: 91962

Even if you restrict yourself to single-character function names, you are in no danger of running out, as there are (about) 64 thousand Unicode characters, any one of which is a valid function name.

Given that you can in fact have names that are ten thousand characters long, you are on even safer ground.

Upvotes: 1

Matt Furden
Matt Furden

Reputation: 29

This isn't quite an answer to your question but may help avoid the issue depending on how the functions in your namespace are being used. You could make them into local functions using letfn, allowing you to reuse names for functions that are only used within the context of another function.

(defn main-fn [x]
  (letfn [(secondary-fn [x] (* x x))
          (another-fn [x] (secondary-fn (inc x)))]
    (/ (another-fn x) 4)))

Upvotes: 2

Related Questions