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))))
;;    "