List of Figures

Chapter 1. Introduction to Clojure

Figure 1.1. Shows the stages of a typical language processor and where the developer has programmatic control. Clojure doesn’t follow this model, as you’ll see.

Figure 1.2. An example AST. This shows a tree structure representation of code that might be written as 1 + 2, in a language such as Java or Ruby. There’s no notion of concrete syntax here.

Figure 1.3. Shows the stages of the Clojure runtime. Unlike typical languages, the developer has programmatic control in between the read and evaluate stages, via the macro system.

Figure 1.4. The object-oriented paradigm is only one of many in the field. The Clojure language borrows from several and offers the programmer the capability to quickly create new languages on top of it.

Chapter 4. Polymorphism with multimethods

Figure 4.1. There are several types of polymorphism. Subtype polymorphism (a form of inclusion) is the most common (Java/C++) kind of polymorphism but is only one of several.

Figure 4.2. An example of the commonly found subtype polymorphism. Which makeSound gets called depends on the type of receiver and is determined at runtime.

Figure 4.3. A simplistic hierarchy representing an AST. Each node is a subclass of a generic SyntaxNode and has functions for various tasks that a compiler or IDE might perform.

Figure 4.4. A modification is needed to simulate double dispatch. The accept method is a somewhat unclear but required method in each node, which will call back the visitor.

Figure 4.5. Each visitor class does one operation and knows how to process each kind of node. Adding new kinds of operation involves adding new visitors.

Figure 4.6. With multiple dispatch, visitors can have a straightforward, polymorphic visit method (off the type of SyntaxNodes), and SyntaxNode doesn’t need the accept method infrastructure.

Figure 4.7. Our domain demands a hierarchy of membership statuses. We can use this external-facing classification to simplify our code by defining an ad hoc hierarchy reflecting this structure.

Figure 4.8. A hierarchy could lead to multiple inheritance. Clojure doesn’t do anything to prevent this, but it provides an elegant way to break a tie if multiple dispatch values match.

Chapter 5. Clojure and Java interop

Figure 5.1. Typical organization of a Clojure project. The src directory contains the source code, organized in a similar way to Java packages.

Figure 5.2. Adding two new files, dcf.clj and fcf.clj, in a subdirectory of utils that have code for the same com.curry.utils.calculators namespace.

Chapter 6. State and the concurrent world

Figure 6.1. A reference that points to completely different immutable values over time

Chapter 7. Evolving Clojure through macros

Figure 7.1. Phases of the Clojure runtime. This separation is what makes the macro system possible.

Chapter 9. Data storage with Clojure

Figure 9.1. Output from the scan "clojure_test" command issued at the HBase shell

Chapter 10. Clojure and the web

Figure 10.1. How an HTTP request flows through the Ring framework

Chapter 11. Scaling through messaging

Figure 11.1. After establishing a connection with RabbitMQ via a call to new-connection, a message producer can send messages with a routing key using the send-message function. Similarly, a message consumer can accept messages sent with a particular routing key via a call to next-message-from.

Figure 11.2. Message flow between a client and a bank of remote worker processes. A client makes a request by calling a worker function, which sends a message over RabbitMQ via a global, well-known work queue. One of several workers picks it up and services the request. When finished, it sends the response back via a temporary routing key that was specified by the calling client.

Chapter 12. Data processing with Clojure

Figure 12.1. The mapping phase of the map/reduce approach applies a function to each input value, producing a list of key/value pairs for each input. All these lists (each containing several key/value pairs) are gathered into another list to constitute the final output of the mapping phase.

Figure 12.2. The combine phase takes the output of the mapping phase and collects each key and associated values from the collection of lists of key/value pairs. The combined output is then a map with unique keys created during the mapping process, with each associated value being a list of values from the mapping phase.

Figure 12.3. The input to the reduce phase is the output of the combiner, which is a map, with keys being all the unique keys found in the mapping operation and the values being the collected values for each key from the mapping process. The output of the reduce phase can be any arbitrary value.

Figure 12.4. The master/slave work framework builds on the remote worker code written in the chapter on scaling out Clojure programs with RabbitMQ. A master accepts a sequence of input elements and farms out the processing of each to a bank of remote worker processes. Each task sent off this way is tracked in the Redis key/value store, and this status is updated by the master as well as by the workers.

Chapter 13. More on functional programming

Figure 13.1. Although both partial and curry return functions, a partially applied function is always ready to run when it’s applied to the remaining arguments. If given insufficient arguments, a partially applied function will throw an exception complaining about that. Curried functions return functions that are further curried if they’re applied to insufficient arguments.

Figure 13.2. The minimal object system we’ve built implements a major portion of features that are supported by most common OO systems. Everything ultimately derives from a common entity called OBJECT. Instances look up the class they derive from and look up methods there. Methods can also be looked up in the chain of hierarchy.

Chapter 14. Protocols, records, and types

Figure 14.1. Calling def-modus-operandi creates a var that will hold information about the modus operandi, which can later be used to introspect it. The macro itself makes as many calls to defmulti as needed. The detail-modus-operandi macro is the other side of the modus operandi concept: it fills out the implementation details by expanding to as many defmethod calls as specified. It also updates the modus-operandi var to reflect the implementor information.

Figure 14.2. Calling defprotocol performs an analogous operation where a var is created to hold information about the protocol and its implementors. The underlying implementation will also result in a Java interface that pertains to the protocol being defined. Calls to extend, extend-type, and extend-protocol will update the var with implementor details and generate Java classes that implement the protocol.

Chapter 15. More macros and DSLs

Figure 15.1. As usual, the Clojure reader first converts the text of our programs into data structures. During this process, macros are expanded, including our defrot-encrypter macro, which generates a tableau. This tableau is a Clojure map and is included in the final form of the source code as an inline lookup table.

Figure 15.2. The typical layers in a DSL-driven system are shown here. Such systems benefit from a bottom-up design where the lowest levels are the primitive concepts of the domain modeled on top of the basic Clojure language. Higher layers are compositions of these primitives into more complex domain concepts. Finally, a runtime layer sits on top of these, which can execute code specified in a domain-specific language. This final layer often represents the core solution of the problem that the software was meant to solve.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.245.167