Reader small image

You're reading from  The Clojure Workshop

Product typeBook
Published inJan 2020
Reading LevelBeginner
PublisherPackt
ISBN-139781838825485
Edition1st Edition
Languages
Right arrow
Authors (5):
Joseph Fahey
Joseph Fahey
author image
Joseph Fahey

Joseph Fahey has been a developer for nearly two decades. He got his start in the Digital Humanities in the early 2000s. Ever since then, he has been trying to hone his skills and expand his inventory of techniques. This lead him to Common Lisp and then to Clojure when it was first introduced. As an independent developer, Joseph was able to quickly start using Clojure professionally. These days, Joseph gets to write Clojure for his day job at Empear AB.
Read more about Joseph Fahey

Thomas Haratyk
Thomas Haratyk
author image
Thomas Haratyk

Thomas Haratyk graduated from Lille University of Science and Technology and has been a professional programmer for nine years. After studying computer science and starting his career in France, he is now working as a consultant in London, helping start-ups develop their products and scale their platforms with Clojure, Ruby, and modern JavaScript.
Read more about Thomas Haratyk

Scott McCaughie
Scott McCaughie
author image
Scott McCaughie

Scott McCaughie lives near Glasgow, Scotland where he works as a senior Clojure developer for Previse, a Fintech startup aiming to solve the problem of slow payments in the B2B space. Having graduated from Heriot-Watt University, his first 6 years were spent building out Risk and PnL systems for JP Morgan. A fortuitous offer of a role learning and writing Clojure came up and he jumped at the chance. 5 years of coding later and it's the best career decision he's made. In his spare time, Scott is an avid reader, enjoys behavioral psychology and financial independence podcasts, and keeps fit by commuting by bike, running, climbing, hill walking, snowboarding. You get the picture!
Read more about Scott McCaughie

Yehonathan Sharvit
Yehonathan Sharvit
author image
Yehonathan Sharvit

Yehonathan Sharvit has been a software developer since 2001. He discovered functional programming in 2009. It has profoundly changed his view of programming and his coding style. He loves to share his discoveries and his expertise. He has been giving courses on Clojure and JavaScript since 2016. He holds a master's degree in Mathematics.
Read more about Yehonathan Sharvit

Konrad Szydlo
Konrad Szydlo
author image
Konrad Szydlo

Konrad Szydlo is a psychology and computing graduate from Bournemouth University. He has worked with Clojure for the last 8 years. Since January 2016, he has worked as a software engineer and team leader at Retailic, responsible for building a website for the biggest royalty program in Poland. Prior to this, he worked as a developer with Sky, developing e-commerce and sports applications, where he used Ruby, Java, and PHP. He is also listed in the Top 75 Datomic developers on GitHub.
Read more about Konrad Szydlo

View More author details
Right arrow

Appendix

About

This section is included to assist the students to perform the activities present in the book. It includes detailed steps that are to be performed by the students to complete and achieve the objectives of the book.

1. Hello REPL!

Activity 1.01: Performing Basic Operations

Solution:

  1. Open the REPL.
  2. Print the message "I am not afraid of parentheses" to motivate yourself:
    user=> (println "I am not afraid of parentheses")
    I am not afraid of parentheses
    nil
  3. 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):
    user=> (* (+ 1 2 3) (- 10 3))
    42
  4. Print the message "Well done!" to congratulate yourself:
    user=> (println "Well done!")
    Well done!
    Nil
  5. Exit the REPL by pressing Ctrl + D or typing the following command:
    user=> (System/exit 0)

By completing this activity, you have written code that prints a message to the standard output. You have also performed some mathematical operations using the prefix notation and nested expressions.

Activity 1.02: Predicting the Atmospheric Carbon Dioxide Level

Solution:

  1. Open your favorite editor and...

2. Data Types and Immutability

Activity 2.01: Creating a Simple In-Memory Database

