Adam Arold
Adam Arold

Reputation: 30568

What is the best practice for using clojure.repl functions during interacitve development?

I often find myself typing (ns user) and pressing C+c M+n repeatedly when I work on clojure source code. The problem is that I often use functions like source and doc and they are in clojure.repl and I don't want to :require them to my namespaces. What are experienced clojurians doing in this case?

Clarification: I know how clojure's namespacing works. What I want to achieve is to be able to call (source myfunc),(doc myfunc) etc. without the need to use fully qualified names in the REPL and without the need to require the functions from clojure.repl in each of my namespaces.

Upvotes: 3

Views: 342

Answers (2)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91597

Thanks for clearing up what you are asking for.

Leiningen has a feature called :injections which you can combine with vinyasa to get this effect If you put something like this in your leiningen profile:

~/lein/profiles.clj:

{:user {:plugins []
        :dependencies [[im.chit/vinyasa "0.1.8"]]
        :injections [(require 'vinyasa.inject)
                      (vinyasa.inject/inject
                       'clojure.core '>
                       '[[clojure.repl doc source]
                         [clojure.pprint pprint pp]])]}}

Because this is in your profiles.clj it only affects you. Other people who work on the project won't be affected.


Because injecting into clojure.core strikes me as a little iffy, I follow the vinyasa author's advice and inject into a namespace called . which is crated by my profile for every project I work on. This namespace always exists which makes these function work even in newly created namespaces that don't yet refer clojure.core.

My ~/.lein/profiles.clj:

{:user
  {:plugins []
   :dependencies [[spyscope "0.1.4"]
                  [org.clojure/tools.namespace "0.2.4"]
                  [io.aviso/pretty "0.1.8"]
                  [im.chit/vinyasa "0.4.7"]]
   :injections
   [(require 'spyscope.core)
    (require '[vinyasa.inject :as inject])
    (require 'io.aviso.repl)
    (inject/in ;; the default injected namespace is `.`

               ;; note that `:refer, :all and :exclude can be used
               [vinyasa.inject :refer [inject [in inject-in]]]
               [clojure.pprint :refer [pprint]]
               [clojure.java.shell :refer [sh]]
               [clojure.repl :refer [doc source]]
               [vinyasa.maven pull]
               [vinyasa.reflection .> .? .* .% .%> .& .>ns .>var])]}}

which works like this:

hello.core> (./doc first)
-------------------------
clojure.core/first
([coll])
  Returns the first item in the collection. Calls seq on its
    argument. If coll is nil, returns nil.
nil
hello.core> (in-ns 'new-namespace)
#namespace[new-namespace]
new-namespace> (./doc first)
nil
new-namespace> (clojure.core/refer-clojure)
nil
new-namespace> (./doc first)
-------------------------
clojure.core/first
([coll])
  Returns the first item in the collection. Calls seq on its
    argument. If coll is nil, returns nil.
nil

Upvotes: 4

OlegTheCat
OlegTheCat

Reputation: 4513

To achieve this, you may use vinyasa library, in particular its inject functionality. Basically you need to add needed functions from clojure.repl namespace to clojure.core namespace. After you won't need to require them explicitly. See the following:

user> (require '[vinyasa.inject :refer [inject]])
nil

;; injecting `source` and `doc` symbols to clojure.core
user> (inject '[clojure.core [clojure.repl source doc]]) 
[]

;; switching to some other namespace
user> (require 'my-project.core)
nil
user> (in-ns 'my-project.core)
#namespace[my-project.core]

;; now those functions are accessible w/o qualifier
my-project.core> (doc vector) 
-------------------------
clojure.core/vector
([] [a] [a b] [a b c] [a b c d] [a b c d e] [a b c d e f] [a b c d e f & args])
  Creates a new vector containing the args.
nil

Upvotes: 1

Related Questions