Clojure

clojure.main/repl unconditionally refers REPL utilities into *ns*

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Completed
  • Affects Version/s: Release 1.4, Release 1.5
  • Fix Version/s: Release 1.5
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code
  • Approval:
    Ok

Description

A number of vars from clojure.repl, clojure.java.javadoc, and clojure.pprint are unconditionally referred into *ns* by clojure.main/repl. This is fine when it is being used e.g. as the primary driver of a terminal-bound Clojure REPL, but other usages can end up bringing those utility vars into namespaces other than 'user. This can cause problems if clojure.main/repl is used to drive a REPL within namespaces that already have referred or interned vars with the same names as those utility vars, e.g.:

$ java -jar ~/.m2/repository/org/clojure/clojure/1.5.0-alpha6/clojure-1.5.0-alpha6.jar
Clojure 1.5.0-alpha6
user=> (ns foo)
nil
foo=> (defn pp [] "hi!")
#'foo/pp
foo=> (pp)
"hi!"
foo=> (clojure.main/repl)
foo=> (pp)
nil
nil
foo=> (defn pp [] "whoops")
CompilerException java.lang.IllegalStateException: pp already refers to: #'clojure.pprint/pp in namespace: foo, compiling:(NO_SOURCE_PATH:7:1)

Worse, nREPL uses clojure.main/repl (in large part to maximize the consistency of REPL behaviour across different Clojure versions), where each user expression is evaluated through a separate clojure.main/repl invocation. This leads to the same problems as above, but for every nREPL user, session, and expression (reported @ NREPL-31).

A simple fix for this is to perform these refers only if *ns* is 'user (which, AFAICT, was the only intended effect of CLJ-310, CLJ-454, and https://github.com/clojure/clojure/commit/04764db, the changes that added these automatic implicit refers to clojure.main/repl).

  1. CLJ-1085.diff
    10/Oct/12 2:19 PM
    1 kB
    Chas Emerick
  2. CLJ-1085-refactor.diff
    15/Oct/12 4:47 AM
    2 kB
    Chas Emerick

Activity

Hide
Chas Emerick added a comment -

Patch attached to only refer in the utility vars if in the user namespace.

Show
Chas Emerick added a comment - Patch attached to only refer in the utility vars if in the user namespace.
Chas Emerick made changes -
Field Original Value New Value
Patch Code [ 10001 ]
Attachment CLJ-1085.diff [ 11548 ]
Hide
Steve Miner added a comment -

It would probably be better to test with ns-name (as opposed to comparing strings):

(= 'user (ns-name ns))

Show
Steve Miner added a comment - It would probably be better to test with ns-name (as opposed to comparing strings): (= 'user (ns-name ns))
Chas Emerick made changes -
Attachment CLJ-1085.diff [ 11548 ]
Hide
Kevin Downey added a comment -

I don't follow how it is required to call `clojure.main/repl` for every input

Show
Kevin Downey added a comment - I don't follow how it is required to call `clojure.main/repl` for every input
Hide
Chas Emerick added a comment -

Gah, of course. :-/ Patch updated.

Show
Chas Emerick added a comment - Gah, of course. :-/ Patch updated.
Chas Emerick made changes -
Attachment CLJ-1085.diff [ 11549 ]
Hide
Chas Emerick added a comment -

@Kevin: It's not required, but I found it far more straightforward to not try to pretend that the underlying transport was stream-based when it's actually message-based. It also means that sessions can be very lightweight: unless code is being evaluated within a session, it is not occupying a thread, and takes up only as much space as its map of thread-local bindings.

Show
Chas Emerick added a comment - @Kevin: It's not required, but I found it far more straightforward to not try to pretend that the underlying transport was stream-based when it's actually message-based. It also means that sessions can be very lightweight: unless code is being evaluated within a session, it is not occupying a thread, and takes up only as much space as its map of thread-local bindings.
Chas Emerick made changes -
Attachment CLJ-1085-refactor.diff [ 11560 ]
Hide
Chas Emerick added a comment -

Based on discussion on clojure-dev, I have attached an alternative patch ({{CLJ-1085-refactor.diff}}), which:

  • breaks out the libspecs specifying the implicit refers into their own var (so that they can be consistently applied by other REPL implementations)
  • moves the default require of the libspecs to be invoked only when REPL is started from clojure.main
Show
Chas Emerick added a comment - Based on discussion on clojure-dev, I have attached an alternative patch ({{CLJ-1085-refactor.diff}}), which:
  • breaks out the libspecs specifying the implicit refers into their own var (so that they can be consistently applied by other REPL implementations)
  • moves the default require of the libspecs to be invoked only when REPL is started from clojure.main
Hide
Stuart Halloway added a comment -

Screened and liked the second "refactor" patch.

Show
Stuart Halloway added a comment - Screened and liked the second "refactor" patch.
Stuart Halloway made changes -
Approval Screened [ 10004 ]
Fix Version/s Release 1.5 [ 10150 ]
Rich Hickey made changes -
Approval Screened [ 10004 ] Ok [ 10007 ]
Stuart Halloway made changes -
Resolution Completed [ 1 ]
Status Open [ 1 ] Closed [ 6 ]

People

Vote (3)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: