Core Clojure Functions and Athens DevCards
Jul 5, 2020
# 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.