Clojure Programming Fundamentals: A Concise Guidebook
By Ted Noreux
()
About this ebook
Unlock the power of functional programming with "Clojure Programming Fundamentals: A Concise Guidebook", your essential guide to mastering the Clojure language. Whether you're new to programming or an experienced developer looking to expand your skills, this book offers a comprehensive journey through Clojure's core concepts, practical applications, and its vibrant ecosystem.
Dive into the world of Clojure with clear, meticulous explanations that cover everything from setting up your development environment to building sophisticated web applications. Explore Clojure's innovative approaches to data structures, functional programming, concurrency, and parallelism. Learn how to manage state and identity in your applications, handle errors effectively, and debug with ease. Discover the seamless interoperability between Clojure and Java, and leverage the vast Java ecosystem within your Clojure projects.
Through practical examples, expert insights, and focused tutorials, "Clojure Programming Fundamentals" empowers you to write efficient, elegant code that taps into the full potential of Clojure. Embrace the functional programming paradigm, and embark on a journey to becoming a proficient Clojure developer, ready to tackle the challenges of modern software development with confidence and creativity.
Read more from Ted Noreux
Mastering Database Design Rating: 0 out of 5 stars0 ratingsElixir Programming Fundamentals: A Concise Guidebook Rating: 0 out of 5 stars0 ratingsDevOps Engineer's Guidebook: Essential Techniques Rating: 0 out of 5 stars0 ratingsStochastic Calculus for Finance Rating: 0 out of 5 stars0 ratings
Related to Clojure Programming Fundamentals
Related ebooks
Learn ClojureScript: Functional programming for the web Rating: 0 out of 5 stars0 ratingsLearning ClojureScript Rating: 0 out of 5 stars0 ratingsClojure for Java Developers Rating: 0 out of 5 stars0 ratingsMastering Clojure Rating: 0 out of 5 stars0 ratingsThe Struts Framework: Practical Guide for Java Programmers Rating: 0 out of 5 stars0 ratingsClojure Reactive Programming Rating: 0 out of 5 stars0 ratingsKotlin In-Depth [Vol-I]: A Comprehensive Guide to Modern Multi-Paradigm Language Rating: 0 out of 5 stars0 ratingsSelenium Framework Design in Keyword-Driven Testing: Automate Your Test Using Selenium and Appium Rating: 0 out of 5 stars0 ratingsMastering Computer Programming: A Comprehensive Guide Rating: 0 out of 5 stars0 ratingsClojure High Performance Programming - Second Edition Rating: 0 out of 5 stars0 ratingsKotlin at a Glance: Use of Lambdas and higher-order functions to write more concise, clean, reusable, and simple code Rating: 0 out of 5 stars0 ratingsJava for Beginners: A Crash Course to Learn Java Programming in 1 Week Rating: 5 out of 5 stars5/5Building Kotlin Applications: A comprehensive guide for Android, Web, and Server-Side Development (English Edition) Rating: 0 out of 5 stars0 ratingsSoftware Engineering & Object Oriented Modeling Rating: 0 out of 5 stars0 ratingsPro Oracle SQL Development: Best Practices for Writing Advanced Queries Rating: 0 out of 5 stars0 ratings(Part 2) Java 4 Selenium WebDriver: Come Learn How To Program For Automation Testing Rating: 0 out of 5 stars0 ratingsBeginning MLOps with MLFlow: Deploy Models in AWS SageMaker, Google Cloud, and Microsoft Azure Rating: 0 out of 5 stars0 ratingsDataflow and Reactive Programming Systems Rating: 0 out of 5 stars0 ratingsBeginning Java EE 7 Rating: 4 out of 5 stars4/5ORACLE PL/SQL Interview Questions You'll Most Likely Be Asked Rating: 5 out of 5 stars5/5Pro Java Microservices with Quarkus and Kubernetes: A Hands-on Guide Rating: 0 out of 5 stars0 ratingsJava™ Programming: A Complete Project Lifecycle Guide Rating: 0 out of 5 stars0 ratingsMastering C: A Comprehensive Guide to Programming Excellence Rating: 0 out of 5 stars0 ratingsPractical Programming 6 in 1: Python Machine Learning, JavaScript, React 17, And Angular With Typescript Rating: 0 out of 5 stars0 ratings
Programming For You
Grokking Algorithms: An illustrated guide for programmers and other curious people Rating: 4 out of 5 stars4/5SQL: For Beginners: Your Guide To Easily Learn SQL Programming in 7 Days Rating: 5 out of 5 stars5/5Coding All-in-One For Dummies Rating: 4 out of 5 stars4/5Learn to Code. Get a Job. The Ultimate Guide to Learning and Getting Hired as a Developer. Rating: 5 out of 5 stars5/5HTML & CSS: Learn the Fundaments in 7 Days Rating: 4 out of 5 stars4/5Python Programming : How to Code Python Fast In Just 24 Hours With 7 Simple Steps Rating: 4 out of 5 stars4/5SQL QuickStart Guide: The Simplified Beginner's Guide to Managing, Analyzing, and Manipulating Data With SQL Rating: 4 out of 5 stars4/5Python: Learn Python in 24 Hours Rating: 4 out of 5 stars4/5Python QuickStart Guide: The Simplified Beginner's Guide to Python Programming Using Hands-On Projects and Real-World Applications Rating: 0 out of 5 stars0 ratingsExcel : The Ultimate Comprehensive Step-By-Step Guide to the Basics of Excel Programming: 1 Rating: 5 out of 5 stars5/5Linux Command Line and Shell Scripting Bible Rating: 3 out of 5 stars3/5The Little SAS Book: A Primer, Sixth Edition Rating: 5 out of 5 stars5/5C++ Learn in 24 Hours Rating: 0 out of 5 stars0 ratingsC Programming for Beginners: Your Guide to Easily Learn C Programming In 7 Days Rating: 4 out of 5 stars4/5Linux: Learn in 24 Hours Rating: 5 out of 5 stars5/5PYTHON: Practical Python Programming For Beginners & Experts With Hands-on Project Rating: 5 out of 5 stars5/5Unreal Engine from Zero to Proficiency (Foundations): Unreal Engine from Zero to Proficiency, #1 Rating: 0 out of 5 stars0 ratingsPython: For Beginners A Crash Course Guide To Learn Python in 1 Week Rating: 4 out of 5 stars4/5Mastering Windows PowerShell Scripting Rating: 4 out of 5 stars4/5Programming Arduino: Getting Started with Sketches Rating: 4 out of 5 stars4/5Python Machine Learning By Example Rating: 4 out of 5 stars4/5SQL All-in-One For Dummies Rating: 3 out of 5 stars3/5
Reviews for Clojure Programming Fundamentals
0 ratings0 reviews
Book preview
Clojure Programming Fundamentals - Ted Noreux
Clojure Programming Fundamentals: A Concise Guidebook
Ted Noreux
Copyright © 2024 by Ted Noreux
All rights reserved. No part of this publication may be reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other electronic or mechanical methods, without the prior written permission of the publisher, except in the case of brief quotations embodied in critical reviews and certain other noncommercial uses permitted by copyright law.
Contents
1 Introduction to Clojure and Its Ecosystem
1.1 What is Clojure?
1.2 The Philosophy of Clojure
1.3 The Clojure Ecosystem: Overview of Tools and Libraries
1.4 Getting to Know the REPL
1.5 Basic Syntax and Notation
1.6 Data Types and Literals
1.7 Namespaces and Code Organization
1.8 Clojure’s Place in the JVM Ecosystem
1.9 Community and Resources for Learning
2 Getting Started with the Clojure Environment
2.1 Installing Clojure
2.2 Understanding the Clojure CLI Tools
2.3 Using the REPL for Interactive Development
2.4 Working with the Leiningen Build Tool
2.5 Project Structure and Dependencies
2.6 Writing Your First Clojure Program
2.7 Exploring Clojure Editors and IDEs
2.8 Running and Building Clojure Applications
2.9 Managing Dependencies with Clojure
2.10 ClojureScript: A Brief Introduction
2.11 Tips for Efficiently Working in the Clojure Environment
3 Core Data Structures and Their Operations
3.1 Immutable Data Structures: An Introduction
3.2 Lists and Their Operations
3.3 Vectors: Usage and Performance Characteristics
3.4 Maps: Creating and Manipulating Key-Value Pairs
3.5 Sets: Uniqueness and Set Operations
3.6 Sequences: Understanding Lazy Evaluation
3.7 Keywords and Symbols
3.8 Advanced Data Structures: Queues, Stacks, and Trees
3.9 Transforming Data with Transducers
3.10 Destructuring for Easier Data Manipulation
3.11 Understanding Equality and Comparison in Clojure
3.12 Persistent Data Structures: Behind the Scenes
4 Functional Programming in Clojure
4.1 Principles of Functional Programming
4.2 Pure Functions: Definition and Benefits
4.3 Higher-Order Functions and Their Uses
4.4 Function Composition and Chaining
4.5 Clojure’s Core Functional Programming Constructs
4.6 Recursion and Recursive Solutions
4.7 Immutability and State Management
4.8 Lambdas and Anonymous Functions
4.9 Closures: Capturing and Retaining State
4.10 Lazy Sequences and Their Applications
4.11 Functional Data Transformation with map, reduce, and filter
4.12 Using letfn and fn for Local and Anonymous Functions
5 Concurrency and Parallelism in Clojure
5.1 Understanding Concurrency and Parallelism
5.2 The Clojure Concurrency Model: An Overview
5.3 Atoms: Managing Mutable State Safely
5.4 Refs and Software Transactional Memory (STM)
5.5 Agents: Asynchronous State Management
5.6 Futures and Promises: Handling Asynchronous Operations
5.7 Core.async: Clojure’s Take on Communicating Sequential Processes (CSP)
5.8 Parallel Programming with pmap and reducers
5.9 Thread Management and Executors
5.10 Best Practices for Writing Concurrent Clojure Code
5.11 Debugging and Profiling Concurrent Applications
5.12 Real-world Use Cases for Concurrency and Parallelism in Clojure
6 State and Identity
6.1 Understanding State and Identity in Functional Programming
6.2 Immutability: Core Concepts and Benefits
6.3 Managing State in Clojure: An Overview
6.4 Atoms: Safe, Synchronous State Change
6.5 Refs: Coordinated, Transactional State Change
6.6 Agents: Independent, Asynchronous State Change
6.7 Vars: Dynamic Binding and Namespaces
6.8 Versioning Data with Clojure’s Immutable Data Structures
6.9 Time in Functional Programming: Temporal Coupling and its Management
6.10 Design Patterns for State Management in Clojure
6.11 Comparing State Management in Clojure with Other Programming Paradigms
6.12 Real-world Examples of State and Identity Management in Clojure Applications
7 Error Handling and Debugging
7.1 Error Handling in Clojure: An Overview
7.2 Understanding Clojure’s Exception Model
7.3 The try, catch, finally, and throw Constructs
7.4 Custom Exceptions and Error Types
7.5 Using ex-info and ex-data for Richer Error Information
7.6 Best Practices for Exception Handling in Clojure
7.7 Debugging Techniques in Clojure
7.8 Leveraging REPL for Debugging
7.9 Profiling Clojure Applications: Tools and Techniques
7.10 Logging in Clojure: Libraries and Best Practices
7.11 Unit Testing and Error Handling
7.12 Advanced Debugging: Analyzing JVM Threads and Memory
8 Interacting with Java from Clojure
8.1 Java Interoperability: An Introduction
8.2 Calling Java Methods from Clojure
8.3 Creating Java Objects in Clojure
8.4 Using Java Collections in Clojure
8.5 Implementing Java Interfaces in Clojure
8.6 Extending Java Classes with Clojure
8.7 Working with Java Threads in Clojure
8.8 Exception Handling with Java Exceptions
8.9 Accessing and Manipulating Java Fields
8.10 Using Java Annotations in Clojure
8.11 Building and Consuming Java Libraries
8.12 Performance Considerations for Java Interoperability
9 Building Web Applications with Clojure
9.1 Introduction to Web Development with Clojure
9.2 Setting Up a Clojure Web Development Environment
9.3 Understanding the Ring Spec
9.4 Building Web Applications with Compojure
9.5 Advanced Routing with Reitit
9.6 Creating RESTful Services
9.7 Integrating with Databases
9.8 Session Management and Security Practices
9.9 Frontend Development with ClojureScript
9.10 Deploying Clojure Web Applications
9.11 Performance Optimization for Clojure Web Apps
9.12 Testing Web Applications in Clojure
10 Testing and Quality Assurance in Clojure Projects
10.1 Introduction to Testing in Clojure
10.2 Setting Up a Testing Environment
10.3 Writing Unit Tests with clojure.test
10.4 Advanced Testing Techniques
10.5 Property-based Testing with test.check
10.6 Integration Testing Best Practices
10.7 Mocking and Stubbing in Tests
10.8 Continuous Integration for Clojure Projects
10.9 Code Coverage and Quality Analysis
10.10 Performance Testing and Benchmarking
10.11 Error Handling and Robustness Testing
10.12 Documentation and Readability in Testing
Introduction
This book, Clojure Programming Fundamentals: A Concise Guidebook,
is designed to serve as a comprehensive resource for individuals seeking to learn and master Clojure, a modern, dynamic, and functional programming language. The purpose of this book is to provide readers with a solid foundation in the principles of Clojure programming, covering both theoretical aspects and practical applications. Through a series of well-structured chapters, we aim to equip readers with the knowledge and skills necessary to build robust, efficient, and maintainable software applications using Clojure.
The content of this book is organized into several chapters, each focusing on a key area of Clojure programming. Starting with an introduction to Clojure and its ecosystem, we guide readers through setting up a Clojure development environment, understanding core data structures, embracing functional programming principles, and harnessing the power of concurrency and parallelism. Additionally, we cover essential topics such as state and identity management, error handling and debugging, interacting with Java, building web applications, and ensuring quality through testing. Each chapter is carefully crafted to provide detailed explanations, practical examples, and expert insights on effectively using Clojure’s features and libraries.
Intended for programmers who are new to Clojure as well as those with experience in other programming languages seeking to expand their functional programming capabilities, this book aims to be accessible to a wide audience. Whether readers are students, professionals, or hobbyists, this guidebook offers valuable content for anyone looking to explore the world of Clojure programming. By the end of this book, readers will have gained a comprehensive understanding of Clojure, enabling them to confidently tackle a wide range of programming challenges and contribute to the growing Clojure community.
In writing Clojure Programming Fundamentals: A Concise Guidebook,
we have strived to create a resource that not only teaches the basics of Clojure but also inspires readers to explore its vast potential. Our goal is to foster a deeper appreciation for the elegant simplicity of Clojure and the powerful paradigms of functional programming. Welcome to the world of Clojure programming—you are now equipped to embark on an enriching journey of learning and discovery.
Chapter 1
Introduction to Clojure and Its Ecosystem
Clojure is a dynamic, general-purpose programming language that emphasizes the functional programming paradigm and operates on the Java Virtual Machine (JVM). It offers a rich set of immutable, persistent data structures and facilitates powerful abstractions, enabling developers to write robust, efficient code. The language’s ecosystem is vibrant, with an array of tools, libraries, and frameworks designed to streamline development across various domains, including web development, data analysis, and systems programming. This chapter provides an overview of Clojure’s philosophy, syntax, and core concepts, alongside practical insights into navigating its ecosystem, setting up a development environment, and leveraging community resources for learning and problem-solving.
1.1
What is Clojure?
Clojure is a modern dialect of the Lisp programming language that operates on the Java Virtual Machine (JVM). As a functional programming language, Clojure places a strong emphasis on immutability and the manipulation of functions. This is reflective of its foundation on Lisp’s long-standing philosophy of code as data and data as code, allowing Clojure to harness the power of abstract syntax tree (AST) manipulations directly. Moreover, Clojure distinguishes itself through its sophisticated features tailored for concurrent programming, making it exceptionally well-suited for multi-threaded tasks.
A key characteristic of Clojure is its rich collection of immutable, persistent data structures. Unlike traditional structures that modify the original data in-place, immutable structures in Clojure do not allow direct alteration once created. Instead, any ’modification’ operation returns a new version of the structure that incorporates the change, leaving the original untouched. This behavior significantly simplifies reasoning about the program state, especially in concurrent applications. Examples of such structures include vectors, maps, sets, and lists, which are the bedrock for Clojure’s data handling capabilities.
The syncretism of Clojure on the JVM bestows it with a dual advantage. First, it inherits the robustness, portability, and extensive library ecosystem of Java. Clojure code can seamlessly interoperate with Java, thereby enabling developers to leverage the enormous collection of existing Java libraries. Second, it introduces the expressive power and succinctness typical of functional languages, facilitating the development of complex applications with fewer lines of code and enhanced readability.
Clojure’s syntax, while rooted in Lisp, is distinguished by its minimalist design. The syntax is inherently uniform, employing parentheses to group code into expressions and leveraging prefix notation for function calls. This simplicity does not detract from the language’s expressiveness. On the contrary, it promotes clarity and ease of understanding, particularly in defining functions and controlling program flow.
For beginners, the concept of REPL (Read-Eval-Print Loop) is central to understanding how Clojure development operates. The REPL facilitates an interactive programming environment where developers can instantly evaluate Clojure expressions, receive feedback, test functions, and iteratively develop their programs. This interactive cycle encourages experimentation and rapid prototyping, making the language accessible and engaging for newcomers.
Lastly, Clojure’s ecosystem comprises an array of tools, libraries, and frameworks designed to support development across various applications. Whether it is web development with interfaces like Ring and Compojure, or data analysis with libraries such as Incanter, Clojure offers a comprehensive suite for developers to embark on their projects. Moreover, tools like Leiningen and Boot simplify project management and build processes, while CIDER and Light Table provide powerful environments for writing and debugging Clojure code.
In summary, Clojure stands out as a functional, Lisp-inspired language that capitalizes on the JVM’s capabilities. Its emphasis on immutability, concurrent programming, and a robust ecosystem equips developers to tackle modern software challenges efficiently and effectively.
1.2
The Philosophy of Clojure
The philosophy of Clojure is deeply rooted in the principles of functional programming and simplicity. Developed by Rich Hickey, Clojure was designed with the goal to address the complexities that developers face with concurrent programming and to provide a robust platform for software development. The core philosophy encompasses simplicity, powerful abstractions, and immutability, all of which contribute to the language’s efficacy in creating efficient, scalable, and easy-to-maintain software.
Simplicity
In Clojure, simplicity refers to the ease with which developers can understand and apply concepts without unnecessary complications. The language achieves simplicity by reducing state changes, which are often the source of bugs in software development, especially in concurrent applications. Clojure encourages the use of immutable data structures and pure functions, which do not cause side effects. This principle of minimal side effects simplifies reasoning about programs and enhances their predictability and reliability.
Powerful Abstractions
Clojure is designed to provide high-level abstractions that facilitate expressing general patterns of problem-solving. Its support for first-class functions, higher-order functions, and powerful macros allows developers to write code that is not only concise but also expressive. These abstractions enable Clojure to provide a platform where common programming idioms can be efficiently represented, making the development process more efficient and the code more modular and reusable.
Immutability
Immutability is a cornerstone of Clojure’s philosophy. By default, Clojure’s core data structures are immutable, meaning once they are created, their state cannot be altered. This design decision is instrumental in making concurrent programming more manageable by eliminating the need for locks or complex synchronization mechanisms. Immutability encourages a functional approach to problem-solving, where transformations are applied to data structures to produce new versions instead of modifying them in place. This leads to more predictable, easier to debug, and inherently thread-safe code.
Emphasis on Concurrency
Clojure places a strong emphasis on building concurrent applications. It provides a rich set of abstractions, such as atoms, agents, and software transactional memory, to handle state changes in a concurrent environment safely. These tools make it easier for developers to write programs that can take advantage of multicore processors without the pitfalls of traditional thread-based concurrency models.
Hosted on the JVM
Clojure’s decision to operate on the Java Virtual Machine (JVM) brings several advantages, including portability, access to Java libraries, and performance benefits. By leveraging the JVM, Clojure programs can run on any platform that supports Java, allowing developers to utilize the extensive Java ecosystem. This includes the ability to interoperate with Java code, use Java libraries directly from Clojure, and benefit from the JVM’s performance optimizations and garbage collection.
The philosophy of Clojure is not merely about language design but also about providing a practical approach to software development. Its emphasis on simplicity, immutability, powerful abstractions, and concurrency aims to address the common challenges faced by developers, making it a compelling choice for those seeking to build robust, efficient, and maintainable applications.
1.3
The Clojure Ecosystem: Overview of Tools and Libraries
The Clojure ecosystem encompasses a wide range of tools and libraries that cater to various aspects of software development, from building web applications to processing big data. At its core, this ecosystem aims to provide Clojure developers with a robust set of resources that simplify coding tasks, enhance productivity, and foster innovation. This section delineates the essential tools and libraries within the Clojure landscape, helping developers to navigate and utilize this rich ecosystem effectively.
First and foremost, Leiningen stands out as a pivotal tool in the Clojure ecosystem. Acting as a build automation and project management tool, Leiningen simplifies the process of creating, managing, and deploying Clojure applications. It handles project dependencies, compilation, packaging, and various other tasks through a declarative configuration file named project.clj. To illustrate, creating a new Clojure project with Leiningen involves executing the following command in the terminal:
1 lein new app hello-world
This command generates a new Clojure project directory named hello-world with a predefined structure, including a project.clj file that describes the project’s configuration.
Another cornerstone of the Clojure ecosystem is the Clojure Integrated Development Environment (IDE) and editor support. Several IDEs and text editors offer specialized support for Clojure programming, including syntax highlighting, code completion, and REPL integration. For instance, CIDER enhances Emacs with Clojure and ClojureScript support, offering interactive programming features directly within the editor. Similarly, IntelliJ IDEA with the Cursive plugin provides an integrated environment for Clojure development, featuring smart code navigation, refactoring capabilities, and interactive debugging.
1 ; Example of Clojure code in an editor with syntax highlighting 2 ( defn greet [name] 3 ( str Hello ,
name !
) )
Following the development tools, the Clojure ecosystem is home to a plethora of libraries that address specific domains and challenges. For web development, libraries such as Ring and Compojure play crucial roles. Ring provides a simple, unified basis for web application development in Clojure, modeling HTTP requests and responses as Clojure maps. Compojure builds on Ring to offer concise routing syntax for defining web application routes:
1 ( defroutes app-routes 2 ( GET /
[] Welcome to Clojure Web Development
) 3 ( GET / hello /: name
[name] ( str Hello
name)))
For data analysis and manipulation, libraries like Clojure Data Analysis Cookbook and Incanter offer comprehensive tools for statistical analysis, data visualization, and machine learning. The following snippet demonstrates how Incanter can be used for generating a simple histogram:
1 ( use ’ ( incanter core stats charts)) 2 ( view (histogram (sample-normal 1000)))
Moreover, the Clojure ecosystem interlocks seamlessly with the Java ecosystem, thanks to Clojure’s design as a hosted language on the JVM. This compatibility allows Clojure developers to leverage Java libraries and frameworks directly, enhancing Clojure’s utility and versatility. For example, accessing a JDBC database can be easily achieved using the Clojure.java.jdbc library, bridging the world of Clojure with Java’s extensive ecosystem of database drivers and utilities.
To encapsulate, the Clojure ecosystem offers a rich landscape of tools and libraries that support a wide range of development activities. Whether it is project management with Leiningen, interactive development with sophisticated IDE support, or leveraging domain-specific libraries for web development and data analysis, Clojure’s ecosystem provides the necessary components for developers to build efficient, effective solutions. As the ecosystem continues to evolve, it remains a fertile ground for exploration, innovation, and productive software development.
1.4
Getting to Know the REPL
The Read-Eval-Print Loop (REPL) is integral to the Clojure development workflow. It provides an interactive environment where expressions are read, evaluated, and the results printed out, facilitating a rapid development and testing cycle. This section will elucidate the REPL’s operation and demonstrate its utility in Clojure programming.
Clojure’s REPL encourages experimentation, making it a powerful tool for learning and debugging. To initiate a REPL session, ensure Clojure is installed on your system. Launching the REPL can be achieved through the command line by simply typing clojure or lein repl if using Leiningen, a popular project management tool for Clojure.
Once the REPL starts, it waits for user input. A prompt indicates readiness to accept expressions. Let’s examine the basic workflow within a REPL session:
1. Read: The REPL reads the input provided by the user. 2. Eval: It evaluates the read expression according to Clojure’s semantics. 3. Print: The result of the evaluation is printed out for the user to see. 4. Loop: This process repeats, awaiting next input.
Consider the following simple example where we calculate the sum of two numbers:
1 (+ 2 3)
When this expression is entered into the REPL, the output will look like:
5
The REPL is not limited to simple arithmetic operations. It supports the execution of complex expressions, function definitions, and even the manipulation of data structures. For instance, defining a function to add two numbers can be done as follows:
1 ( defn add [a b] 2 (+ a b))
To call this function, you would input:
1 ( add 5 7)
The REPL would then output 12, indicating the function has been successfully defined and executed.
Understanding the REPL’s