"fixing" last-var-wins

4 weergaven
Naar het eerste ongelezen bericht

Chas Emerick

ongelezen,
13 mei 2010, 11:04:2913-05-2010
aan cloju...@googlegroups.com
There's already been a fair bit of discussion about the new last-var-
wins behaviour in various places, but I wanted to raise it as a topic
here in a (last minute?) effort to get it eliminated or significantly
modified for 1.2.

For those who don't know what last-var-wins is, Stuart Halloway posted
this on the main list introducing the change:

http://groups.google.com/group/clojure/browse_frm/thread/2c7de76c3561bdf0#

== Problems ==

Various problems (or, perhaps specifically, increased complexity) have
been associated with last-var-wins:

1. The order of :use clauses is now significant: http://twitter.com/cgrand/status/13548877336

2. It is now remarkably easy to get into situations where whether
locally-defined or referred functions are being used is ambiguous.
Laurent Petit illustrated this here (http://groups.google.com/group/clojure/msg/fe4faddb4a5dc7d5
). Understanding what's going on here is simple in a REPL setting,
but consider a namespace where a var that shadows a core (or other
referred namespace) var is defined ahead of usage of that var. The
resulting breakage may or may not be obvious or immediate. Yes, there
are warnings, but that's....unsatisfying? :-) In cases like this, an
error is vastly preferable to a warning IMO.

3. It's also now far, far too easy to clobber data in a REPL.
Consider a REPL session that has gotten into a state where var 'foo'
contains some data structure that you've successfully massaged into a
particular state...and then you :use a namespace that contains a
public var 'foo'. Not happy-making.

4. The latest issue I stumbled across is an ns I have that had defined
a munge multimethod; there had been a (:refer-clojure :exclude
[munge]) clause in the ns declaration to avoid the core munge fn. On
a hunch, I removed the exclusion so that last-var-wins semantics
applied, and got an odd/fun error when loading the compiled ns [1].
You can try yourself with these defs:

(defmulti munge (fn [a & _] a))
(defmethod munge String [s])

I'm sure this last one can be "fixed" with some nip and tucks, but the
broader point is that last-var-wins seems to be a case where the
medicine is worse than the disease (i.e. the pain associated with
upgrading to clojure 1.2 [and later] when libraries define newly-
conflicting vars).

== Possible Solutions ==

My initial reaction to last-var-wins <http://groups.google.com/group/clojure/msg/86384d7e6555c024
> suggested a different path (roughly, speculative loading of
namespaces and definition of vars), though I was presuming that the
change suggested there was too complicated to tackle for 1.2.

However, Chris Houser, Christophe Grand, and I had a lengthy
discussion in #clojure last week on this topic <http://clojure-log.n01se.net/date/2010-05-07.html#09:47
>, where some options were discussed. Chris indicated that a "local-
var-wins" approach is at least plausible <http://clojure-log.n01se.net/date/2010-05-07.html#10:23a
> -- and though I'm a noob when it comes to understanding the
implementation of namespaces, I think he may be right that it'd be
relatively simple change that would dodge the issues associated with
last-var-wins and still pass the bar of eliminating upgrade pain.

(This last potential solution would not address issue #2 for new code;
I think the only thing that will ensure fully-consistent semantics for
that case would be speculative definition of vars.)

So, there's my piece. I do hope something can be done to "fix" last-
var-wins before 1.2 goes out the door. FWIW, I'll willing and happy
to do whatever's necessary (/ blessed) in order to make it happen.

Cheers,

- Chas


[1]
WARNING: munge already refers to: #'myns.utils/munge in namespace:
myns.utils, being replaced by: #'clojure.core/munge
WARNING: munge already refers to: #'clojure.core/munge in namespace:
myns.utils, being replaced by: #'myns.utils/munge
Exception in thread "main" java.lang.ExceptionInInitializerError
(couchdb.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:5359)
at clojure.lang.Compiler.eval(Compiler.java:5335)
at clojure.lang.Compiler.load(Compiler.java:5776)
at clojure.lang.Compiler.loadFile(Compiler.java:5739)
at clojure.lang.RT$3.invoke(RT.java:294)
at user$eval__1.invoke(run1029025770706331565.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:5343)
at clojure.lang.Compiler.load(Compiler.java:5776)
at clojure.lang.Compiler.loadFile(Compiler.java:5739)
at clojure.main$load_script__6498.invoke(main.clj:214)
at clojure.main$script_opt__6526.invoke(main.clj:266)
at clojure.main$main__6544.doInvoke(main.clj:347)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.lang.Var.invoke(Var.java:369)
at clojure.lang.AFn.applyToHelper(AFn.java:165)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.main.main(main.java:37)
Caused by: java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at clojure.lang.RT.loadClassForName(RT.java:1568)
at clojure.lang.RT.load(RT.java:397)
at clojure.lang.RT.load(RT.java:379)
at clojure.core$load__5294$fn__5301.invoke(core.clj:4820)
at clojure.core$load__5294.doInvoke(core.clj:4819)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.core$load_one__5235.invoke(core.clj:4644)
at clojure.core$load_lib__5250.doInvoke(core.clj:4681)
at clojure.lang.RestFn.applyTo(RestFn.java:143)
at clojure.core$apply__3776.invoke(core.clj:537)
at clojure.core$load_libs__5266.doInvoke(core.clj:4719)
at clojure.lang.RestFn.applyTo(RestFn.java:138)
at clojure.core$apply__3776.invoke(core.clj:537)
at clojure.core$require__5288.doInvoke(core.clj:4784)
at clojure.lang.RestFn.invoke(RestFn.java:437)
at myns.couchdb
$eval__5$loading__5183__auto____6.invoke(couchdb.clj:1)
at myns.couchdb$eval__5.invoke(couchdb.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:5343)
... 16 more
Caused by: java.lang.IllegalStateException: Var myns.utils/munge is
unbound.
at clojure.lang.Var.deref(Var.java:142)
at clojure.lang.Var.get(Var.java:133)
at myns.utils__init.load(Unknown Source)
at myns.utils__init.<clinit>(Unknown Source)
... 36 more

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To post to this group, send email to cloju...@googlegroups.com.
To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.

Stuart Halloway

ongelezen,
13 mei 2010, 11:24:2813-05-2010
aan cloju...@googlegroups.com
Chas,

Thanks for putting this together. Can't respond in detail right now
because Rich and I are in the classroom, but I would like to suggest
that we make a community push to fix key libraries' warnings over the
next 48 hours.

That gives us the option of turning last-var-wins off entirely, and go
to Clojure 1.2 without doing anything new/untested in this area.

Stu
Allen beantwoorden
Auteur beantwoorden
Doorsturen
0 nieuwe berichten