Overthunk

Types, Exercises and Functional Enlightenment

Jun 23, 2020


Contents

Expectations

Yesterday, I set some expectations for myself regarding what I would learn today. And after spending a few hours vacillating between CFGU and Clojure for the Brave and True, I’ve chosen CFGU as my current learning material. Brave and True will come later once I get familiarity with the basics of Clojure.

I also expected myself to be more productive today after all the effort I spent setting a good dev environment with Emacs and I have to say that that’s panned out as well. I do feel quite comfortable interacting with the CIDER REPL and I can feel my old muscle memory for Emacs keybinds coming back.

What I learned

I spent most of today on Chapter 2 of CFGU which covers basic Clojure types and some commonly used functions.

Some interesting observations:

Using Regexes is also surprisingly simple in Clojure. I like that CFGU tackles this topic early on, since searching for text in strings is a pretty basic operation and everyone who’s learning to program know how to do it effectively.

4Clojure

Unlike my usual method of learning, where I tend to focus more on theory initially, this time I’m diving head on into practical problem solving. I sped through the elementary difficulty problems on 4Clojure. Some of the problems actually did end up making me think about how to solve them in idiomatic Clojure. Instead of using imperative constructs, I tried to use more recursion and functional composition techniques to solve them.

There were 2 problems that led to BIG LEARNING MOMENTS.

No: 21

Write a function which returns the Nth element from a sequence.

(= (__ '(4 5 6 7) 2) 6)

My first instinct was to use a for-loop, maintain a counter and when that counter was equal to N, return the element at the current index. However, I haven’t really come across for loops in Clojure so far. And I wanted to solve it without reaching into that Imperative Toolbox. So I tried using Recursion. This was my final solution:

(defn newnth [coll ind]
      (if (= 0 ind)
          (first coll)
          (newnth (rest coll) (- ind 1))

Pretty classic solution I would say. When I discussed this with my fellow Cohort members, their solutions were eye-opening. One was - #((vec %1) %2) which uses the vec function to convert the collection into a vector and then access the index directly. Vectors unlike Lists are array indexed and we can simply get the element at an index.

Another solution was - (fn [xs n] (first (drop n xs))). This uses the drop function which returns a lazy sequence of all but the first n items in coll. Lazy Sequences are a functional programming concept which effectively allow you to chain multiple transformations on a sequence and only perform the data intensive computation when needed. This is much more efficient than a strictly imperative model where the transformations are done immediately.

No: 22

Write a function which returns the total number of elements in a sequence.

This one has some interesting constraints. For one, the inputs can be lists, vectors or strings.

After that little functional composition revelation I had, I decided to leverage some pretty powerful functions, namely map and reduce. No FP language can be complete without these two.

Always you three

My original solution:

(fn [coll]
    (reduce + (map #(if % 1 0) coll)))

Mapping the #(if % 1 0) over a sequence yields a list of 1s with the same length as the original sequence. This list of 1s can then be reduced and summed up giving us the length of the sequence. I was a little disappointed in myself for using the #(if % 1 0) since I wanted to avoid any control flow mechanisms.

A cohort member used (fn [x] 1) in place of #(if % 1 0) which I think is a better idiomatic choice. (fn [x] 1) simply returns 1 for any input which is exactly what I wanted to do!

Takeaways

I’m honestly pleasantly surprised how useful it is to be able to bounce ideas off of other people. Being a part of Team Seneca and getting to talk about Clojure and problems is genuinely an honor. I’ve learned quite a lot of already!

Let’s end this day with a tally of what I completed:

#(prn "Fin Day 2! See ya tomorrow")