Ah, how fast the days go by. It feels like yesterday (cliched) when I wrapped my first expression in a brand new pair of parantheses handed down to me by the Clojure Elders.
Well today was the end to an excellent holiday weekend. Like I mentioned yesterday, my goal for today was to get into Chapter 4 of Brave Clojure and dive into some Clojure issues. I think I was able to do both 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.
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!
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.