Solution:

  1. First, create the helper functions. You can get the Hash Map by executing the read-db function with no arguments, and write to the database by executing the write-db function with a Hash Map as an argument:
    user=>
    (def memory-db (atom {}))
    #'user/memory-db
    (defn read-db [] @memory-db)
    #'user/read-db
    user=> (defn write-db [new-db] (reset! memory-db new-db))
    #'user/write-db
  2. Start by creating the create-table function. This function should take one parameter: the table name. It should add a new key (the table name) at the root of our Hash Map database, and the value should be another Hash Map containing two entries – an empty vector at the data key and an empty Hash Map at the indexes key:
    user=>
    (defn create-table
      [table-name]
      (let [db (read-db)]
        (write-db (assoc db table-name {:data [] :indexes {}}))...

3. Functions in Depth

Activity 3.01: Building a Distance and Cost Calculator

Solution:

  1. Start by defining the walking-speed and driving-speed constants:
     (def walking-speed 4)
    (def driving-speed 70)
  2. Create two other constants representing two locations with the coordinates :lat and :lon. You can use the previous example with Paris and Bordeaux or look up your own. You will be using them to test your distance and itinerary functions:
     (def paris {:lat 48.856483 :lon 2.352413})
    (def bordeaux {:lat 44.834999  :lon -0.575490})
  3. Create the distance function. It should take two parameters representing the two locations for which we need to calculate the distance. You can use a combination of sequential and associative destructuring right in the function parameters to disassemble the latitude and longitude from both locations. You can decompose the steps of the calculation in a let expression and use the Math/cos function to calculate the cosine and Math/sqrt to calculate...

4. Mapping and Filtering

Activity 4.01: Using map and filter to Report Summary Information

Solution:

  1. To start, set up a simple framework using the ->> threading macro:
    (defn max-value-by-status [field status users]
                   (->> 
                     users
                     ;; code will go here
                     ))

    This defines the fundamental structure of our function, which we can sum up as follows: start with users and send it through a series of transformations.

  2. The first of these transformations will be to filter out all the users that don't have the status we are looking for. We'll use filter for that, naturally, and we'll include a...

5. Many to One: Reducing

Activity 5.01: Calculating Elo Ratings for Tennis

Solution:

  1. Here is the minimal deps.edn file you'll need:
    {:deps
     {org.clojure/data.csv {:mvn/version "0.1.4"}
      semantic-csv {:mvn/version "0.2.1-alpha1"}
      org.clojure/math.numeric-tower {:mvn/version "0.0.4"}}}
  2. Here is the corresponding namespace declaration:
    (ns packt-clj.elo
      (:require [clojure.math.numeric-tower :as math]
                [clojure.java.io :as io]
                [clojure.data.csv :as csv]
                [semantic-csv.core :as sc])
  3. For the overall structure of your function, we will follow the same patterns we've used so far: a with-open macro with some pre-processing code:
    (defn elo-world
      ([csv k]
       (with-open...

6. Recursion and Looping

Activity 6.01: Generating HTML from Clojure Vectors

Solution:

  1. We'll use clojure.string in our solution. It is not strictly necessary, but clojure.string/join is a very convenient function. Because clojure.string is a standard Clojure namespace, a deps.edn file containing only an empty map is sufficient for this activity:
    (ns my-hiccup
     (:require [clojure.string :as string]))
  2. Here are the smaller functions that we'll use later in the main HTML-producing function:
    (defn attributes [m]
     (clojure.string/join " "
                (map (fn [[k v]]
                   (if (string? v)
                    (str (name k) "=\"" v "\"")
               ...

7. Recursion II: Lazy Sequences

Activity 7.01: Historical, Player-Centric Elo

Solution:

  1. Set up your project, which should be based on the code written for the last exercises in this chapter.
  2. The solution follows the pattern established with take-matches. Let's start with the parameters. We need to define separate behaviors for matches played by the "focus player" and matches played between other players. The first thing we need is, of course, a way to identify the player, so we'll add a player-slug argument. This wasn't necessary in take-matches because there we treated all the matches the same, regardless of who played in them.

    In take-matches, we had a limit argument to control how deeply we walked the tree. In this case, we need two different parameters, which we will call focus-depth and opponent-depth. Together, that gives us the following parameters for our new focus-history function:

    (defn focus-history [tree player-slug focus-depth opponent...

8. Namespaces, Libraries and Leiningen

Activity 8.01: Altering the Users List in an Application

Solution:

  1. Import the clojure.string namespace with use and the :rename keyword for the replace and reverse functions:
    (use '[clojure.string :rename {replace str-replace, reverse str-reverse}])
  2. Create a set of users:
    (def users #{"mr_paul smith" "dr_john blake" "miss_katie hudson"})
  3. Replace the underscore between honorifics and first names:
    (map #(str-replace % #"_" " ") users)

    This will return the following:

    ("mr paul smith" "miss katie hudson" "dr john blake")
  4. Use the capitalize function to capitalize each person's initials in the user group:
    (map #(capitalize %) users)

    This will return the following:

    ("Mr_paul smith" "Miss_katie hudson" "Dr_john blake")
  5. Update the user list by using the string's replace and capitalize functions:
    (def updated-users...

9. Host Platform Interoperability with Java and JavaScript

Activity 9.01: Book-Ordering Application

Solution:

  1. Create a new project:
    lein new app books-app
  2. Import the necessary namespaces:
    (ns books-app.core
      (:require [books-app.utils :as utils])
      (:import [java.util Scanner])
      (:gen-class))
  3. Create a map to hold books by year:
    (def ^:const books {:2019 {:clojure {:title "Hands-On Reactive Programming with Clojure" :price 20}
                               :go      {:title "Go Cookbook" :price 18}}
                        :2018 {:clojure {:title "Clojure Microservices" :price 15}
                ...

10. Testing

Activity 10.01: Writing Tests for the Coffee-Ordering Application

Solution:

  1. Import the testing namespaces:
    (ns coffee-app.utils-test
        (:require [clojure.test :refer :all]
                  [coffee-app.core :refer [price-menu]]
                  [coffee-app.utils :refer :all]))
  2. Create tests using the clojure.test library to display the orders messages.
  3. Test the application using the is macro:
    (deftest display-order-test
             (testing "Multiple tests with is macro"
                      (is (= (display-order {:number 4 :price 3.8 :type :latte}) "Bought 4 cups of latte for €3.8"))
                ...

11. Macros

Activity 11.01: A Tennis CSV Macro

Solution:

  1. Here is one possibility for the expanded code:
    (with-open [reader (io/reader csv)]
      (->> (csv/read-csv reader)
           sc/mappify
           (sc/cast-with {:winner_games_won sc/->int
                          :loser_games_won sc/->int})
           (map #(assoc % :games_diff (- (:winner_games_won %) (:loser_games_won %))))
           (filter #(> (:games_diff %) threshold))
           (map #(select-keys % [:winner_name :loser_name :games_diff]))
           doall))

    This should be taken as a rough sketch for the final output.  

  2. Set up your project. The deps.edn file should look like this:
    {:deps
    &...

12. Concurrency

Activity 12.01: A DOM Whack-a-mole Game

Solution:

  1. Create a project with lein figwheel:
    lein new figwheel packt-clj.dom-whackamole -- --rum
  2. Move to the new packt-clj.dom-whackamole directory and start a ClojureScript REPL:
    lein figwheel

    In your browser, at localhost:3449/index.html, you should see the default Figwheel page:

    Figure 12.26: Default Figwheel page

  3. Open dom-whackamole/src/packt-clj/dom-whackamole/core.cljs in your editor or IDE. This is where you will write all the remaining code.
  4. Define the atoms that will determine the game's state:
    (def game-length-in-seconds 20)
    (def millis-remaining (atom (* game-length-in-seconds 1000)))
    (def points (atom 0))
    (def game-state (atom :waiting))
    (def clock-interval (atom nil))
    (def moles (atom (into []
                (repeat 5 {:status :waiting
                     ...

13. Database Interaction and the Application Layer

Activity 13.01: Persisting Historic Tennis Results and ELO Calculations

Solution:

  1. In a new project, begin with the following dependencies:
    {:deps {clojure.java-time {:mvn/version "0.3.2"}
            hikari-cp {:mvn/version "2.8.0"}
            org.apache.derby/derby {:mvn/version "10.14.2.0"}
            org.clojure/data.csv {:mvn/version "0.1.4"}
            org.clojure/java.jdbc {:mvn/version "0.7.9"}
            semantic-csv {:mvn/version "0.2.1-alpha1"}}}
  2. In our src directory, create the following namespaces:
    packt-clj.tennis.database
    packt-clj.tennis.elo
    packt-clj.tennis.ingest
    packt-clj.tennis.parse
    packt-clj.tennis.query
  3. Creating our connection pool in the database namespace...

14. HTTP with Ring

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

Solution:

  1. Add the following dependencies to packt-clj.tennis in the deps.edn file:
    {:deps {..
            clj-http {:mvn/version "3.10.0"}
            compojure {:mvn/version "1.6.1"}
            metosin/muuntaja {:mvn/version "0.6.4"}
            org.clojure/data.json {:mvn/version "0.2.6"}
            ring/ring-core {:mvn/version "1.7.1"}
            ring/ring-jetty-adapter {:mvn/version "1.7.1"}}
  2. Create our namespace with the following require route:
    (ns packt-clj.tennis.api
      (:require
        [clojure.edn :as edn]
        [compojure.core :refer [context defroutes...

15. The Frontend: A ClojureScript UI

Activity 15.01: Displaying a Grid of Images from the Internet

Solution:

  1. At the command-line prompt, create a new Figwheel project using the following Leiningen command:
    lein new figwheel packt-clj.images -- --reagent
  2. Move to the packt-clj.images/ directory and type:
    lein figwheel

    After a few seconds, your browser should open to the default Figwheel page:

    Figure 15.8: A fresh ClojureScript project waiting for your code

  3. Open the src/packt_clj/images/core.cljs file in your preferred editor and modify the code:
    (ns packt-clj.images.core
        (:require [reagent.core :as r]))
  4. The commonly used alias for Reagent is r instead of Reagent:
    (defonce app-state (r/atom {:images []
                                           :author-display true...
lock icon
The rest of the chapter is locked
You have been reading a chapter from
The Clojure Workshop
Published in: Jan 2020Publisher: PacktISBN-13: 9781838825485
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Authors (5)

author image
Joseph Fahey

Joseph Fahey has been a developer for nearly two decades. He got his start in the Digital Humanities in the early 2000s. Ever since then, he has been trying to hone his skills and expand his inventory of techniques. This lead him to Common Lisp and then to Clojure when it was first introduced. As an independent developer, Joseph was able to quickly start using Clojure professionally. These days, Joseph gets to write Clojure for his day job at Empear AB.
Read more about Joseph Fahey

author image
Thomas Haratyk

Thomas Haratyk graduated from Lille University of Science and Technology and has been a professional programmer for nine years. After studying computer science and starting his career in France, he is now working as a consultant in London, helping start-ups develop their products and scale their platforms with Clojure, Ruby, and modern JavaScript.
Read more about Thomas Haratyk

author image
Scott McCaughie

Scott McCaughie lives near Glasgow, Scotland where he works as a senior Clojure developer for Previse, a Fintech startup aiming to solve the problem of slow payments in the B2B space. Having graduated from Heriot-Watt University, his first 6 years were spent building out Risk and PnL systems for JP Morgan. A fortuitous offer of a role learning and writing Clojure came up and he jumped at the chance. 5 years of coding later and it's the best career decision he's made. In his spare time, Scott is an avid reader, enjoys behavioral psychology and financial independence podcasts, and keeps fit by commuting by bike, running, climbing, hill walking, snowboarding. You get the picture!
Read more about Scott McCaughie

author image
Yehonathan Sharvit

Yehonathan Sharvit has been a software developer since 2001. He discovered functional programming in 2009. It has profoundly changed his view of programming and his coding style. He loves to share his discoveries and his expertise. He has been giving courses on Clojure and JavaScript since 2016. He holds a master's degree in Mathematics.
Read more about Yehonathan Sharvit

author image
Konrad Szydlo

Konrad Szydlo is a psychology and computing graduate from Bournemouth University. He has worked with Clojure for the last 8 years. Since January 2016, he has worked as a software engineer and team leader at Retailic, responsible for building a website for the biggest royalty program in Poland. Prior to this, he worked as a developer with Sky, developing e-commerce and sports applications, where he used Ruby, Java, and PHP. He is also listed in the Top 75 Datomic developers on GitHub.
Read more about Konrad Szydlo