Clojure is a dynamically-typed general-purpose programming language with Lisp syntax.
Its features support the functional style of programming with first-class functions and immutable values by default. Using reassignable variables is not as easy in Clojure as in many mainstream languages, since variables have to be created and updated like container objects. This encourages use of pure values that will stay the way they were at the moment they were last seen. This typically makes code much more predictable, testable and concurrency-capable. This works for collections too, since Clojure's built-in data structures are persistent.
For performance, Clojure supports type-hinting to eliminate unnecessary reflection where possible. Also, groups of changes to persistent collections can be done to transient versions, reducing the amount of objects involved. This is not necessary most of the time, since persistent collections fast to copy since they share most of their data. Their performance guarantees are not far from their mutable counterparts.
Among other features, Clojure also has:
Due to its simple syntax and high extensibility (via macros, implementation of core interfaces and reflection), some commonly-seen language features can be added to Clojure with libraries. For instance, core.typed
brings a static type checker, core.async
brings simple channel-based concurrency mechanisms, core.logic
brings logic programming.
Designed as a hosted language, it can interoperate with the platform it runs on. While the primary target is JVM and the entire ecosystem behind Java, alternative implementations can run in other environments too, such as ClojureCLR running on the Common Language Runtime or ClojureScript running on JavaScript runtimes (including web browsers). While alternative implementations may lack some of the functionality from the JVM version, they are still considered one family of languages.
Macros are just functions that run at compile time, i.e. during the eval
step in a read-eval-print-loop.
Reader macros are another form of macro that gets expanded at read time, rather than compile time.
Best practice when defining macro.
#
at the end of each symbol.Clojure spec is a new specification/contracts library for clojure available as of version 1.9.
Specs are leveraged in a number of ways including being included in documentation, data validation, generating data for testing and more.
The core.match
library implements a pattern match compilation algorithm that uses the notion of "necessity" from lazy pattern matching.
As a hosted language, Clojure provides excellent interoperability support with Java. Clojure code can also be called directly from Java.
This should not be confused with (defn)
, which is used for defining functions.
Performing mathematical operations is the basis of manipulating data and working with lists. Therefore, understand how it works is key to progressing in one's understanding of Clojure.
Transducers allow the lazyness to be controlled as they are consumed. For example into
is eager as would be expected, but sequence
will lazily consume the sequence through the transducer. However, the lazyness guarantee is different. Enough of the source will be consumed to produce an element initially:
(take 0 (sequence (map #(do (prn '-> %) %)) (range 5)))
;; -> 0
;; => ()
Or decide if the list is empty:
(take 0 (sequence (comp (map #(do (prn '-> %) %)) (remove number?)) (range 5)))
;; -> 0
;; -> 1
;; -> 2
;; -> 3
;; -> 4
;; => ()
Which differs from the usual lazy sequence behaviour:
(take 0 (map #(do (prn '-> %) %) (range 5)))
;; => ()