Skip to content

Latest commit

 

History

History
172 lines (124 loc) · 6.17 KB

2020-07-07.md

File metadata and controls

172 lines (124 loc) · 6.17 KB

Learning Clojure in Public - Week 3 Day 2 (16/35)

Expectations

The expectation today was cover chapter 5 of Brave Clojure. My cohort-mate and were also on the final stretch for our collaborative Athens issue so I wanted to cover some of that as well.

What I learned

What makes a pure function

  • A pure function always returns the same result given the same argument(s). This is called referential transparency.
  • A pure function cannot cause any side effects (changes that are observable outside the function itself). With pure function you pass the result of a function as argument to another. It's called function composition. Functional programming encourages you to build more complex functions by combining simpler functions.

Reminder on how to use arity overloading to do the starting case (here is a sum):

(defn sum
   ([vals] (sum vals 0))
   ([vals accumulating-total]
       (if (empty? vals)
       accumulating-total
       (sum (rest vals) (+ (first vals) accumulating-total)))))

A more efficient is to do this with recur:

(defn sum
  ([vals]
     (sum vals 0))
  ([vals accumulating-total]
     (if (empty? vals)
       accumulating-total
       (recur (rest vals) (+ (first vals) accumulating-total)))))

comp

comp takes function and returns a new anonymous function that is the function composition of them. ((comp inc *) 2 3), first it multiplies and then takes the result and increases it.

Here is another example to access nested maps:

def character
  {:name "Smooches McCutes"
   :attributes {:intelligence 10
                :strength 4
                :dexterity 5}})
(def c-int (comp :intelligence :attributes))
(def c-str (comp :strength :attributes))
(def c-dex (comp :dexterity :attributes))

(c-int character)
; => 10

(c-str character)
; => 4

(c-dex character)
; => 5

memoize

memoize saves the arguments and the return of a function, so when you evaluate the function with the same arguments again the result is returned immediately.

(def memo-sleepy-identity (memoize sleepy-identity))
(memo-sleepy-identity "Mr. Fantastico")
; => "Mr. Fantastico" after 1 second

(memo-sleepy-identity "Mr. Fantastico")
; => "Mr. Fantastico" immediately

Brave Clojure Chapter 5 Exercises

Exercise 1: You used (comp :intelligence :attributes) to create a function that returns a character’s intelligence. Create a new function, attr, that you can call like (attr :intelligence) and that does the same thing.

(defn attr
  [attribute]
  (comp attribute :attributes))

Exercise 2: Implement the comp function.

(defn my-comp
  [& functions]
  (fn [& args]
    (reduce (fn [arg f] (f arg)) (apply (last functions) args) (reverse functions))))

This one was quite challenging, and I'm happy I found a solution. It's for sure different from the ones already posted online.

Exercise 3: Implement the assoc-in function. Hint: use the assoc function and define its parameters as [m [k & ks] v].

(defn my-assoc-in
  [m [k & ks] val]
  (if (nil? ks) (assoc m k val) (assoc m k (my-assoc-in (get m k {}) ks val))))

Exercise 4: Look up and use the update-in function.

user=>
(def foo {:user {:bar 1}})
#'user/foo
user=>
(update-in foo [:user :bar] inc)
{:user {:bar 2}}

Exercise 5: Implement update-in.

(def foo {:user {:bar 1}})

(defn my-update-in
  [m [k & ks] f]
  (if (nil? ks) (do (prn "hello") (assoc m k (f (get m k 1)))) (assoc m k (my-assoc-in (get m k {}) ks f))))

  (my-update-in foo [:user :bar] inc)

This one doesn't work just yet, it's almost there but instead of evaluating the function with arguments it just returns the function itself.

Continuing investigation on issue

Today I am looking at the unindent problem, part of the same issue. When on a page, the user cannot unindent a node that is already at room level. However, when the user has "entered" a node (that's when you click on the bullet and zoom into a node that becomes a page), that's when it is possible to unindent it (it goes back to the page, which makes sense but is counterintuitive for the user).

I originally thought it was the enter function that was responsible for getting into the node, but this is actually for pressing... Enter.

Our next lead was :current-route which was closer. Eventually we found a way to pass the navigated page down to the unindent event which we can then compare to the parent node of the current node and figure out if we can indent or not.

(defn unindent
  [uid context-root]
  (let [parent (db/get-parent [:block/uid uid])
        grandpa (db/get-parent (:db/id parent))
        new-block {:block/uid uid :block/order (inc (:block/order parent))}
        reindex-grandpa (->> (inc-after (:db/id grandpa) (:block/order parent))
                             (concat [new-block]))]
    (if (= (:block/uid parent) context-root)
      {}
      (when (and parent grandpa)
        {:transact! [[:db/retract (:db/id parent) :block/children [:block/uid uid]]
                     {:db/id (:db/id grandpa) :block/children reindex-grandpa}]}))))


(reg-event-fx
 :unindent
 (fn [{rfdb :db} [_ uid]] ;; Pass in the reframe db as a cofx to the :unindent event handler
   (unindent uid (get-in rfdb [:current-route :path-params :id]))))

Takeaways

I'm glad the brave clojure chapter was lighter than the others, so I was able to spend more time on troubleshooting our issue. We now even have an open PR for it!

The exercises were also challenging and I am glad I completed them. I got to compare my solutions with others posted on GitHub and mine are different, no one came up with super smart solutions, which is reassuring.

Tomorrow I will spend some time looking into the existing issues and see if I can pick one up by myself.

Let's end the day with a tally of what I completed:

  • Completed Chapter 5 of Clojure for the Brave and True
  • Also did the 5 exercises in that same chapter
  • FInally solved the last problems in our first collaborative issue in Athens, and opened the PR