Search code examples
clojureclojurescript

Using required namespace in the repl


When I require a namespace inside a clojure-script source file, I can use it afterwards in the code.

E.g:

(ns my.core
  (:require [mylib.core :as lib]))

(lib/my-f)

(def something 99)

However, when I try to call (lib/my-f) inside the repl - after changing the namespace via (ns my.core) - I cannot access it. In contrast, all other definitions inside the ns are acessible: like something from the example above.

Is there a way to access the requirements in the repl? Or do I have to require them manually in the repl every time? This would be very tedious of course.


Solution

  • If you use ns to change namespace in a ClojureScript REPL, this sets the namespace aliases to match those used in the ns form.

    Here is an example illustrating the concept:

    $ clj -m cljs.main
    ClojureScript 1.10.520
    cljs.user=> (ns foo.core (:require [clojure.string :as string]))
    
    foo.core=> (string/starts-with? "abc" "a")
    true
    foo.core=> (ns bar.core)
    
    bar.core=> (ns foo.core)
    
    foo.core=> (string/starts-with? "abc" "a")
    WARNING: No such namespace: string, could not locate string.cljs, string.cljc, or Closure namespace "" at line 1 <cljs repl>
    WARNING: Use of undeclared Var string/starts-with? at line 1 <cljs repl>
    ReferenceError: "string" is not defined
    

    If instead you use the in-ns REPL special to change to an existing namespace, this will preserve aliases:

    $ clj -m cljs.main
    ClojureScript 1.10.520
    cljs.user=> (ns foo.core (:require [clojure.string :as string]))
    
    foo.core=> (string/starts-with? "abc" "a")
    true
    foo.core=> (ns bar.core)
    
    bar.core=> (in-ns 'foo.core)
    nil
    foo.core=> (string/starts-with? "abc" "a")
    true
    

    An interesting related aspect: If you use require, it will, under the hoods, employ an ns form with special meta baked into the form that preserves existing aliases:

    $ clj -m cljs.main
    ClojureScript 1.10.520
    cljs.user=> (require '[clojure.string :as string])
    nil
    cljs.user=> (require '[clojure.string :as str])
    nil
    cljs.user=> (string/starts-with? "abc" "a")
    true
    cljs.user=> (str/starts-with? "abc" "a")
    true
    

    If you are curious, this is the :merge true meta here: https://github.com/clojure/clojurescript/blob/r1.7.228/src/main/clojure/cljs/repl.cljc#L679

    and it is honored by the analyzer here: https://github.com/clojure/clojurescript/blob/r1.7.228/src/main/clojure/cljs/analyzer.cljc#L1953

    By seeing how this works, it should provide some insight into why an ns form evaluated directly in the REPL (without merge meta) can lead to aliases being cleared.

    In short, avoid directly using the ns special to change to a namespace in the REPL. Instead use it to create a new namespace in the REPL while specifying any required namespaces.

    Use the in-ns REPL special to switch to an existing namespace. It can also be used to create a new namespace.

    Use require to load namespaces into the REPL, and then use in-ns to switch to them.