{
"$type": "site.standard.document",
"canonicalUrl": "https://numergent.com/2017-01/Reading-Clojure.html",
"path": "/2017-01/Reading-Clojure.html",
"publishedAt": "2017-01-16T16:29:21.000Z",
"site": "at://did:plc:cf6futaebyc2k4wgzsr4v42k/site.standard.publication/3mp2ewx43js2g",
"tags": [
"clojure",
"clojurescript"
],
"textContent": "Preamble\n\nBack in early December I held a Clojure workshop for the Bucharest Functional Programming meet up. Having to explain the language to developers who were completely new at it was an interesting experience. I decided to start from the ground up: how to even read the blasted thing.\n\n\n\nReading Clojure\n\nI think what takes people the longest to get used to is how to _read_ the language - particularly if they are already familiar with functional programming semantics. It took me a while to get used to it myself, and even longer to realize why that was.\n\nWhen reading Java, or C#, we have these markers all over the place. I didn't even think about them. I had gotten used to just scanning a page of code, paging down, my brain on something else, until my eyes seized. They spotted a public static and then my brain came on, checked if it was the function I cared about, and otherwise it went back into sleep mode while I paged down.\n\nClojure's syntax is very compact. You don't have these noisy markers when reading code, so you need to be paying attention. You're going to have much less code to scan through, but your mind needs to be on.\n\nIt's a pleasant change, but it requires practice. We learn by doing, but we also learn by reading what others have done. So I figure the best way to start is to describe how to read Clojure.\n\nLuckily, reading Clojure is trivial. _The difficult part actually comes from having to abandon bad habits and expectations of it being more complicated than it is_.\n\nBecause that's the second thing I learned. I realized that something that was making it difficult for me to write and read Clojure, was that I was attempting to superimpose on it the more baroque constructs of the languages I was used to.\n\nSo let’s dive right in.\n\nLists and vectors\n\nI thought about easing this in, and talking about code organization and functional programming and what not, but this is important. This is, in fact, the most important bit here.\n\nThis is a list: (1 2 3)\n\nThis is a vector: [1 2 3] \n\nWe'll talk about how they are different in a bit - it's mostly about how some functions treat them. For all effects and purposes, the list acts like you would expect a list to act, the vector as an array.\n\nThis is also a list: (+ 1 2 3)\n\nThis is also a vector: [+ 1 2 3]\n\nThat's it. That is 90% of what you need to know.\n\nEvaluation semantics\n\nThe question then is... if this is a list:\n\nAnd this is also a list\n\nWhy does the latter get executed and but the second one doesn't? \n\nBefore we go any further, take a step back. Open a text file, make some notes as to the reasons why you'd expect the second line to be an invocation.\n\n...\n\n...\n\n...\n\nReady?\n\nOK.\n\nIn fact, that's a misconception. When Clojure encounters a non-empty list, it always assumes that they are something to evaluate (either a special form, a macro, or a function). The first value is what's being invoked, the rest are its parameters. \n\nSo in the case of \n\nIt will invoke the function + with the arguments 1, 2 and 3.\n\nPeriod. Dead simple. That's it. That's pretty much all there is to reading Clojure.\n\nAs for the first example, if Clojure were to encounter (1 2 3) in the middle of a program, it'll try to execute it, and expect 1 to be a function. That'll throw up an error.\n\nThere's a way to avoid that, called \"quoting\". We'll get to that later.\n\nIf you're joining us from Java...\n\nNow, if you are used to Cor Java or Scala, there's a perfectly normal and reasonable reaction to this. There's a part of your brain that's nodding along, thinking \"yeah, yeah, homoiconicity, sure\", and at the same time placing a huge asterisk around it. \n\nThat asterisk likely points to something saying \"well, there's special cases, like if we need to define a function, or ifs, or a loop. Those things have their own syntax that maybe smells like this, but is different\". Maybe that voice is fine with there being a special case, but it still thinks there is.\n\nLet's look at some examples to shake off this implicit assumption.\n\nExamples\n\nLet's look at some cases.\n\nor\n\nThis is trivial, right? We just apply the logical or function to everything that comes right after. Same thing as when we were adding. No surprise.\n\nIn Java we'd have the or in the middle of every value, here we just front-load it. No biggie.\n\nThat's because in Java || is an operator, same way that + is. Java does have some things that are inherent syntax, like the if.\n\nif\n\nSo let's look at what an if is like in Clojure.\n\nAha! We can already see some different things here. Syntax, right? And in fact, if you've read about Clojure, you've seen that an if is what is called a special form.\n\nSpecial forms aside\n\nWhich is true. Clojure does have something called special forms. One hears special forms and thinks \"that's the stuff that is different\".\n\nIt's not. Not for syntax, and not on how the semantics are evaluated.\n\nWhat I want you to hear when somebody says special forms is not special cases but primitives. They are things implemented directly into the language, not things that behave differently.\n\nKeep that in mind whenever you see the term come up.\n\nBack to decoding the if\n \nLet's decode that one for a bit. \n\nFirst of all, there's the question mark in odd?. That's not special syntax, but a convention to indicate a value is a boolean. It's good form to use it, but not necessary.\n\nThen we have the structure that we expect. We have the thing to evaluate (odd?), followed by what to evaluate if true, and what to evaluate if false.\n\nBut this is actually not special syntax. The \"branches\" don't even need to be \"in parenthesis\". Remember: that's something we do only if we have to evaluate a function. In fact, there are no \"branches\", but forms to evaluate in either case, which could just as well just be values.\n\nTherefore, this would be just as valid an if as the one above:\n\nIf got-a-list? is true, then Clojure would evaluate the function of converting the list to a string, otherwise it'd just return the string value of the second case (a string evaluates to itself).\n\nFunction declaration\n\nOnce you've grokked that, we should be ready to look at function declaration. Language shapes the way we think, so in order to do that, we should first consider how one's mind is likely to describe code.\n\nHow we think about functions\n\nLet's go back to how we declare a trivial function in Java.\n\nLet's go over how we'd describe what we are looking at here. This might seem like a trivial thing, but don't skip over it.\n\n- We have the public keyword, telling us how available the function is,\n- We then have the static keyword, telling us if it belongs to a class or an instance,\n- Then there's a type the function returns,\n- Then there's the function name,\n- Then comes the parameter declaration, wrapped in parenthesis,\n- Then there are brackets wrapping a code block,\n- Then there's the return keyword followed by what to return.\n\nWe really have no other way to do it. We need to do this descriptively, think in terms of characters and glyphs that mark what we see. The parenthesis, the brackets, they have no use other than as syntax markers.\n\ndefn, round 1\n\nLet's now look at how we would do this in Clojure. Say, we want to add the \"hello world\" of functions and re-implement inc:\n\nThis doesn't look too bad. Still, that part of your brain might think \"well, that's a special syntax that expects some things in order, like the parameters in brackets, or it gets a syntax error\".\n\nComing from Java, we might be tempted to read it like this: \n\n- We have the defn keyword, \n- Then the function name, \n- Then the parameter names in square brackets, \n- Then we just traded the brackets in the body for parenthesis.\n\nLet's disabuse that part of your brain from the notion right away.\n\nThis is just the exact same thing as when we were describing lists and vectors before. If we were to break this down into its elements, we would instead see it as:\n\n- defn is the name of what's being invoked. Everything else after it are arguments,\n- The first argument is a symbol specifying the name that the function will be bound to (plus-one),\n- Then there's an argument that's a vector with the symbols for the parameter names,\n- The rest is considered implicitly as a list, and its elements will be evaluated one by one. \n\nThe result value of a function is the result value of the last item evaluated, in this case, (+ i 1).\n\nBut notice the fundamental difference there, which is easy to gloss over. The parameter names are not \"in square brackets\", _they are a basic data structure containing symbols_. Whatever is receiving this symbol vector can manipulate it or pass it to other functions at will.\n\nIn fact, defn is not even a special form, it's not a primitive, and it's not an inherent part of the language. It's a macro.\n\ndefn, round 2\n\nHow about this, then?\n\n \nThis is, once again, the exact same thing. Let's break it down, with some nesting to differentiate between the parameter lists and what's being invoked.\n\nAgain, we have the defn macro, with the same form as before. Then its first element is another macro, let, which receives as its first parameter a vector of symbol/value pairs.\n\nLet this sink in for a bit. _Every case of Clojure code that you encounter will follow this exact same pattern_. No matter what that code is or where it comes from, you can be 100% sure that the first value will be the item being invoked, and whatever comes after are arguments.\n\nEven if you see a known function name further down the chain... guess what? That means a function is being passed as an argument.\n\nTao\n\nThis is perhaps the most fundamental thing here. If you take anything from this long spiel, please let it be this bit.\n\nThis here is the tao of Clojure syntax. There truly is nothing but the list. In this case, syntax and evaluation semantics are one and the same.\n\nThere is nothing that will deviate from this. Value assignment, function invocation, type declaration... it'll all take the same form.\n\nQuiz!\n\nOK, pop quiz! Are these lines valid Clojure?\n\nYes! Both are, as a matter of fact. They are both lists starting with a symbol of a function to invoke. Having said that, the second one will not compile, as the function inc expects some parameters.\n\nHaving established that inc is a function, how about these?\n\nThese are syntactically valid Clojure as well. \n\n- inc is merely the symbol associated with the function.\n- [inc] is a vector whose first element is the function inc.\n- [inc 1] is a vector whose first element is the function inc, followed by the element 1. \n\nIf Clojure finds any of these, it won't try to invoke them as it would with a list. It will merely evaluate the function reference, and in the case of the last two, will create a vector with these values. This is useful when you want to pass a function as a parameter to another function.\n\nHaving looked at that, would you then expect this to be syntactically valid Clojure?\n\nAnd it is as well! We are just constructing a list of stuff, some of which are vectors and some of which are symbols. Now, is it semantically valid? As in, will it not only compile, but execute?\n\n...\n\n...\n\n...\n\nYes! Clojure will go down the list, one by one, and evaluate them. The resulting value will be the last item evaluated, or in this case, v. That's effectively a very noisy identity function.\n\nOf course, you're not likely to encounter a function like that. I'm only writing something so odd-looking to eliminate two preconceptions:\n\n- First, that everything needs to be wrapped in parenthesis. You don't need to do that unless you're creating a list or invoking something;\n- Second, that Clojure has all these syntactic special cases that other languages do, only done differently.\n\nWrapping up\n\nPhew, that was a long read.\n\nIf you're just coming to Clojure, I hope this has helped scrub off some deeply ingrained habits about how to read it. While I haven't touched on many areas, like sets, keywords, hash maps or meta-data, I thought that clarifying this first was more important. We should also go into how defn is not even a primitive, and just macro combining a few primitives in a single, convenient package, but we'll get to that later.\n\nNow go out and start getting acquainted with Clojure!",
"title": "Reading Clojure"
}