Namespaces
Description
Namespaces are compiler bookkeeping for organizing global symbols. When in a namespace then any symbols that are defined will have the current NAMESPACE:: prepended to the symbol. When symbols are resolved the compiler will also try to prepend the current namespace first in order to find the symbol.
Entering a namespace
From code use the 'with-ns' form.
(doc 'with-ns)
;; => "Usage: (with-ns SYMBOL sexp+)
;;
;; Create a namespace and compile sexp+ within it. Restore the previous namespace when scope ends.
;; THe symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
;; This will cause all globals defined to have namespace:: prepended.
;; This will also clear any existing imports.
;;
;; Section: core
;;
;; Example:
;; (with-ns test-with-ns
;; (def ttf (fn () '(1 2 3)))
;; (test::assert-equal '(1 2 3) (ttf))
;; (test::assert-equal '(1 2 3) (test-out::ttf)))
;; (test::assert-equal '(1 2 3) (test-out::ttf))
;; "
From the top-level REPL you can use 'ns'.
(doc 'ns)
;; => "Usage: (ns SYMBOL)
;;
;; Changes to namespace. This is "open-ended" change and is intended for use with
;; the REPL prefer with-ns for scripts.
;; The symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
;; This will cause all globals defined to have namespace:: prepended.
;; This will also clear any existing imports.
;;
;; Section: core
;;
;; Example:
;; (ns testing)
;; (def x #t)
;; (test::assert-true x)
;; (ns ::)
;; (test::assert-true testing::x)
;; "
This is an open-ended namespace change intended for the repl, prefer with-ns for a scoped namespace in code.
Imports
Other namespaces can be imported to allow its symbols to be accessed in a shorter form. Use the 'import' form for this .
(doc 'import)
;; => "Usage: (import namespace [:as symbol])
;;
;; Will import a namespace. Without an :as then all symbols in the namespace will become available in the current
;; namespace as if local. With [:as symbol] then all namespace symbols become available with symbol:: prepended.
;;
;; Section: core
;;
;; Example:
;; (ns testing)
;; (def x #t)
;; (test::assert-true x)
;; (ns ::)
;; (test::assert-true testing::x)
;; (import testing)
;; (test::assert-true x)
;; (import testing :as t)
;; (test::assert-true t::x)
;; "
For instance using (import iter)
will allow any symbols in the iter namespace to be used without prepending 'iter::'. You can also use the (import iter :as i)
, the :as form allows the namespace to be given a different name. In this case the iter namespace could be replaced with 'i', (i::for ...) instead of (iter::for ...) for example. Imports are resolved in the order they are compiled in case of conflict (i.e. the first import that resolves a symbol wins). Imports are attached to the current namespace, changing namespaces will clear imports (note that 'with-ns' saves and restores the previous namespace with imports).
Loading code
To load new code into your environment use load or run-script.
Load
The load form should generally be preferred. It will compile the code at compile time (vs runtime) and execute it at runtime. This means:
- The path parameter has to be known at compile time: a string const, defined global or form that does not need local inputs.
- Any symbols defined in the loaded code will be known to the compiler at compile time and available for use.
(doc 'load)
;; => "Usage: (load path) -> [last form value]
;;
;; Read and eval a file (from path- a string). The load special form executes at compile time.
;; This means it's parameter must resolve at compile time. Most of the time you will want to use
;; this in conjunction with 'with-ns' to namespace the contents.
;; Note: on it's own does nothing with namespaces.
;;
;; Section: core
;;
;; Example:
;; (comp-time (def test-temp-file (get-temp-file)) nil)
;; (defer (fs-rm test-temp-file))
;; (let (tst-file (fopen test-temp-file :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(with-ns test-load")
;; (fprn tst-file " (defn test-fn () '(1 2 3)))"))
;; (load test-temp-file) ; put stuff in it's own namespace
;; (test::assert-equal '(1 2 3) (test-load::test-fn))
;;
;;
;; (with-ns test-out2
;; (comp-time
;; (def test-temp-file (get-temp-file))
;; (let (tst-file (fopen test-temp-file :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(defn test-fn () '(1 2 3))"))
;; nil)
;; (defer (fs-rm test-temp-file))
;; (load test-temp-file) ; put new stuff in current namespace
;; (test::assert-equal '(1 2 3) (test-fn))
;; (test::assert-equal '(1 2 3) (test-out2::test-fn)))
;; "
Run Script
The run-script form loads each form in the file, compiles and executes it at runtime. This means:
- It can take any parameter since it is resolved at runtime.
- Globals it defines will NOT be known until after it runs at runtime.
(doc 'run-script)
;; => "Usage: (run-script path) -> [last form value]
;;
;; Read and eval a file (from path- a string).
;;
;; Section: scripting
;;
;; Example:
;; (def test-load::test-fn)
;; (with-temp-file (fn (tmp)
;; (let (tst-file (fopen tmp :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(with-ns test-load")
;; (fprn tst-file " (defn test-fn () '(1 2 3)))"))
;; (run-script tmp)
;; (test::assert-equal '(1 2 3) (test-load::test-fn))))
;; "