(clj 4) Learning about when, maps and closures

It's been more than two months since I did any Clojure - for the obvious reasons. Luckily I did take notes as I proceeded with chapter 3 of "Clojure for the Brave and True". So the plan is to process these notes into a blog post, which means this post will cover the sections "Data Structures" and "Functions" of that third chapter. Leaving me ready to proceed with the rest of the chapter, i.e. "Pulling It All Together" and the summary and exercises.

Why is there a when?

The part about control flow is actually before the part about and and or which I talked about in my previous post, but according to my notes I returned to it. I don't remember why to be honest.

The book provides the following example of when:

(when true
    (println "Success!")
    "abra cadabra")
; => Success!
; => "abra cadabra"

And if the condition is false, you get:

(when false
    (println "First do!")
    "By Odin's Elbow!")
; => nil

So when allows you to do several things and return something when the condition is true, while it will return nil if the condition is false. The thing that got me curious is that you can do the same thing almost as easily with if and do:

(if true
  (do (println "Success!")
      "abra cadabra")
  )
; => Success!
; => "abra cadabra"

(if false
  (do (println "First do!")
      "By Odin's Elbow!")
  )
; => nil

I can see two advantages to having the when. It needs fewer characters: two brackets and a space. It's also more readable, since there is no do and no indentation decisions because of that do. Is that enough reason? I honestly don't know, because I haven't written enough Clojure to decide if you use when often enough to make it worth it having it.

There is another difference, however, which I found out thanks to vim-fireplace's K command, which looks up the symbol under the cursor with doc. if and do are Special Forms, while when is a Macro. What difference that makes, I don't know yet. In the section about functions in the same chapter, it does say on page 126:

You’ll learn everything there is to know about macro calls and special forms in Chapter 7. For now, the main feature that makes special forms “special” is that, unlike function calls, they don’t always evaluate all of their operands.

And on page 127:

Macros are similar to special forms in that they evaluate their operands differently from function calls, and they also can’t be passed as arguments to functions.

I made a note to revisit this when I get to Chapter 7. So one day there should be a link to that post here.

Maps

With my Python background I had some trouble with the syntax of maps. With keywords being used as keys, they look like this:

{:first-name "Charlie"
 :last-name "McFishwich"}

So the same characters as a Python dictionary but in a different order. This made me appreciate the value of practice. I won't be enjoying writing Clojure if I mess up the syntax of maps every time because of Python muscle memory.

Practice wasn't without its hurdles, though. When I defined a map and evaluated it like this:

(def my-other-map (hash-map :a 100 :b 101 :c 102))
(my-other-map)

I got the error ArityException Wrong number of args (0) passed to: PersistentArrayMap clojure.lang.AFn.throwArity (AFn.java:429).

Removing ther parantheses around the map's name resulted in:

Error detected while processing function <SNR>48_printop[1]..<SNR>48_opfunc[40]..function <SNR>48_printop[1]..<SNR>48_opfunc:
line   21:
E20: Mark not set
E20: Mark not set
Press ENTER or type command to continue

The solution turned out to be to not have the parantheses (so just my-other-map on its own line), but then not use cpp to evaluate the statement, which evaluates the innermost statement, but use cp$ (evaluate until end of the line) or cp_ (evaluate current line).

Trying to evaluate the innermost form of a string in parantheses, gave me an impressive error message as well:

