Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Discover millions of ebooks, audiobooks, and so much more with a free trial

From $11.99/month after trial. Cancel anytime.

The Clojure Workshop: Use functional programming to build data-centric applications with Clojure and ClojureScript
The Clojure Workshop: Use functional programming to build data-centric applications with Clojure and ClojureScript
The Clojure Workshop: Use functional programming to build data-centric applications with Clojure and ClojureScript
Ebook1,513 pages9 hours

The Clojure Workshop: Use functional programming to build data-centric applications with Clojure and ClojureScript

Rating: 0 out of 5 stars

()

Read preview

About this ebook

Learn how to solve problems using Clojure or ClojureScript and become a confident functional programmer with the help of engaging activities and challenging projects

Key Features
  • Master the tools and patterns of the Clojure and ClojureScript ecosystems
  • Learn the fundamentals of functional programming and immutability
  • Apply your skills practically by developing a range of scalable applications
Book Description

The Clojure Workshop is a step-by-step guide to Clojure and ClojureScript, designed to quickly get you up and running as a confident, knowledgeable developer.

Because of the functional nature of the language, Clojure programming is quite different to what many developers will have experienced. As hosted languages, Clojure and ClojureScript can also be daunting for newcomers because of complexities in the tooling and the challenge of interacting with the host platforms. To help you overcome these barriers, this book adopts a practical approach. Every chapter is centered around building something.

As you progress through the book, you will progressively develop the 'muscle memory' that will make you a productive Clojure programmer, and help you see the world through the concepts of functional programming. You will also gain familiarity with common idioms and patterns, as well as exposure to some of the most widely used libraries.

Unlike many Clojure books, this Workshop will include significant coverage of both Clojure and ClojureScript. This makes it useful no matter your goal or preferred platform, and provides a fresh perspective on the hosted nature of the language.

By the end of this book, you'll have the knowledge, skills and confidence to creatively tackle your own ambitious projects with Clojure and ClojureScript.

What you will learn
  • Write idiomatic code with Clojure and ClojureScript
  • Understand and use common patterns and best practices
  • Experiment with code and interact with programs using the REPL
  • Learn the fundamentals of functional programming and immutability
  • Master concepts including mapping, filtering, reducing and recursion
  • Structure and build your code using namespaces and Leiningen
  • Write unit tests to validate application behavior
  • Simplify your code and improve efficiency with macros
Who this book is for

The Clojure Workshop is for anyone who is curious about functional programming and wants to get started learning Clojure or ClojureScript. Prior experience of another programming language, such as Java or JavaScript, is recommended, and will help you grasp the concepts covered in this book more easily.

LanguageEnglish
Release dateJan 29, 2020
ISBN9781838825119
The Clojure Workshop: Use functional programming to build data-centric applications with Clojure and ClojureScript

Related to The Clojure Workshop

Related ebooks

Programming For You

View More

Reviews for The Clojure Workshop

Rating: 0 out of 5 stars
0 ratings

