Dunaj - Towards Simpler API

With core API splitted into separate namespaces and with protocols
being used to specify core abstractions, Dunaj further simplifies its
API by providing and adhering to additional idioms, conventions and
best practices. Moreover, Dunaj changes how compiler recognizes
special symbols, and makes them fully qualified.

Fully qualified special symbols

Clojure defines a handful of special symbols like if, loop,
recur or try. Unfortunatelly, special symbols are not qualified
and it is very hard to shadow them with custom implementation when
needed, as special symbols override any referred vars
(such need is justified in several libraries, e.g. core.typed or
synthread). Dunaj adds qualified versions of all special symbols
and puts them in the clojure.core namespace, subject to standard
refer rules. Dunaj’s API does not contain any special symbols (if,
loop and other special symbols are in Dunaj defined as macros).
The handling of unqualified special symbols in both reader and
compiler is left almost untouched and behaves very similarly as in
Clojure. Old version of Dunaj had more strict handling of qualified
specials symbols in reader but this had caused backwards compatibility
problems with e.g. tools.analyzer or ztellman/potemkin.

Note that for special symbol def, Dunaj provides a def+
macro that accepts type signatures.

1
2
3
4

(nsfoo.bar)
#'if;;=> java.lang.RuntimeException: Unable to resolve var: if in this context

By providing such generalized predicates and functions, Dunaj was
able to support more functionalities for transformed collections.
For example, reversed vectors (both primitive and ordinary) are now
sectionable and counted, Vars are named, instants and regexes
have a canonical string representation and it is now trivial to add
fold support for a collection that is sectionable.

Unified def-like syntax

Dunaj unifies syntax for all def-like macros. The def, defn,
defmacro, deftype, defrecord and defprotocol now all accept
optional type signature (not for defmacro), docstring and metadata
map. Following example demonstrates the new syntax:

API

A namespace consists of a set of named Vars that refer to other
objects. API is defined as a set of namespaces, with one entry point
namespace (which name usually ends with core).
Strict rules were applied to the contents of Dunaj’s public API. The
API as provided by Dunaj will only contain non-dynamic Vars that hold
functions, macros, deftype maps, constants, default objects, type
signatures or dynamic vars. Dunaj’s policy is to discourage
the use of following types of objects in the API:

No special forms. They are an implementation detail and should be
hidden behind a macro.

No dynamic Vars, as they cannot be aliased or extended.

No host classes or interfaces.

No generated deftypes constructors (both positional and named)

Protocols and protocol methods are considered a part of SPI
(and documented as such), even if defined together with
API functions or macros in the same namespace.

Dynamic vars are handled like other reference types such as atoms or
refs. Dunaj’s approach is to define vars that will hold a dynamic var
of your choice. Just as you wouldn’t put an atom itself in a
namespace (it’s also impossible as namespace can only contain
Vars), dynamic Vars have no place in the API.