ClassCastException class java.lang.String cannot be cast to class clojure.lang.IFn (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')  clojure-noob.core/eval6974 (form-init11099437324952859876.clj:5)

My notes tell me I didn't understand what exactly was going on here. Today, while writing this blog post, I went back in the book to the start of the chapter and there it explains that Clojure recognizes two kinds of structures: literal representations of data structures and operations. All operations take the form of opening parenthesis, operator, operands, closing parenthesis. Me putting the name of my hash map or a string literal in parantheses is neither of those structures, so makes sense I get an error.

Then for the scope of evaluation, cpp evaluates the innermost form. However I am on a line with just the name of my map, so I'm not sure what it tries to evaluate. Perhaps the whole file? In any case it's not what I want to evaluate, so it's ok that it throws an error instead of working.

Finally, I was surprised that all these three ways to get a value from a map are part of the language:

(def my-map {:a 0 :b {:c "ho hum"}})

(get my-map :a)
; => 0

(my-map :a)
; => 0

(:a my-map)
; => 0

Functions and returning functions

The section about functions ends with the following explanation about returning functions:

By now you’ve seen that functions can return other functions. The returned functions are closures, which means that they can access all the variables that were in scope when the function was created. Here’s a standard example:

(defn inc-maker
  "Create a custom incrementor"
  [inc-by]
  #(+ % inc-by))


(def inc3 (inc-maker 3))
(inc3 7)
; => 10

Here, inc-by is in scope, so the returned function has access to it even when the returned function is used outside inc-maker.

I do not understand this explanation. In my mind the inc-maker returns a function that adds 3 to a number, so basically (defn inc3 [number] (+ number 3)), and I don't see how the scope of inc-by comes into play. (I am noticing now though that in the example def is used, not defn to create the inc3 function.) So now I have some code that does make sense to me, while the explanation of the code doesn't, which is a somewhat weird place to be in.

In the hope of figuring this out I checked Wikipedia which says about clojures: "Operationally, a closure is a record storing a function together with an environment." Which is in line with the explanation quoted above, but it doesn't help me understand.

It doesn't help either that the name of this language, Clojure, is a pun on the term 'closure', which makes me feel it is important to understand them. Some more searching didn't get me anything useful, although I did find this post from Rich Hickey about naming Clojure "Clojure":

The name was chosen to be unique. I wanted to involve c (c#), l (lisp) and j (java).

Once I came up with Clojure, given the pun on closure, the available domains and vast emptiness of the googlespace, it was an easy decision.

And that's where I decided to accept I would not be understanding why closures are important when functions return other functions. Except that after writing the previous sentence, I started wondering if I could construct a more complicated function that returns a function, that would help me understand. I failed to construct such a function, since it wouldn't accept me binding a variable in either of the ways I tried to.

In any case, hopefully I will understand this one day. It does leave me with the feeling that the explanation about closures doesn't need to be in this part of the book. Perhaps it would be more in its place in chapter 7 which describes how Clojure runs your code.


As I did in my previous post, this postscript contains some smaller things I noticed and/or learned.

Notes on blogging

Thanks to the combination of my notes, my play-around files and some re-playing around, it was fairly easy to write this blog post. I don't like leaving such a gap between learning and blogging, but it's good to know that when it happens, it's not a problem.

Notes on Vim

I had to relearn a few things. Vim-fireplace will not connect to the REPL if you don't start one with lein repl first. Use "+ in Vim to copy-paste to the system clipboard (thank you past me for the cheatsheet).

Changing to gruvbox as Vim theme was a good idea. I did have to move the colorscheme line in my .vimrc because the statusbar was not green in normal mode.

In my previous post's postscrpit I was wondering how to get the result of an evaluation into my file. I have found a way, but it's quite a few keystrokes: copy the thing, paste the thing, evaluate it with c!{motion} which replaces it with the result of the evaluation, then gcc to make it into a comment. I might need to make a custom vim command to make that easier.

According to my notes I used ctrl+n (next match) and ctrl+p (previous match) for auto-completion, so I added those to my cheatsheet.

(clj 3) Clojure's 'and' and 'or' are weird (but not really)

Early in chapter 3 of the Brave and True-book the Boolean operators and and or are introduced:

Clojure uses the Boolean operators or and and. or returns either the first truthy value or the last value. and returns the first falsey value or, if no values are falsey, the last truthy value.

This explanation is followed by some examples:

(or false nil :large_I_mean_venti :why_cant_I_just_say_large)
; => :large_I_mean_venti

(or (= 0 1) (= "yes" "no"))
; => false

(or nil)
; => nil
(and :free_wifi :hot_coffee)
; => :hot_coffee

(and :feelin_super_cool nil false)
; => nil

What I found remarkable about this is that and and or do not return a boolean in all cases. Before I go into that, let's back up a second and cover their basics in a little more depth first.

Read more…

(clj 2) Setting up Vim for Clojure

As mentioned in my previous post I've decided to use Vim as my Clojure editor. That leaves me with three things to do: getting reacquainted with Vim, updating my Vim config in the .vimrc file, and installing both general and Clojure-specific Vim plugins.

Getting reacquainted with Vim

Ever since I learned Vim basics a long time ago I have been using it once in a while to make small edits to a config file or a commit message, but not for anything more complicated than that. So that's the first thing I wanted to address: refresh my basics and make sure I know where to find more information when I need it.

Vimtutor

I figured that a good way to get back into Vim was the Vimtutor. It's a 30-minute interactive tutorial where you edit a file with instructions with vim. I kept notes of what it covers, which you can find here. Vim's help also includes a quickref, an "Overview of the most common commands you will use", which is intimidatingly long considering that description. As a reference to look things up in, it should be useful, though.

Read more…

(clj 1) Deciding on a Clojure editor

The second chapter of "Clojure for the Brave and True" is all about Emacs, "an excellent Clojure editor". Now you might wonder: does your choice of editor really matter that much? You're learning the language, so you don't need advanced IDE features. Some syntax highlighting, some code completion, something to help you manage all those parantheses perhaps, done. That would be true if not for the Clojure REPL.

The Clojure REPL

The REPL is definitely a Thing™️ in Clojure. It gives you a prompt where you can type code and it will execute it immediately. You can also load files with code into it, interacting with the functions and data defined in those. So that's a signifcantly faster feedback loop than having to compile and then run - which is how you'd normally run something written in Clojure, since its primary platform is the JVM. There are different ways of launching a REPL, but most guides I found tell you to use Leiningen. Oh, and REPL stands for Read-Evaluate-Print_Loop, because that's what the REPL does.

It definitely feels like this REPL is a bigger deal than I appreciate right now. Probably because I have only just begun learning Clojure. On the other hand, I may have also been spoiled by the quick feedback provided by Python and its IDLE. On the third hand, it's only because of learning of the Clojure REPL, I looked into importing files into Python's IDLE and found out that's indeed a thing it can do.

Read more…

(clj 0) Diving straight in with some koans

Why Clojure?

A long time ago (meaning I don't remember when but it's been a while) I learned about a programming language called Lisp. It was said that by studying this language you'd gain deep insights into programming and you'd never write code the same way again. That definitely piqued my interest.

Some time later (don't remember when exactly either) I learned about the existence of Clojure and that it too, was something special. So I added it to the list of programming languages I wanted to learn some day.

Then, earlier this month, I felt the need to start a new project. To learn something new. So I figured I'd learn Lisp. That didn't last long though, as I found out that the recommended editor for Lisp is Emacs. And from trying out Emacs and vi about 20 years ago I knew that for some reason vi does fit with how my mind works, and Emacs doesn't.

Read more…