When Rich Hickey created Clojure he did so because he wanted a Lisp language that used immutable data structures by default, that was designed with concurrency in mind, and was compatible with an industry-standard platform: the Java virtual machine (JVM).
Initially released in 2007, Clojure is uniquely suited to a future dominated by an increasing demand for big data, microservices, and multicore technologies. In this article we’ll dive into Clojure and what sets it apart from other functional programming languages.
What is Clojure?
Clojure is a general-purpose, dynamic, compiled, and predominantly functional programming language from the Lisp family tree. Amazon, Staples, and Walmart are just some examples of major companies that use it in their technology stacks. Let’s take a closer look at some of the features that define this language.
It’s a dialect of Lisp for the JVM
Clojure often identifies itself as a “functional Lisp for the JVM.” You’ve probably already heard of the JVM, but what is Lisp and what does it mean to be a dialect of it?
Lisp is a family of languages, the first of which appeared in 1958 as a practical mathematical notation for computer programs. Inspired by Alonzo Church’s lambda calculus, Lisp became the language of choice for early AI research. As a modern dialect of Lisp, Clojure supports the following features:
- Distinctive Lisp-like syntax. It’s a prefix syntax with a liberal use of parentheses.
- REPL (Read-Eval-Print-Loop) environment that tightly integrates with popular integrated development environments (IDEs). REPL gives programmers the joy of instantly seeing the results of their code as they write it, encouraging experimentation and boosting programmer productivity.
- Code as data, which means that like Lisp, Clojure source code is a valid data structure known as an abstract syntax tree that can be accessed and manipulated.
- Powerful macro system leveraging that code is data to enable metaprogramming, which is the art of writing code that generates more code.
Lisp is short for “list processor.” Linked lists are the data structure that allow Clojure and other Lisp languages to treat source code as data, giving the language its metaprogramming powers. That’s the reason for all those parentheses; the source code is actually a parenthesized list data structure called an s-expression.
It’s functional and dynamic
Clojure embraces functional programming (FP). Functions are treated as first-class citizens, and data is immutable by default. When you create lists, maps, vectors, etc., they are immutable by definition. Functional features include:
- Declarative programming model. You express the logic of a program’s structure and elements (what you want data to do) without having to describe its control flow (how it’s done).
- Support for higher order functions. These are functions that can take in functions as arguments and/or return functions as results.
- Immutable persistent data structures. When a change occurs, the old data structure is preserved, and a new structure is returned expressing the relevant parts of the old structure with the newly created data.
- Absence of side effects. While complete absence of side effects is impossible for real-world applications, Clojure’s immutable information model does a good job of isolating them. Clojure uses side effects explicitly via its language syntax.
While most functional languages, such as Scala and Haskell, tend toward static types, Clojure is dynamic. REPL makes it easier to catch errors as you code, and dynamism makes code more flexible and extensible. Check out Rich Hickey’s keynote at Clojure/conj 2017 for more details on Clojure’s design choices.
It decouples polymorphism from object-oriented programming
Polymorphism isn’t just for object-oriented programming (OOP) languages. It’s a feature that involves handling different data types with a uniform interface. Clojure supports polymorphism in the following ways:
- Multimethods, which are functions that enable ad hoc polymorphism that is applied at run time by dispatching on types, attributes, values, and metadata of one or more arguments.
- Protocols, which are groups of functions that have different implementations for different data types.
It’s desirable to be able to write a function that can be used for multiple data types and extend said function regardless of inputs. Multimethods and protocols give Clojure polymorphism without the need for OO inheritance.
Made for concurrency
Clojure has great support for concurrency, which is the ability to deal with multiple tasks at once, such as leveraging the power of multicore CPUs. Immutable data structures can easily be shared across multiple threads.
And since Clojure is a practical language for real-world applications, it also has a few tricks to allow state change in concurrent systems without compromising stability and consistency.
- The software transactional memory (STM) system provides an elegant solution for sharing state between threads in a synchronous and coordinated manner via transactional references (using the functions alter, ref, deref, dosync, ref-set, etc.).
- Agents let you share changing state between threads in an asynchronous and independent manner.
- Atoms let you share changing state between threads in a synchronous and independent manner.
- Dynamic variables allow you to isolate changing state within threads.
Clojure is able to avoid unwanted side effects by being explicit in its handling of changing state. Concurrency is directly supported by the language. There are even libraries, such as core.async, based on CSP (communicating sequential processes), which make asynchronous programming easier.
It’s great for microservices
There’s an emerging trend of enterprises switching to microservice architectures (Upwork included). The advantage of building software out of smaller isolated functional units is that they are easier to test, scale, and share.
This advantage is becoming harder to ignore in a world that is increasingly reliant on distributed technologies such as the Internet of Things (IoT), where it’s important that software systems can deal with the huge diversity of devices and platforms.
Clojure encourages the creation of small isolated code blocks that can work anywhere. Immutability makes it decoupled by design, while macros give it the flexibility and power of OO design. Clojure readily compiles to Java bytecode for the JVM, allowing it to fit easily into larger industry-standard technology stacks.
ClojureScript
ClojureScript (CLJS) brings the benefits of Clojure to web application development. CLJS compiles to JavaScript, allowing it to run on the client side across browsers and mobile devices. You can even run it on the back end of web technologies on Node.js.
CLJS offers a rich set of tools. Reagent and re-frame allow it to integrate with React and React Native. Solutions such as Figwheel allow developers to instantly see code changes in the browser without reloading. The core.async library mentioned earlier makes CLJS a natural choice for writing JavaScript applications that require a high level of concurrency (e.g. chat applications).
CLJS basically gives those who love working with Clojure the ability to work within the JavaScript software domain.
Conclusion
Clojure is a powerful language geared toward concurrency that gives you the benefits of Lisps, FP, and dynamism. An advanced REPL gives Clojure programmers the freedom to experiment, making them more productive. Support for the JVM makes Clojure a viable option for many Java-based technology stacks. Clojure is great for distributed, concurrent, multicore application, or situations where a components-oriented approach to software development is desired.
Eager to learn about Clojure and other programming languages? Check out the Hiring Headquarters for more tips and tricks.
from Business 2 Community https://ift.tt/2zgTLPZ
Comments
Post a Comment