0 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    Book preview

    The Clojure Workshop - Joseph Fahey

    9781838825485cov_Low_Res.png

    The Clojure Workshop

    Copyright © 2020 Packt Publishing

    All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

    Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the authors, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.

    Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

    Authors: Joseph Fahey, Thomas Haratyk, Scott McCaughie, Yehonathan Sharvit, and Konrad Szydlo

    Technical Reviewers: Cavan David, Kirill Erokhin, Punit Naik, David Parker, Prashant Rathod, Erwin Rooijakkers, and John Stevenson

    Managing Editor: Rutuja Yerunkar

    Acquisitions Editors: Sarah Lawton, Manuraj Nair, Royluis Rodrigues, and Karan Wadekar

    Production Editor: Roshan Kawale

    Editorial Board: Shubhopriya Banerjee, Bharat Botle, Ewan Buckingham, Megan Carlisle, Mahesh Dhyani, Manasa Kumar, Alex Mazonowicz, Bridget Neale, Dominic Pereira, Shiny Poojary, Abhishek Rane, Brendan Rodrigues, Erol Staveley, Ankita Thakur, Nitesh Thakur, and Jonathan Wray

    First Published: January 2020

    Production Reference: 3241220

    ISBN: 978-1-83882-548-5

    Published by Packt Publishing Ltd.

    Livery Place, 35 Livery Street

    Birmingham B3 2PB, UK

    Table of Contents

    Preface

    1. Hello REPL!

    Introduction

    REPL Basics

    Exercise 1.01: Your First Dance

    Exercise 1.02: Getting around in the REPL

    Activity 1.01: Performing Basic Operations

    Evaluation of Clojure Code

    Basic Special Forms

    Exercise 1.03: Working with if, do, and when

    Bindings

    Exercise 1.04: Using def and let

    Exercise 1.05: Creating Simple Functions with fn and defn

    Activity 1.02: Predicting the Atmospheric Carbon Dioxide Level

    Truthiness, nil, and equality

    Exercise 1.06: The Truth Is Simple

    Equality and Comparisons

    Exercise 1.07: Comparing Values

    Activity 1.03: The meditate Function v2.0

    Summary

    2. Data Types and Immutability

    Introduction

    Simple Data Types

    Strings

    Numbers

    Exercise 2.01: The Obfuscation Machine

    Booleans

    Symbols

    Keywords

    Collections

    Maps

    Exercise 2.02: Using Maps

    Sets

    Exercise 2.03: Using Sets

    Vectors

    Exercise 2.04: Using Vectors

    Lists

    Exercise 2.05: Using Lists

    Collection and Sequence Abstractions

    Exercise 2.06: Working with Nested Data Structures

    Activity 2.01: Creating a Simple In-Memory Database

    Summary

    3. Functions in Depth

    Introduction

    Destructuring

    Exercise 3.01: Parsing Fly Vector's Data with Sequential Destructuring

    Exercise 3.02: Parsing MapJet Data with Associative Destructuring

    Advanced Call Signatures

    Destructuring Function Parameters

    Arity Overloading

    Variadic Functions

    Exercise 3.03: Multi-arity and Destructuring with Parenthmazes

    Higher-Order Programming

    First-Class Functions

    Partial Functions

    Composing Functions

    Exercise 3.04: High-Order Functions with Parenthmazes

    Multimethods

    Exercise 3.05: Using Multimethods

    Activity 3.01: Building a Distance and Cost Calculator

    Summary

    4. Mapping and Filtering

    Introduction

    map and filter

    map

    Exercise 4.01: Working with map

    filter

    Exercise 4.02: Getting Started with filter

    Other Members of the filter Family – take-while and drop-while

    Exercise 4.03: Partitioning a Sequence with take-while and drop-while

    Using map and filter Together

    Threading Macros

    Using Lazy Sequences

    Exercise 4.04: Watching Lazy Evaluation

    Exercise 4.05: Creating Our Own Lazy Sequence

    Common Idioms and Patterns

    Anonymous Functions

    Keywords as Functions

    Exercise 4.06: Extracting Data from a List of Maps

    Sets as Predicates

    Filtering on a Keyword with comp and a Set

    Exercise 4.07: Using comp and a Set to Filter on a Keyword

    Returning a List Longer than the Input with mapcat

    Mapping with Multiple Inputs

    Exercise 4.08: Identifying Weather Trends

    Consuming Extracted Data with apply

    Exercise 4.09: Finding the Average Weather Temperature

    Activity 4.01: Using map and filter to Report Summary Information

    Importing a Dataset from a CSV File

    Exercise 4.10: Importing Data from a CSV File

    Real-World Laziness

    Exercise 4.11: Avoiding Lazy Evaluation Traps with Files

    Convenient CSV Parsing

    Exercise 4.12: Parsing CSV with semantic-csv

    Exercise 4.13: Querying the Data with filter

    Exercise 4.14: A Dedicated Query Function

    Exercise 4.15: Using filter to Find a Tennis Rivalry

    Activity 4.02: Arbitrary Tennis Rivalries

    Summary

    5. Many to One: Reducing

    Introduction

    The Basics of reduce

    Exercise 5.01: Finding the Day with the Maximum Temperature

    Initializing reduce

    Partitioning with reduce

    Looking Back with reduce

    Exercise 5.02: Measuring Elevation Differences on Slopes

    Exercise 5.03: Winning and Losing Streaks

    Reducing without reduce

    zipmap

    Exercise 5.04: Creating a Lookup Table with zipmap

    Maps to Sequences, and Back Again

    group-by

    Exercise 5.05: Quick Summary Statistics with group-by

    Summarizing Tennis Scores

    Exercise 5.06: Complex Accumulation with reduce

    Introduction to Elo

    Exercise 5.07: Calculating Probabilities for a Single Match

    Exercise 5.08: Updating Player Ratings

    Activity 5.01: Calculating Elo Ratings for Tennis

    Summary

    6. Recursion and Looping

    Introduction

    Clojure's Most Procedural Loop: doseq

    Looping Shortcuts

    Exercise 6.01: An Endless Stream of Groceries

    Recursion at Its Simplest

    Exercise 6.02: Partitioning Grocery Bags

    When to Use recur

    Exercise 6.03: Large-Scale Grocery Partitioning with recur

    What about loop?

    Exercise 6.04: Groceries with loop

    Tail Recursion

    Solving Complex Problems with Recursion

    Exercise 6.05: Europe by Train

    Pathfinding

    Exercise 6.06: The Search Function

    Exercise 6.07: Calculating the Costs of the Routes

    A Brief Introduction to HTML

    Activity 6.01: Generating HTML from Clojure Vectors

    Summary

    7. Recursion II: Lazy Sequences

    Introduction

    A Simple Lazy Sequence

    Consuming a Sequence

    Exercise 7.01: Finding Inflection Points

    Exercise 7.02: Calculating a Running Average

    Lazy Consumption of Data

    Lazy Trees

    Exercise 7.03: A Tennis History Tree

    Exercise 7.04: A Custom take Function

    Knowing When to Be Lazy

    Exercise 7.05: Formatting the Matches

    Activity 7.01: Historical, Player-Centric Elo

    Summary

    8. Namespaces, Libraries and Leiningen

    Introduction

    Namespaces

    Exercise 8.01: Investigating Namespaces Started by Default in REPL

    Exercise 8.02: Navigating Namespaces

    Importing Clojure Namespaces Using the refer Function

    Exercise 8.03: Using the refer Function to Import a Namespace

    Advanced Use of the refer Function

    Exercise 8.04: Using the :only Keyword

    Exercise 8.05: Using the :exclude Keyword

    Exercise 8.06: Using the :rename Keyword

    Importing Clojure Functions with require and use

    Exercise 8.07: Importing Clojure Functions with require and use

    Activity 8.01: Altering the Users List in an Application

    When You Want use versus When You Want require

    Leiningen—A Build Tool in Clojure

    Exercise 8.08: Creating a Leiningen Project

    Investigating project.clj

    Exercise 8.09: Executing the Application on the Command Line

    Exercise 8.10: Executing Application on the Command Line with arguments

    Activity 8.02: Summing Up Numbers

    Working with External Libraries

    Exercise 8.11: Using an External Library in a Leiningen Project

    Creating and Executing a jar with Leiningen

    Exercise 8.12: Creating a Jar File

    Leiningen Profiles

    Exercise 8.13: Adding Leiningen Profiles to a Project

    User-Wide Profiles

    Exercise 8.14: Using User-Wide Profiles

    Useful Clojure Libraries

    Activity 8.03: Building a Format-Converting Application

    Summary

    9. Host Platform Interoperability with Java and JavaScript

    Introduction

    Using Java in Clojure

    Exercise 9.01: Importing a Single Java Class in Clojure

    Working with Time in Java

    Exercise 9.02: Importing Multiple Java Classes in Clojure

    Exercise 9.03: Macros That Help Us Use Java in Clojure

    Working with Java I/O

    Immutability in Clojure

    Exercise 9.04: Coffee-Ordering Application – Displaying a Menu

    Exercise 9.05: Coffee-Ordering Application – Saving and Loading Orders

    Working with Java Data Types

    Exercise 9.06: Java Data Types

    Activity 9.01: Book-Ordering Application

    Using JavaScript in ClojureScript

    Exercise 9.07: Working with JavaScript Data Types

    Figwheel Template

    Reactive Web Programming Using Rum

    Exercise 9.08: Investigating Figwheel and Rum

    Drag and Drop

    Exercise 9.09: JavaScript Interoperability with Drag and Drop

    Exceptions and Errors in Clojure

    Exercise 9.10: Handling Errors and Exceptions in Clojure

    Errors in JavaScript

    ClojureScript Leiningen Templates

    Exercise 9.11: Handling Errors in ClojureScript

    Activity 9.02: Creating a Support Desk

    Summary

    10. Testing

    Introduction

    Why Testing Is Important

    Functional Testing

    Non-Functional Testing

    Clojure Unit Testing

    Exercise 10.01: Unit Testing with the clojure.test Library

    Using the Expectations Testing Library

    Exercise 10.02: Testing the Coffee Application with Expectations

    Unit Testing with the Midje Library

    Exercise 10.03: Testing the Coffee Application with Midje

    Property-Based Testing

    Exercise 10.04: Using Property-Based Testing in the Coffee-Ordering Application

    Activity 10.01: Writing Tests for the Coffee-Ordering Application

    Testing in ClojureScript

    Exercise 10.05: Setting Up Testing in ClojureScript

    Exercise 10.06: Testing ClojureScript Code

    Testing ClojureScript Applications with Figwheel

    Exercise 10.07: Tests in Figwheel Applications

    Exercise 10.08: Testing a ClojureScript Application

    Activity 10.02: Support Desk Application with Tests

    Summary

    11. Macros

    Introduction

    What is a Macro?

    A Very Minimal Macro

    Compile Time and Run Time

    Runtime Parameters

    Syntax Quoting

    Exercise 11.01: The and-ors Macro

    Exercise 11.02: An Automatic HTML Library

    Exercise 11.03: Expanding the HTML Library

    Macros in ClojureScript

    Macro Hygiene

    Avoiding Variable Capture with Automatic Gensyms

    Exercise 11.04: Monitoring Functions

    When to Use Manual gensyms

    Activity 11.01: A Tennis CSV Macro

    Interface Design

    Implementation

    Summary

    12. Concurrency

    Introduction

    Concurrency in General

    Automatic Parallelization with pmap

    Exercise 12.01: Testing Randomness

    Futures

    Exercise 12.02: A Crowdsourced Spellchecker

    Coordination

    Atoms

    Concept: Retries

    Refs and Software Transactional Memory

    Exercise 12.03: Stock Trading

    More Cohesion with refs

    Exercise 12.04: Keeping up with the Stock Price

    Agents

    Atoms in ClojureScript

    Exercise 12.05: Rock, Scissors, Paper

    Watchers

    Exercise 12.06: One, Two, Three… Rock!

    Activity 12.01: A DOM Whack-a-mole Game

    Summary

    13. Database Interaction and the Application Layer

    Introduction

    Connecting to a Database

    Exercise 13.01: Establishing a Database Connection

    Introduction to Connection Pools

    Exercise 13.02: Creating a Connection Pool

    Creating Database Schemas

    Primary Keys

    Foreign Keys

    Exercise 13.03: Defining and Applying a Database Schema

    Managing Our Data

    Inserting Data

    Inserting Single Rows

    Inserting Multiple Rows

    Exercise 13.04: Data Insertion

    Querying Data

    Exercise 13.05: Querying Our Database

    Manipulating Query Return Values

    Exercise 13.06: Controlling Results with Custom Functions

    Updating and Deleting Data

    Exercise 13.07: Updating and Removing Existing Data

    Introduction to the Application Layer

    Exercise 13.08: Defining the Application Layer

    Activity 13.01: Persisting Historic Tennis Results and ELO Calculations

    Summary

    14. HTTP with Ring

    Introduction

    HTTP, Web Servers, and REST

    Request and Response

    Exercise 14.01: Creating a Hello World Web Application

    Request Routing

    Using Compojure

    Exercise 14.02: Introducing Routing with Compojure

    Response Formats and Middleware

    Exercise 14.03: Response Rendering with Muuntaja

    Handling a Request Body

    Exercise 14.04: Working with a request Body

    Static Files

    Exercise 14.05: Serving Static Files

    Integrating with an Application Layer

    Accessing path and query Parameters in Compojure

    Exercise 14.06: Integrating with an Application Layer

    Activity 14.01: Exposing Historic Tennis Results and ELO Calculations via REST

    Summary

    15. The Frontend: A ClojureScript UI

    Introduction

    Hiccup instead of HTML

    Getting Started with Reagent

    The Virtual DOM and Component Lifecycle

    Exercise 15.01: Creating a Reagent Application

    Exercise 15.02: Displaying an Image with Style

    Managing Component State

    Exercise 15.03: A Button that Modifies Its Text

    Components with Children Components

    Exercise 15.04: Creating a Grid of Images

    Hot Reload

    JavaScript Interop

    Exercise 15.05: Fetching Data from an HTTP Endpoint

    Activity 15.01: Displaying a Grid of Images from the Internet

    Activity 15.02: Tennis Players with Ranking

    Summary

    Appendix

    Preface

    About

    This section briefly introduces the coverage of this book, the technical skills you'll need to get started, and the software requirements required to complete all of the included activities and exercises.

    About the Book

    The Clojure Workshop is a step-by-step guide to Clojure and ClojureScript, designed to quickly get you up and running as a confident, knowledgeable developer.

    Because of the functional nature of the language, Clojure programming is quite different to what many developers will have experienced. As hosted languages, Clojure and ClojureScript can also be daunting for newcomers because of complexities in the tooling and the challenge of interacting with the host platforms. To help you overcome these barriers, this book adopts a practical approach. Every chapter is centered around building something.

    As you progress through the book, you will progressively develop the ‘muscle memory' that will make you a productive Clojure programmer, and help you see the world through the concepts of functional programming. You will also gain familiarity with common idioms and patterns, as well as exposure to some of the most widely used libraries.

    Unlike many Clojure books, this Workshop will include significant coverage of both Clojure and ClojureScript. This makes it useful no matter your goal or preferred platform, and provides a fresh perspective on the hosted nature of the language.

    By the end of this book, you'll have the knowledge, skills and confidence to creatively tackle your own ambitious projects with Clojure and ClojureScript.

    Audience

    The Clojure Workshop is an ideal tutorial for the Clojure beginner who is just getting started. A basic understanding of JavaScript and Java would be ideal but not necessary. The Clojure Workshop will guide you well throughout the discussion on the interoperability of these technologies.

    About the Chapters

    Chapter 1, Hello REPL!, gets you typing code immediately. You'll learn the basics of the language, as well as how to get the most out of Clojure's interactive REPL.

    Chapter 2, Data Types and Immutability, provides more building blocks, but these are Clojure building blocks that expose you to one of Clojure's key features: immutability.

    Chapter 3, Functions in Depth, is a deeper dive into one of the areas that sets Clojure apart: the functional programming paradigm. These are the tools that will power you through the rest of the book.

    Chapter 4, Mapping and Filtering, is the first stop on your exploration of Clojure collections. The patterns and techniques here are all about learning to solve problems. The map and filter functions are two of the foremost Clojure workhorses.

    Chapter 5, Many to One: Reducing, will really start getting you thinking in new ways. The data-shaping techniques in this chapter complement those in the previous chapter.

    Chapter 6, Recursion and Looping, takes your collection techniques to the next level. This chapter will make you think. By the end of the chapter, you'll be ready to handle tricky problems using advanced functional patterns.

    Chapter 7, Recursion II: Lazy Sequences, completes the panorama of Clojure collections with a look at a distinctive Clojure feature. If you can write functions to process complex tree structures, you are ready to use Clojure to solve big problems.

    Chapter 8, Namespaces, Libraries, and Leiningen, provides a close look at the tools you need for building real-world Clojure and ClojureScript applications. You have the skills to write good Clojure code; now you need to know how to put your application together.

    Chapter 9, Host Platform Interoperability with Java and JavaScript, brings you up to speed on a topic that is one of Clojure's great strengths but can also be daunting. As a hosted language, Clojure gives you access to the underlying platform. Knowing how and when to use that power is a key Clojure skill.

    Chapter 10, Testing, is another important step in serious, real-world programming. Understanding Clojure and ClojureScript testing stories is a skill every professional programmer needs.

    Chapter 11, Macros, will help you understand a distinctive feature of the Lisp family of languages. Macros allow rich abstraction, but underneath the surface, there are a lot of important practical details.

    Chapter 12, Concurrency, reveals another unique Clojure strength. This chapter will give you a taste for building multithreaded applications on the Java Virtual Machine or event-driven ClojureScript single-page applications.

    Chapter 13, Database Interaction and the Application Layer, shows you how to leverage Clojure's database libraries. Many real applications require databases, so these skills are essential.

    Chapter 14, HTTP with Ring, shows you how to set up and run a Clojure-driven web server. The Ring libraries are the most widely used HTTP technology in the Clojure world.

    Chapter 15, The Frontend: A ClojureScript UI, helps put together many of the things you've already learned about ClojureScript, the last layer on a Clojure web stack.

    Conventions

    Code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles are shown as follows: Please note that this function is in the clojure.string namespace, which is not referred to by default.

    Words that you see on the screen, for example, in menus or dialog boxes, also appear in the text like this: When you click on the Fetch Images button, the images appear with authors' names.

    A block of code is set as follows:

    (defn remove-large-integers [ints]

      (remove #(and (integer? %) (> % 1000)) ints))

    In cases where inputting and executing some code gives an immediate output, this is shown as follows:

    user=> (sort [3 7 5 1 9])

    (1 3 5 7 9)

    In the example above, the code entered is (sort [3 7 5 1 9]), and the output is (1 3 5 7 9).

    New terms and important words are shown like this: Welcome to the Clojure Read Eval Print Loop (REPL), a command-line interface that we can use to interact with a running Clojure program.

    Key parts of code snippets are highlighted as follows:

    {:deps {compojure {:mvn/version 1.6.1}

    metosin/muuntaja {:mvn/version 0.6.4}

            ring/ring-core {:mvn/version 1.7.1}

            ring/ring-jetty-adapter {:mvn/version 1.7.1}}

    user=> (require '[muuntaja.middleware :as middleware])

    =>nil

    Long code snippets are truncated and the corresponding names of the code files on GitHub are placed at the top of the truncated code. The permalinks to the entire code are placed below the code snippet. It should look as follows:

    kvitova_matches.clj

    1  (def matches

    2    [{:winner-name Kvitova P.,

    3      :loser-name Ostapenko J.,

    4      :tournament US Open,

    5      :location New York,

    6      :date 2016-08-29}

    7     {:winner-name Kvitova P.,

    8      :loser-name Buyukakcay C.,

    9      :tournament US Open,

    10     :location New York,

    11     :date 2016-08-31}

    The full code can be found at: https://packt.live/2GcudYj

    Before You Begin

    Each great journey begins with a humble step. Our upcoming adventure in the land of Clojure is no exception. Before we can do awesome things with data, we need to be prepared with a productive environment. In this section, we will see how to do that.

    Installing Java

    Before installing Clojure, you need to make sure that you have the Java Developer's Kit (JDK) installed on your computer. For Mac and Linux users, prebuilt binaries are a few keystrokes away. On Mac, for Homebrew users, you can just type:

    $ brew install openjdk

    On Debian-based Linux distributions, you can check which version is available by typing the following:

    $ apt-get search openjdk

    Depending on the output, you can then type something like:

    $ sudo apt-get install openjdk-11-jdk

    Clojure does not require a particularly recent version of the JDK.

    For Windows, you can download the OpenJDK installer here: https://packt.live/3aBu1Qg. Once you have the installer, click on it to run, then follow the instructions.

    Installing Clojure

    Once you have a working JDK on your system, setting up Clojure is easy with the Leiningen tool.

    Copy the appropriate version (Windows or Mac/Linux) from the Leiningen home page, here: https://leiningen.org/.

    Place Leiningen in a directory that is part of your system's $PATH and make it executable.

    On Mac or Linux, this means putting it in a directory such as ~/bin and calling chmod:

    $ chmod +x ~/bin/lein

    On Windows, to change the $PATH variable, go to Control Panel > User Accounts > User Accounts and click on Change My Environment Variables. In the pane showing the user variables for your personal user account, click on Path and then choose Edit.

    Figure 0.1: User accounts

    Figure 0.1: User accounts

    Click on New to add a line, then type in the path to your new bin directory:

    Figure 0.2: Adding the path in bin directory

    Figure 0.2: Adding the path in bin directory

    Now that Leiningen is installed and executable, from the command line, simply type:

    $ lein

    Leiningen will fetch Clojure and all the libraries it needs to manage Clojure. And now, by simply typing lein repl, you'll have your very first Clojure REPL:

    Figure 0.3: REPL started

    Figure 0.3: REPL started

    Editors and IDEs

    While you can certainly do a lot with a REPL running in a console, it's much more convenient to integrate the Clojure REPL into your favorite editor. Plugins exist for just about every editor and environment out there, from Vim to Emacs and from IntelliJ to Electron or Visual Studio Code.

    We can't cover all the possible environments here, but we recommend using the coding tools you are already familiar with and adding a Clojure plugin. The best code editor is the one you enjoy using. As long as there is a Clojure plugin for it, you should be up and running in no time.

    Installing the Code Bundle

    Download the code files from GitHub at https://packt.live/2vbksal. Refer to these code files for the complete code bundle.

    If you have any issues or questions about installation, please email us at workshops@packt.com.

    The high-quality color images used in book can be found at https://packt.live/2O5EzNX

    1. Hello REPL!

    Overview

    In this chapter, we explain the basics of creating Clojure programs. We start by getting you familiar with the Read Eval Print Loop (REPL), where most of the experimentation happens when writing code. The REPL also allows you to explore code and documentation by yourself, so it is an excellent place to start. After the quick dive in the REPL, we describe in more detail how to read and understand simple Lisp and Clojure code, which syntax can sometimes appear unsettling. We then explore fundamental operators and functions in Clojure, which enable you to write and run simple Clojure programs or scripts.

    By the end of this chapter, you will be able to use the REPL and work with functions in Clojure.

    Introduction

    Have you ever ended up entangled in the spaghetti code of an object-oriented application? Many experienced programmers would say yes, and at some point in their journey or career would reconsider the foundation of their programs. They might look for a simpler, better alternative to object-oriented programming, and Clojure is an appealing choice. It is a functional, concise, and elegant language of the Lisp family. Its core is small, and its syntax minimal. It shines because of its simplicity, which takes a trained eye to notice and ultimately understand. Employing Clojure's more sophisticated building blocks will allow you to design and build sturdier applications.

    Whether you are a seasoned programmer or a novice, hobbyist or professional, C# wizard or Haskell ninja, learning a new programming language is challenging. It is, however, a highly rewarding experience that will make you an overall better programmer. In this book, you will learn by doing and will ramp up your skills quickly.

    Clojure is an excellent choice of programming language to learn today. It will allow you to work efficiently using a technology built to last. Clojure can be used to program pretty much anything: from full-blown client-server applications to simple scripts or big data processing jobs. By the end of this book, you will have written a modern web application using Clojure and ClojureScript and will have all the cards in your hand to start writing your own!

    REPL Basics

    Welcome to the Clojure Read Eval Print Loop (REPL), a command-line interface that we can use to interact with a running Clojure program. REPL, in the sense that it reads the user's input (where the user is you, the programmer), evaluates the input by instantly compiling and executing the code, and prints (that is, displays) the result to the user. The read-eval-print three-step process repeats over and over again (loop) until you exit the program.

    The dynamism provided by the REPL allows you to discover and experiment with a tight feedback loop: your code is evaluated instantly, and you can adjust it until you get it right. Many other programming languages provide interactive shells (notably, other dynamic languages such as Ruby or Python), but in Clojure, the REPL plays an exceptional and essential role in the life of the developer. It is often integrated with the code editor and the line between editing, browsing, and executing code blurs toward a malleable development environment similar to Smalltalk. But let's start with the basics.

    Throughout these exercises, you may notice some mentions of Java (for example, in the stack trace in the second exercise). This is because Clojure is implemented in Java and runs in the Java Virtual Machine (JVM). Clojure can, therefore, benefit from a mature ecosystem (a battle-tested, widely deployed execution platform and a plethora of libraries) while still being a cutting-edge technology. Clojure is designed to be a hosted language, and another implementation, called ClojureScript, allows you to execute Clojure code on any JavaScript runtime (for example, a web browser or Node.js). This hosted-language implementation choice allows for a smaller community of functional programmers to strive in an industry dominated by Java, .NET Core, and JavaScript technologies. Welcome to the Clojure party, where we're all having our cake and eating it too.

    Exercise 1.01: Your First Dance

    In this exercise, we will perform some basic operations in the REPL. Let's get started:

    Open Terminal and type clj. This will start a Clojure REPL:

    $ clj

    The output is as follows:

    Clojure 1.10.1

    user=>

    The first line is your version of Clojure, which in this example is 1.10.1. Don't worry if your version is different—the exercises we will go through together should be compatible with any version of Clojure.

    The second line displays the namespace we are currently in (user) and prompts for your input. A namespace is a group of things (such as functions) that belong together. Everything you create here will be in the user namespace by default. The user namespace can be considered your playground.

    Your REPL is ready to read.

    Let's try to evaluate an expression:

    user=> Hello REPL!

    The output is as follows:

    Hello REPL!

    In Clojure, literal strings are created with double quotes, . A literal is a notation for representing a fixed value in source code.

    Let's see what happens if we type in multiple strings:

    user=> Hello Again

    The output is as follows:

    Hello

    Again

    We have just evaluated two expressions sequentially, and each result is printed onto separate lines.

    Now, let's try a bit of arithmetic, for example, 1 + 2:

    user=> 1 + 2

    The output is as follows:

    1

    #object[clojure.core$_PLUS_ 0xe8df99a clojure.core$_PLUS_@e8df99a]

    2

    The output is not exactly what we expected. Clojure evaluated the three components, that is, 1, +, and 2, separately. Evaluating + looks strange because the + symbol is bound to a function.

    Note

    A function is a unit of code that performs a specific task. We don't need to know more for now except that functions can be called (or invoked) and can take some parameters. A function's argument is a term that's used to design the value of a parameter, but those terms are often used interchangeably.

    To add those numbers, we need to call the + function with the arguments 1 and 2.

    Call the + function with the arguments 1 and 2 as follows:

    user=> (+ 1 2)

    The output is as follows:

    3

    You will soon discover that many basic operations that are usually part of a programming language syntax, such as addition, multiplication, comparison, and so on, are just simple functions in Clojure.

    Let's try a few more examples of basic arithmetic. You can even try to pass more than two arguments to the following functions, so adding 1 + 2 + 3 together would look like (+ 1 2 3):

    user=> (+ 1 2 3)

    6

    The other basic arithmetic operators are used in a similar way. Try and type the following expressions:

    user=> (- 3 2)

    1

    user=> (* 3 4 1)

    12

    user=> (/ 9 3)

    3

    After typing in the preceding examples, you should try a few more by yourself – the REPL is here to be experimented with.

    You should now be familiar enough with the REPL to ask the following question:

    user=> (println Would you like to dance?)

    Would you like to dance?

    nil

    Don't take it personally – nil was the value that was returned by the println function. The text that was printed by the function was merely a side effect of this function.

    nil is the Clojure equivalent of null, or nothing; that is, the absence of meaningful value. print (without a new line) and println (with a new line) are used to print objects to the standard output, and they return nil once they are done.

    Now, we can combine those operations and print the result of a simple addition:

    user=> (println (+ 1 2))

    3

    nil

    A value of 3 was printed and the value of nil was returned by this expression.

    Notice how we have nested those forms (or expressions). This is how we chain functions in Clojure:

    user=> (* 2 (+ 1 2))

    6

    Exit the REPL by pressing Ctrl + D. The function to exit is System/exit, which takes the exit code as a parameter. Therefore, you can also type the following:

    user=> (System/exit 0)

    In this exercise, we discovered the REPL and called Clojure functions to print and perform basic arithmetic operations.

    Exercise 1.02: Getting around in the REPL

    In this exercise, we will introduce a few navigational key bindings and commands to help you use and survive the REPL. Let's get started:

    Start by opening the REPL again.

    Notice how you can navigate the history of what was typed earlier and in previous sessions by pressing Ctrl + P (or the UP arrow) and Ctrl + N (or the DOWN arrow).

    You can also search (case-sensitive) through the history of the commands you have entered: press Ctrl + R and then Hello, which should bring back the HelloAgain expression we typed earlier. If you press Ctrl + R a second time, it will cycle through the matches of the search and bring back the very first command: Hello REPL!. If you press Enter, it will bring the expression back to the current prompt. Press Enter again and it will evaluate it.

    Now, evaluate the following expression, which increments (adds 1 to) the number 10:

    user=> (inc 10)

    11

    The returned value is 11, which is indeed 10 + 1.

    *1 is a special variable that is bound to the result of the last expression that was evaluated in the REPL. You can evaluate its value by simply typing it like this:

    user=> *1

    11

    Similarly, *2 and *3 are variables bound to the second and third most recent values of that REPL session, respectively.

    You can also reuse those special variable values within other expressions. See if you can follow and type this sequence of commands:

    user=> (inc 10)

    11

    user=> *1

    11

    user=> (inc *1)

    12

    user=> (inc *1)

    13

    user=> (inc *2)

    13

    user=> (inc *1)

    14

    Notice how the values of *1 and *2 change as new expressions are evaluated. When the REPL is crowded with text, press Ctrl + L to clear the screen.

    Another useful variable that's available in the REPL is *e, which contains the result of the last exception. At the moment, it should be nil unless you generated an error earlier. Let's trigger an exception voluntarily by dividing by zero:

    user=> (/ 1 0)

    Execution error (ArithmeticException) at user/eval71 (REPL:1).

    Divide by zero

    Evaluating *e should contain details about the exception, including the stack trace:

    user=> *e

    #error {

     :cause Divide by zero

     :via

     [{:type java.lang.ArithmeticException

       :message Divide by zero

       :at [clojure.lang.Numbers divide Numbers.java 188]}]

     :trace

     [[clojure.lang.Numbers divide Numbers.java 188]

      [clojure.lang.Numbers divide Numbers.java 3901]

      [user$eval1 invokeStatic NO_SOURCE_FILE 1]

      [user$eval1 invoke NO_SOURCE_FILE 1]

      [clojure.lang.Compiler eval Compiler.java 7177]

      [clojure.lang.Compiler eval Compiler.java 7132]

      [clojure.core$eval invokeStatic core.clj 3214]

      [clojure.core$eval invoke core.clj 3210]

      [clojure.main$repl$read_eval_print__9086$fn__9089 invoke main.clj 437]

      [clojure.main$repl$read_eval_print__9086 invoke main.clj 437]

      [clojure.main$repl$fn__9095 invoke main.clj 458]

      [clojure.main$repl invokeStatic main.clj 458]

      [clojure.main$repl_opt invokeStatic main.clj 522]

      [clojure.main$main invokeStatic main.clj 667]

      [clojure.main$main doInvoke main.clj 616]

      [clojure.lang.RestFn invoke RestFn.java 397]

      [clojure.lang.AFn applyToHelper AFn.java 152]

      [clojure.lang.RestFn applyTo RestFn.java 132]

      [clojure.lang.Var applyTo Var.java 705]

      [clojure.main main main.java 40]]}

    Note

    Different Clojure implementations may have a slightly different behavior. For example, if you tried to divide by 0 in a ClojureScript REPL, it will not throw an exception and instead return the infinity value:

    cljs.user=> (/ 1 0)

    ##Inf

    This is to stay consistent with the host platform: the literal number 0 is implemented as an integer in Java (and Clojure) but as a floating-point number in JavaScript (and ClojureScript). The IEEE Standard for Floating-Point Arithmetic (IEEE 754) specifies that division by 0 should return +/- infinity.

    The doc, find-doc, and apropos functions are essential REPL tools for browsing through documentation. Given that you know the name of the function you want to use, you can read its documentation with doc. Let's see how it works in practice. Start by typing (doc str) to read more about the str function:

    user=> (doc str)

    -------------------------

    clojure.core/str

    ([] [x] [x & ys])

      With no args, returns the empty string. With one arg x, returns

      x.toString().  (str nil) returns the empty string. With more than

      one arg, returns the concatenation of the str values of the args.

    nil

    doc prints the fully qualified name of the function (including the namespace) on the first line, the possible sets of parameters (or arities) on the next line, and finally the description.

    This function's fully qualified name is clojure.core/str, which means that it is in the clojure.core namespace. Things defined in clojure.core are available to your current namespace by default, without you explicitly having to require them. This is because they are fundamental components for building your programs, and it would be tedious to have to use their full name every time.

    Let's try to use the str function. As the documentation explains, we can pass it multiple arguments:

    user=> (str I will be concatenated) (clojure.core/str This works too)

    Iwillbeconcatenated

    This works too

    Let's inspect the documentation of the doc function:

    user=> (doc doc)

    -------------------------

    clojure.repl/doc

    ([name])

    Macro

      Prints documentation for a var or special form given its name,

       or for a spec if given a keyword

    nil

    This function is in the clojure.repl namespace, which is also available by default in your REPL environment.

    You can also look at the documentation of a namespace. As its documentation suggests, your final program would typically not use the helpers in the clojure.repl namespace (for instance, doc, find-doc, and apropos):

    user=> (doc clojure.repl)

    -------------------------

    clojure.repl

      Utilities meant to be used interactively at the REPL

    nil

    When you don't know the name of the function, but you have an idea of what the description or name may contain, you can search for it with the find-doc helper. Let's try and search for the modulus operator:

    user=> (find-doc modulus)

    nil

    No luck, but there's a catch: find-doc is case-sensitive, but the good news is that we can use a regular expression with the i modifier to ignore the case:

    user=> (find-doc #(?i)modulus)

    -------------------------

    clojure.core/mod

    ([num div])

      Modulus of num and div. Truncates toward negative infinity.

    nil

    You don't need to know more about regular expressions for now – you don't even have to use them, but it can be useful to ignore the case when searching for a function. You can write them with the #(?i)text syntax, where text is anything you want to search for.

    The function we were looking for was clojure.core/mod.

    Let's make sure it works according to its documentation:

    user=> (mod 7 3)

    1

    Use the apropos function to search for functions by name, thereby yielding a more succinct output. Say we were looking for a function that transforms the case of a given string of characters:

    user=> (apropos case)

    (clojure.core/case clojure.string/lower-case clojure.string/upper-case)

    user=> (clojure.string/upper-case Shout, shout, let it all out)

    SHOUT, SHOUT, LET IT ALL OUT

    Please note that this function is in the clojure.string namespace, which is not referred to by default. You will need to use its full name until we learn how to import and refer symbols from other namespaces.

    Activity 1.01: Performing Basic Operations

    In this activity, we will print messages and perform some basic arithmetic operations in the Clojure REPL.

    These steps will help you complete this activity:

    Open the REPL.

    Print the message I am not afraid of parentheses to motivate yourself.

    Add 1, 2, and 3 and multiply the result by 10 minus 3, which corresponds to the following infix notation: (1 + 2 + 3) * (10 - 3). You should obtain the following result:

    42

    Print the message Well done! to congratulate yourself.

    Exit the REPL.

    Note

    The solution for this activity can be found via this link.

    Evaluation of Clojure Code

    Clojure is a dialect of Lisp, a high-level programming language that was designed by John McCarthy and first appeared in 1958. One of the most distinctive features of Lisp and its derivatives, or dialects, is the use of data structures to write the source code of programs. The unusual number of parentheses in our Clojure programs is a manifestation of this as parentheses are used to create lists.

    Here, we will focus on the building blocks of Clojure programs, that is, forms and expressions, and briefly look at how expressions are evaluated.

    Note

    The terms expression and form are often used interchangeably; however, according to the Clojure documentation, an expression is a form type: Every form not handled specially by a special form or macro is considered by the compiler to be an expression, which is evaluated to yield a value.

    We have seen how literals are valid syntax and evaluate to themselves, for example:

    user=> Hello

    Hello

    user=> 1 2 3

    1

    2

    3

    We have also learned how to invoke functions by using parentheses:

    user=> (+ 1 2 3)

    6

    It is worth noting at this point that comments can be written with ; at the beginning of a line. Any line starting with ; will not be evaluated:

    user=> ; This is a comment

    user=> ; This line is not evaluated

    Functions are invoked according to the following structure:

    ; (operator operand-1 operand-2 operand-3 …)

    ; for example:

    user=> (* 2 3 4)

    24

    Take note of the following from the preceding example:

    The list, denoted by opening and closing parenthesis, (), is evaluated to a function call (or invocation).

    When evaluated, the * symbol resolves to the function that implements the multiplication.

    2, 3, and 4 are evaluated to themselves and passed as arguments to the function.

    Consider the expression you wrote in Activity 1.01, Performing Basic Operations: (* (+ 1 2 3) (- 10 3)). It can also help to visualize the expression as a tree:

    Figure 1.1: Tree representation of the expression, (* (+ 1 2 3) (- 10 3))

    Figure 1.1: Tree representation of the expression, (* (+ 1 2 3) (- 10 3))

    Evaluating this expression consists of reducing the tree, starting with the offshoots (the innermost lists): (* (+ 1 2 3) (- 10 3)) becomes (* 6 7), which becomes 42.

    The term s-expression (or symbolic expression) is often used to designate those types of expressions. You may come across it again, so it is good to know that an s-expression is a data notation for writing data structures and code with lists, as we demonstrated previously.

    So far, we have only used literal scalar types as operands to our operators, which hold one value, such as numbers, strings, Booleans, and so on. We've only used lists to invoke functions and not to represent data. Let's try to create a list that represents data but not code:

    user=> (1 2 3)

    Execution error (ClassCastException) at user/eval255 (REPL:1).

    java.lang.Long cannot be cast to clojure.lang.IFn

    An exception was thrown because the first item of the list (the operator) was not a function.

    There is a special syntax to prevent the list from being considered as the invocation of a function: the quote. Creating a literal list is done by adding a quotation ', in front of it, so let's try again:

    user=> '(1 2 3)

    (1 2 3)

    user=> '(a b c d)

    (a b c d)

    Great! By preventing the evaluation of the form, we can now write a literal representation of lists.

    This concept will help us get ready for what we are going to cover next. It is, however, fascinating to notice at this point that Clojure code is made up of data structures, and our programs can generate those same data structures. Code is data is a famous saying in the Lisp world, and a powerful concept that allows your program to generate code (known as meta-programming). If you are new to this concept, it is worth pausing for a minute to think and admire the sheer beauty of it. We will explain meta-programming techniques in detail later when explaining macros in Chapter 11, Macros.

    Basic Special Forms

    So far, we have been writing code that complies with the simplest rules of evaluating Clojure code, but there are some behaviors that cannot simply be encoded with normal functions. For example, arguments that have been passed to a function will always be resolved or evaluated, but what if we do not want to evaluate all the operands of an operator? That is when special forms come into play. They can have different evaluation rules for functions when the source code is read by Clojure. For example, the special form if, may not evaluate one of its arguments, depending on the result of the first argument.

    There are a few other special forms that we will go through in this section:

    when, which can be used when we are only interested in the case of a condition being truthy (a value is truthy when considered true in the context of a Boolean expression).

    do, which can be used to execute a series of expressions and return the value of the last expression.

    def and let, which are special forms that are used to create global and local bindings.

    fn and defn, which are special forms that are used to create functions.

    All these special forms have special evaluation rules, all of which we will discover by working through the following three exercises.

    Exercise 1.03: Working with if, do, and when

    In this exercise, we will evaluate expressions using the if, do, and when forms. Let's get started:

    Start your REPL and type in the following expression:

    user=> (if true Yes No)

    Yes

    The special form if, evaluates its first argument. If its value is truthful, it will evaluate argument 2, otherwise (else), it will evaluate argument 3. It will never evaluate both arguments 2 and 3.

    We can nest expressions and start doing more interesting things:

    user=> (if false (+ 3 4) (rand))

    0.4833142431072903

    In this case, the computation of (+ 3 4) will not be executed, and only a random number (between 0 and 1) will be returned by the rand function.

    But what if we wanted to do more than one thing in our branch of the condition? We could wrap our operation with do. Let's see how do works:

    user=> (doc do)

    -------------------------

    do

      (do exprs*)

    Special Form

      Evaluates the expressions in order and returns the value of

      the last. If no expressions are supplied, returns nil.

      Please see http://clojure.org/special_forms#do

      Evaluates the expressions in order and returns the value of

      the last. If no expressions are supplied, returns nil.

    nil

    To use the special form, do type the following expression:

    user=> (do (* 3 4) (/ 8 4) (+ 1 1))

    2

    All the expressions before the final (+ 1 1) expression were evaluated, but only the value of the last one is returned. This does not look very useful with expressions that don't alter the state of the world, and so it would typically be used for side effects such as logging or any other kind of I/O (filesystem access, database query, network request, and so on).

    You don't have to take my word for it, so let's experiment with the side effect of printing to the Terminal:

    user=> (do (println A proof that this is executed) (println And this too))

    A proof that this is executed

    And this too

    nil

    Finally, we can combine the use of if and do to execute multiple operations in a conditional branching:

    user=> (if true (do (println Calculating a random number...) (rand)) (+ 1 2))

    Calculating a random number...

    0.8340057877906916

    Technically, you could also omit the third argument. Bring back the previous expression in the REPL and remove the last expression, that is, (+ 1 2):

    user=> (if true (do (println Calculating a random number...) (rand)))

    Calculating a random number...

    0.5451384920081613

    user=> (if false (println Not going to happen))

    nil

    We have a better construct available for this case: the when operator. Instead of combining if and do, when you are only interested in doing work in one branch of the conditional execution, use when.

    Type the following expression to use when instead of a combination of if and do:

    user=> (when true (println First argument) (println Second argument) And the last is returned)

    First argument

    Second argument

    And the last is returned

    By completing this exercise, we have demonstrated the usage of the special forms known as if, do, and when. We can now write expressions that contain multiple statements, as well as conditional expressions.

    Bindings

    In Clojure, we use the term bindings rather than variables and assignments because we tend to bind a value to a symbol only once. Under the hood, Clojure creates variables and so you may encounter this term, but it would be preferable if you don't think of them as classic variables or values that can change. We won't use the term variable anymore in this chapter as it can be confusing. You can use def to define global bindings and let for local bindings.

    Exercise 1.04: Using def and let

    In this exercise, we will demonstrate the usage of the def and let keywords, which are used to create bindings. Let's get started:

    The special form def allows you to bind a value to a symbol. In the REPL, type the following expression to bind the value 10 to the x symbol:

    user=> (def x 10)

    #'user/x

    Note

    When the REPL returns #'user/x, it is returning a reference to the var you have just created. The user part indicates the namespace where the var is defined. The #' prefix is a way of quoting the var so that we see the symbol and not the value of the symbol.

    Evaluate the expression, x, which will resolve the x symbol to its value:

    user=> x

    10

    Technically, you can change the binding, which is fine when experimenting in the REPL:

    user=> (def x 20)

    #'user/x

    user=> x

    20

    It is, however, not recommended in your programs because it can make it hard to read and complicate its maintenance. For now, it would be better if you just consider such a binding as a constant.

    You can use the x symbol within another expression:

    user=> (inc x)

    21

    user=> x

    20

    Wherever def is invoked, it will bind the value to the symbol in the current namespace. We could try to define a local binding in a do block and see what happens:

    user=> x

    20

    user=> (do (def x 42))

    #'user/x

    user=> x

    42

    The bindings that are created by def have an indefinite scope (or dynamic scope) and can be considered as global. They are automatically namespaced, which is a useful trait to avoid clashing with existing names.

    If we want to have a binding available only to a local scope or lexical scope, we can use the special form let. Type the following expression to create a lexical binding of the y symbol:

    user=> (let [y 3] (println y) (* 10 y))

    3

    30

    let takes a vector as a parameter to create the local bindings, and then a series of expressions that will be evaluated like they are in a do block.

    Note

    A vector is similar to a list, in the sense that they both are a sequential collection of values. Their underlying data structure is different, and we will shed light on this in Chapter 2, Data Types and Immutability. For now, you just need to know that vectors can be created with square brackets, for example, [1 2 3 4].

    Evaluate the y symbol:

    user=> y

    Syntax error compiling at (REPL:0:0).

    Unable to resolve symbol: y in this context

    An error is thrown, that is, Unable to resolve symbol: y in this context, because we are now outside of the let block.

    Type the following expression to create a lexical binding of x to the value 3, and see how it affects the indefinite (global) binding of x that we created in step 4:

    user=> (let [x 3] (println x))

    3

    nil

    user=> x

    42

    Printing x yields the value 3, which means that the global x symbol was temporarily overridden or shadowed by the lexical context in which println was invoked.

    You can create multiple local bindings at once with let by passing an even number of items in the vector. Type the following expression to bind x to 10 and y to 20:

    user=> (let [x 10 y 20]  (str x is x and y is y))

    x is 10 and y is 20

    Combine the concepts of this section and write the following expressions:

    user=> (def message Let's add them all!)

    #'user/message

    user=> (let [x (* 10 3)

                 y 20

                 z 100]

                  (println message)

                  (+ x y z))

    Let's add them all!

    150

    The expression spans over multiple lines to improve readability.

    Exercise 1.05: Creating Simple Functions with fn and defn

    The special form that's used to define functions is fn. Let's jump right into it by creating our first function:

    Type the following expression in your REPL:

    user=> (fn [])

    #object[user$eval196$fn__197 0x3f0846c6 user$eval196$fn__197@3f0846c6]

    We have just created the simplest anonymous function, which takes no parameters and does nothing, and we returned an object, which is our function with no name.

    Create a function that takes a parameter named x and return its square value (multiply it by itself):

    user=> (fn [x] (* x x))

    #object[user$eval227$fn__228 0x68b6f0d6 user$eval227$fn__228@68b6f0d6]

    Remember that, in Clojure, the first item of an expression will be invoked, so we can call our anonymous function by wrapping it with parentheses and providing an argument as the second item of the expression:

    user=> ((fn [x] (* x x)) 2)

    4

    Now this is great, but not very convenient. If we wanted our function to be reusable or testable, it would be better for it to have a name. We can create a symbol in the namespace and bind it to the function.

    Use def to bind the function returned by the special form, fn, to the square symbol:

    user=> (def square (fn [x] (* x x)))

    #'user/square

    Invoke your newly created function to make sure that it works:

    user=> (square 2)

    4

    user=> (square *1)

    16

    user=> (square *1)

    256

    This pattern of combining def and fn is so common that a built-in macro was born out of necessity: defn. Recreate the square function with defn instead of def and fn:

    user=> (defn square [x] (* x x))

    #'user/square

    user=> (square 10)

    100

    Did you notice that the x argument was passed in a vector? We have already learned that vectors are collections, and so we can add more than one symbol to the argument's vector. The values that are passed when calling the function will be bound to the symbols provided in the vector during the function's definition.

    Functions can take multiple arguments, and their bodies can be composed of multiple expressions (such as an implicit do block). Create a function with the name meditate that takes two arguments: a string, s, and a Boolean, calm. The function will print an introductory message and return a transformation of s based on calm:

    user=>

    (defn meditate [s calm]

      (println Clojure Meditate v1.0)

      (if calm

        (clojure.string/capitalize s)

        (str (clojure.string/upper-case s) !)))

    Note

    Editing multiline expressions in the REPL can be cumbersome. As we start creating lengthier functions and expressions that span multiple lines, it would be preferable to have a window of your favorite editor open next to your REPL window. Keep those windows side by side, edit the code in your editor, copy it to your clipboard, and paste it into your REPL.

    The function body contains two main expressions, the first of which is a side effect with println and the second of which is the if block, which will determine the return value. If calm is true, it will politely return the string capitalized (with the first character converted into uppercase), otherwise it will shout and return the string with all its characters to uppercase, ending with an exclamation mark.

    Let's try and make sure that our function works as intended:

    user=> (meditate in calmness lies true pleasure true)

    Clojure Meditate v1.0

    In calmness lies true pleasure

    user=> (meditate in calmness lies true pleasure false)

    Clojure Meditate v1.0

    IN CALMNESS LIES TRUE PLEASURE!

    If we call the function with only the first parameter, it will throw an exception. This is because the parameters that we have defined are required:

    user=> (meditate in calmness lies true pleasure)

    Execution error (ArityException) at user/eval365 (REPL:1).

    Wrong number of args (1) passed to: user/meditate

    One last thing to end our initial tour of these functions is the doc-string parameter. When provided to defn, it will allow you to add a description of your function.

    Add documentation to your square function by adding a doc-string just before the function arguments:

    user=>

    (defn square

      Returns the product of the number `x` with itself

      [x]

      (* x x))

    #'user/square

    The doc-string is not only useful when browsing a project's source code – it also makes it available to the doc function.

    Look up the documentation of your square function with doc:

    user=> (doc square)

    -------------------------

    user/square

    ([x])

      Returns the product of the number `x` with itself

    nil

    It is important to remember that the doc-string needs to come before the function arguments. If it comes after, the string will be evaluated sequentially as part of the function body and won't throw an error. It is valid syntax, but it will not be available in the doc helper and other development tools.

    It is good practice to document the arguments with backticks, `, like we did with `x`, so that development tools (such as the IDE) can recognize them.

    We will take a deeper dive into functions in Chapter 3, Functions in Depth, but these few basic principles will get you a long way in terms of writing functions.

    Activity 1.02: Predicting the Atmospheric Carbon Dioxide Level

    Carbon dioxide (CO2) is an important heat-trapping (greenhouse) gas, currently rising and threatening life as we know it on our planet. We would like to predict future levels of CO2 in the atmosphere based on historical data provided by National Oceanic and Atmospheric Administration (NOAA):

    Figure 1.2: CO2 parts per million (ppm) over the years

    Figure 1.2: CO2 parts per million (ppm) over the years

    Note

    The preceding chart was taken from https://packt.live/35kUI7L and the data was taken from NOAA.

    We will use the year 2006 as a starting point with a CO2 level of 382 ppm and calculate the estimate using a simplified (and optimistic) linear function, as follows: Estimate = 382 + ((Year - 2006) * 2).

    Create a function called co2-estimate that takes one integer parameter called year and returns the estimated level of CO2 ppm for that year.

    These steps will help you complete this activity:

    Open your favorite editor and a REPL window next to it.

    In your editor, define two constants, base-co2 and base-year, with the values 382 and 2006, respectively.

    In

    Enjoying the preview?
    Page 1 of 1