overthunk ltd. A Solitary Software Production

3D portrait of a weird little guy

Core Clojure Functions and Athens DevCards


# Contents

# What I learned today

Chapter 4 of Brave Clojure

Clojure implements functions in terms of sequence abstractions. map, reduce take a seq. If the core sequence functions (first, rest and cons) work on a DS, it implements the sequence abstraction.

What is a Sequence? -

A sequence is a collection of elements organized in linear order vs an unordered collection or a graph without a before-after relationship between its nodes.

Whenever a Clojure function expects a seq, it uses the seq function on the data structure in order to obtain a data structure that allows for first, rest and cons.

seq always returns a value that looks like a list

(seq {:name "Billy" :age 43})
;; => ([:name "Billy"] [:age 43])
;; We get a list of two element vectors

A function like map, or reduce only cares that the data structure it receives can have sequence operations performed on them, not the implementation details.

(defn titleize
  [topic]
  (str topic " for the Brave and True"))

;; vectors
(map titleize ["Hamsters" "ragnarok"])
;; => ("Hamsters for the Brave and True" "ragnarok for the Brave and True")

;; list
(map titleize '("Table Tennis" "Empathy"))
;; => ("Table Tennis for the Brave and True" "Empathy for the Brave and True")

;; set
(map titleize #{"Hamsters" "ragnarok"})
;; => ("Hamsters for the Brave and True" "ragnarok for the Brave and True")

You can do interesting things with the map function like passing a collection of functions as an argument

(def sum #(reduce + %))
(def avg #(/ (sum %) (count %)))
(defn stats
  [numbers]
  (map #(% numbers) [sum count avg]))

(stats [1 2 3 4])
;; => (10 4 5/2)

(stats [80 1 44 13 6])
;; => (144 5 144/5)

or even use map to get the values associated with a keyword from a collection of maps. (since keywords can be used as functions)

(def identities
  [{:alias "Batman" :real "Bruce Wayne"}
   {:alias "Spiderman" :real "Peter Parker"}
   {:alias "Santa" :real "Your mom"}
   {:alias "Easter Bunny" :real "Your dad"}])

(map :real identities)
;; => ("Bruce Wayne" "Peter Parker" "Your mom" "Your dad")

These were two pretty interesting use-cases for map that I hadn’t learned about till now. Thanks Brave Clojure!

Like map, reduce is also a very flexible function that can be used for more things than commonly assumed.

We can use reduce to update a maps values -

(reduce (fn [new-map [key val]] (assoc new-map key (inc val))) {} {:max 30 :min 10})
;; => {:max 31, :min 11}

reduce can also be used to build a larger sequence from a smaller one. It can be used to filter values from a collection etc. reduce can basically be used to derive new values from a seq-able data structures.

# Athens Code

I saw an issue about refactoring a small piece of Athens code. I figured that this would be a good way to understand part of the codebase by myself and potentially contribute.

The issue related to swapping out the create-element function in reagent with adapt-react-class.

The function reagent/adapt-react-class will turn a React Component into something that can be placed into the first position of a Hiccup form, as if it were a Reagent function

The code change itself is quite simple -

[:span {:style {:color (color :link-color)}} (r/create-element mui-icons/Face)]

;; becomes

[(r/adapt-react-class mui-icons/Face) {:style {:color (color :link-color)}}]

We can skip on using the span keyword and the new code makes more semantic sense since we’re rendering the mui-icons/Face component with {:style {:color (color :link-color)}} props.

I also got to test out building DevCards and deploying the commit with GitHub Actions!

# Takeaways

Even the smallest issues can end up teaching you something! It was really fun to explore this little part of the Athens codebase and learn about reagent functions.

I think I achieved what I wanted to today and learned a bit more than I expected which is always quite pleasant. I hope this will give me confidence to dig into more issues and become a better contributor.