peeking at private vars in unit tests

92 views
Skip to first unread message

Stuart Halloway

unread,
Dec 13, 2009, 3:19:26 PM12/13/09
to clo...@googlegroups.com
(1) Is there already a form that does this?

or

(2) If not, how can the following be better/simpler:

(use 'clojure.contrib.with-ns)
(defmacro wall-hack-var
"Return the value of a var in a namespace, even
if it is private. Intended for test automation."
[ns v]
`(with-ns '~ns
(if-let [a-var# (resolve '~v)]
@a-var#
(throw (RuntimeException. (str "No var " '~ns "/" '~v))))))

Stu

Stephen C. Gilardi

unread,
Dec 13, 2009, 3:28:53 PM12/13/09
to clo...@googlegroups.com

On Dec 13, 2009, at 3:19 PM, Stuart Halloway wrote:

> (1) Is there already a form that does this?

Hi Stuart,

I think the trick is resolving manually:

user=> @#'clojure.core/spread
#<core$spread__4510 clojure.core$spread__4510@bb273cc>
user=> (@#'clojure.core/spread [:a :b [:c :d]])
(:a :b :c :d)
user=>

Is that sufficient?

--Steve

Stuart Halloway

unread,
Dec 13, 2009, 6:54:59 PM12/13/09
to clo...@googlegroups.com
Steve,

That's great. I wouldn't have expected it to work. Do you think this
is by design or coincidental/subject to change?

Stu

Stephen C. Gilardi

unread,
Dec 13, 2009, 7:55:51 PM12/13/09
to clo...@googlegroups.com

On Dec 13, 2009, at 6:54 PM, Stuart Halloway wrote:

> That's great. I wouldn't have expected it to work. Do you think this is by design or coincidental/subject to change?

The expression I gave was:

@#'clojure.core/spread

equivalent to:

(deref (var clojure.core/spread))

I see now that the simpler expression:

#'clojure.core/spread

equivalent to

(var clojure.core/spread)

also works:

user=> (#'clojure.core/spread [:a [:b :c]])
(:a :b :c)

I think it's by design that privacy is enforced during symbol resolution but not during var lookup, var deref, or var invoke. It would be (somewhat) complicated and slow for any of the latter 3 to allow using the var from within its home namespace but not from within another namespace.

However, I'm not aware of this behavior being explicitly documented so if the code relying on it must be future-proof, I recommend checking with Rich.

--Steve

Chouser

unread,
Dec 14, 2009, 1:16:49 AM12/14/09
to clo...@googlegroups.com
I'd like a ruling on this as well. It solves a real problem of
macros getting access to helper functions, so if we shouldn't be
relying on it, a reliable solution would be desirable.

This technique is actually used in the "new" branch in
core_deftype.clj so that the 'defprotocol' macro can expand into
a usage of a private fn 'assert-same-protocol'. But that doesn't
necessarily prove it's an acceptible solution.

--Chouser

Stuart Halloway

unread,
Dec 14, 2009, 8:18:55 AM12/14/09
to clo...@googlegroups.com
> I see now that the simpler expression:
>
> #'clojure.core/spread
>
> equivalent to
>
> (var clojure.core/spread)
>
> also works:
>
> user=> (#'clojure.core/spread [:a [:b :c]])
> (:a :b :c)

This works for refs but not for atoms. :-/

Stu

Rich Hickey

unread,
Dec 14, 2009, 8:59:31 AM12/14/09
to clo...@googlegroups.com
I'm fine with continuing to allow (var fully.qualified/foo) to work
for private vars in other namespaces. That and #'fully.qualified/foo
are sufficiently verbose IMO, as the 'private' system is just there to
prevent accident, and the leaking of implementation names, not malice.

Note that emitting a var in a macro is fine, but please don't emit a
dereferenced defn var in a macro (via @) as Steve first showed, as
that will actually embed the function object in your code, which is
not always possible, nor desirable.

Rich

Meikel Brandmeyer

unread,
Dec 14, 2009, 4:15:35 AM12/14/09
to clo...@googlegroups.com
Hi,

Am 14.12.2009 um 07:16 schrieb Chouser:

> I'd like a ruling on this as well. It solves a real problem of
> macros getting access to helper functions, so if we shouldn't be
> relying on it, a reliable solution would be desirable.
>
> This technique is actually used in the "new" branch in
> core_deftype.clj so that the 'defprotocol' macro can expand into
> a usage of a private fn 'assert-same-protocol'. But that doesn't
> necessarily prove it's an acceptible solution.

Why don't we get rid of private Vars anyway? There is always the possibility of a foo.internal namespace containing "private" (but technically public) Vars, which can be accessed easily from foo (via :use). Then also macros don't need voodoo dolls to access them. syntax-quote just resolves them correctly.

Sincerely
Meikel

Rich Hickey

unread,
Dec 14, 2009, 9:00:40 AM12/14/09
to clo...@googlegroups.com
What situation are you talking about specifically?

Rich
Reply all
Reply to author
Forward
0 new messages