Clojure Basics

When learning a new language I like to collate the bare minimum amount of syntax and language information in order to start playing around with it by solving Project Euler Problems.

This is for my own reference really, but hopefully it could be handy for someone else going along the same path. Its really the absolute minimum that you’d need to start having fun.

Table of Contents

  1. Basic Syntax
    1. Forms
    2. Operations
    3. Control Flow
      1. if
      2. cond
      3. do
      4. when
      5. and
      6. or
    4. Data Types
      1. Strings
      2. Numbers
    5. Data Structures
      1. Lists
      2. Vectors
      3. Maps
      4. Sets
      5. Keywords
    6. Core functions
      1. def
      2. cons
      3. conj
      4. first
      5. next
      6. rest
      7. nth
      8. take
      9. drop
      10. complement
  2. Writing Functions
    1. The Structure of a Function
    2. Arity
      1. Using Arity Overloading to provide default arguments
      2. Rest parameter
    3. Destructuring
      1. Vectors
      2. Maps
    4. Anonymous Functions
    5. Threading
  3. Tests
    1. Writing and running tests
    2. Asserts
    3. lein-test-refresh
    4. test.check
  4. Functional Tools
    1. Apply
    2. Partial
    3. Map
    4. Reduce
    5. Filter
    6. Comp
    7. Memoize
  5. Namespaces
    1. What are namespaces?
    2. Creating namespaces
    3. Alias
  6. Macros
    1. What are Macros?

Please note that several of the code examples in this are reworked examples from one of the following:


Basic Syntax

Forms

Forms are the basis of Clojure’s syntax. These include:

  • operations

  • literal representations of data, such as:
    • strings
    • numbers
  • data structures, such as:
    • lists
    • vectors
    • maps

Operations

In Clojure operations all take the form of: open-parenthesis, operator, operands, close-parenthesis

    (operator operand-a operand-b)

Control Flow

if

Clojure has conditionals to control flow, such as if, which has the following structure:

    (if operation-that-returns-Boolean
      then-do-this
      else-do-this)

Notice that the operator if takes takes three operands here. The first is resolves to a Boolean. If this is truthy then the first of the two possible branches will be evaluated (in this case then-do-this). Otherwise, the other branch, else-do-this will be evaluated.

Having the other branch is optional, much like an else term in Ruby or Python. If no alternate branch is provided, when the conditional is evaluated as false, Clojure will return nil.

  1. Truthy and Falsey expressions:
  • Falsey: false , nil
  • Truthy: true , everything else

Notably, the number 0 is a truthy expression in Clojure.

cond

The cond function is for a switch-like control flow. Each condition is stated, followed by the result of that condition.

    (defn pos-neg-or-zero
      "Determines whether or not n is positive, negative, or zero"
      [n]
      (cond
        (< n 0) "negative"
        (> n 0) "positive"
        :else "zero"))
    
    (pos-neg-or-zero 5)
    
    ;=> "positive"
    
    (pos-neg-or-zero -1)
    
    ;=> "negative"
    
    (pos-neg-or-zero 0)
    
    ;=> "zero"

do

The do operator allows you to evaluate multiple expressions in order.

    (do (+ println "hello")
        (+ println "hello again!"))
    
    ;=> hello
    ;=> hello again!
    ;=> nil

Here we have used **println** to print a string as a line.
the **;=>** symbols represent the output of evaluating this **do** expression.
Here each of the sub-expressions have been executed. 
Finally, the return value of the last sub-expression is returned.
(**println** expressions return **nil**)


<a id="orgc45d601"></a>

### when

the **when** operator works like a **if** operator, 
but with no optional second branch. Therefore **when** always returns nil when the Boolean is falsey.
**when** also allows you to evaluate multiple expressions, like **do**.


```clojure
    (when true
      (println "multiple")
      (println "expressions"))
    
    ;=> multiple
    ;=> expressions
    ;=> nil

and

The and operator returns the first falsey value, or if there are no falsey values, the last truthy one.

    (and false true)
    ;=> false
    
    (and (= "a" "a") true 1 0 "last one")
    ;=> "last one"
    
    (and 0 "my string" nil true)
    ;=> nil

or

The or operator returns the first truthy value, or if there are no truthy values, the last value

    (or false true)
    ;=> true
    
    (or 1 0 "last one")
    ;=> 1
    
    (or 0 "my string" nil true)
    ;=> 0

Data Types

Strings

Strings are surrounded in double quotes. A backslash can be used to escape special characters.

    (println "let's print some double quotes: \"\"\"")
    
    ;=> let's print some double quotes: """
    ;=> nil

The function str can be used to make a single string out of multiple string.

    (def first_part "This is the beginning, ")
    (println (str (first_part "and this is the end.")))
    
    ;=> This is the beginning, and this is the end.
    ;=> nil

Numbers

Clojure handles integers, floating point numbers and ratios nativity.

    (+ 1.5 2)
    
    ;=> 3.5
    
    (* 1/2 1/3)
    
    ;=> 1/6

Data Structures

Lists

Lists are collections of values. Lists in Clojure use parentheses, and so look a lot like functions. In order to signify that we are using a list rather than a function to be evaluated, we put a single quote in-front of the list.

    '(1 2 3 4)
    
    ;=> (1 2 3 4)

Note that there is no need for commas between items of a list. Functions such as **first**, **last** and **nth** can be used on lists.

```clojure
    (first '(1 2 3 4))
    
    ;=> 1
    
    (last '(1 2 3 4))
    
    ;=> 4
    
    (nth '(1 2 3 4) 2)
    
    ;=> 3

Note that nth’s second argument is the value of n, which is 0-indexed.

Vectors

Vectors are like lists in some ways, however they have some different properties. They do not require a single quote in-front of of them. They use square braces. Rather than using nth, items can be pulled from a vector using get.

    (get [1 2 3 4] 0)
    
    ;=> 1

Vector elements can be of any type. As with lists.

Maps

Maps are analogous to hashes in Ruby, or dictionaries in Python. They are contained in curly braces and key, value pairs are simply added in that order.

    (def my-map {:first-key "the first one" :second-key "another one"})
    (println (my-map :second-key))
    
```clojure
    ;=> another one

Map values can be of any type. Strings, numbers, nested maps, functions. Anything.

Sets

Sets are collections of unique values. They can ether be hash-sets or sorted sets. Hash-sets are formatted like so:

    #{:a :b "another item" 12}
    
    ;=> #{"another item" 12 :b :a}

Note how it doesn’t retain it’s order.

Calling hash-set on a number of data objects will return a hash-set of them, excluding duplicates.

    (hash-set 1 5 2 5 7)
    
    ;=> #{7 1 2 5}

Keywords

Keywords are mainly used as keys in maps. They always evaluate to themselves.

    :test-keyword
    
    ;=> :test-keyword
    
    (do 
        (def my-map {:a 1 :b 2})
        (:a my-map))
    
    ;=> 1


Core functions

def

the def operator binds a name to a value in Clojure. Generally it is preferable to not re-assign a new value to a name in Clojure.

    (def super-example "this is my great example")
    
    (println super-example)
    
    ;=> this is my great example

cons

Prepends an item to a list.

    (cons 1 '(2 3 4 5 6))
    
    ;=> (1 2 3 4 5 6)


conj

Adds an item to a collection. If it’s a vector, the new item will be added at the end. If it’s a list it’ll be added at the beginning.

    (conj [1 2 3] 4)
    
    ;=> [1 2 3 4]
    
    (conj '(1 2 3) 4)
    
    ;=> (4 1 2 3)

first

Returns the first item in a sequence.

    (first '(:alpha :bravo :charlie))
    
    ;=> :alpha


next

Returns all apart from the first item in a sequence. If the sequence contains only one item next will return nil.

    user=> (next '(:alpha :bravo :charlie))
    
    ;=> (:bravo :charlie)
    
    user=> (next (next '(:one :two :three)))
    
    ;=> (:three)
    
    user=> (next (next (next '(:one :two :three))))
    
    ;=> nil

rest

Like next but always returns a sequence.

    (rest [1 2 3 4 5])           
    
    ;=> (2 3 4 5)
    
    (rest '())
    
    ;=> ()

nth

Returns value at index n.

    (def my-seq ["a" "b" "c" "d"])
    
    (nth my-seq 0)
    
    ;=> "a"
    
    (nth my-seq 1)
    
    ;=> "b"
    
    (nth [] 0)
    
    ;=> IndexOutOfBoundsException ...
    
    (nth [] 0 "nothing found")
    
    ;=> "nothing found"
    
    (nth [0 1 2] 77 1337)
    
    ;=> 1337

take

Takes the first n items from a sequence.

    ;; return a lazy seq of the first 3 items
    (take 3 '(1 2 3 4 5 6))
    
    ;=> (1 2 3)

drop

Discards the first n items from a sequence. Returns a list

    (drop 2 [1 2 3 4])
    
    ;=> (3 4) 

complement

Complement simply inverts the return value of a function that returns a Boolean.

    (defn my-even-test
      [x]
      (even? x))
    
    (def my-odd-test (complement my-even-test))
    
    (my-odd-test 1)
    
    ;=> true

Writing Functions

The Structure of a Function

A main parts of a function are the following:

  • defn
  • The function’s name.
  • A description of the function (aka a docstring).
  • The parameters listed in square brackets.
  • The body of the function.
    (defn my-new-addition-function
      "This is a function that takes two numbers and sums them."
      [first-number second-number]
      (+ first-number second-number))
    
    (my-new-addition-function 3 5)
    ;=> 8

Functions can contain any number of forms. All the forms will be evaluated, unless there are conditionals inside the function. The evaluation of the last form will be the return value of the function.

    (defn many-forms-one-return
      []
      (+ 1 1)
      (+ 1 2)
      (/ 1 2)
      (- 9 3))
    
    (many-forms-one-return)
    
    ;=> 6

Arity

A function’s arity is the number of parameters it takes. Clojure functions support arity overloading, which is polymorphism controlled by the arity of the function.

Airty overloading is added to a function by providing each arity definition in parentheses. Each definition includes an argument list, followed by a function body.

    (defn my-multi-arity-function
      ([one two three]
       (println "Three arguments!"))
    
      ([one two]
       (println "Two arguments!"))
    
      ([one]
       (println "One argument!")))
    
    (my-multi-arity-function 666 "frogs")
    
    ;=> Two arguments!

Using Arity Overloading to provide default arguments

Since a function can call itself recursively, arity overloading can be used to provide default arguments.

    (defn func-with-default-arg
        ([number-to-print]
    	(println (str "here a number for you: " number-to-print)))
        ([]
    	(func-with-default-arg 99)))
    
    (func-with-default-arg 100)
    
    ;=> 100 
    
    (func-with-default-arg)
    
    ;=> 99

Rest parameter

A function can take an unspecified number of arguments. Kind of like argv in a c-style language. Clojure uses an ampersand & followed by a name to signify a rest parameter in the argument list. Named individual arguments can be put before the rest.

    (defn func-using-rest
        [named-arg-a named-arg-b & other-args]
        (println (str "A is : " named-arg-a " and B is : " named-arg-b 
    		  " and the rest sum to: " (reduce + other-args))))
    
    (func-using-rest 10 20 1 2 3 4 5)
    
    ;=> A is : 10 and B is : 20 and the rest sum to: 15

Destructuring

Vectors

The same rest principle can be applied to vectors passed as arguments. We can simply name consecutive items in the vector, followed by a rest parameter.

    (defn destruct-vec
      [[first second & rest] another-arg arg-three]
      (println (str "the second item in the vector is " second
    		" and you also passed " another-arg " and "
    		arg-three)))
    
    (destruct-vec [99 "frog" :keyname] "floop" "gloop")
    
    ;=> the second item in the vector is frog and you also passed floop and gloop

Maps

There are several methods for destructuring maps. We can name the keys, like so:

    (defn destruct-map-1
      [{first-key :a second-key :b}]
      (+ first-key second-key))
    
    (destruct-map-1 {:a 10 :b 20})
    
    ;=> 30

We can also detructure the keys using the :keys keyword.

    (defn destruct-map-2
      [{:keys [first-key second-key]}]
      (+ first-key second-key))
    
    (destruct-map-1 {:a 1/2 :b 1/4})
    
    ;=> 3/4

Anonymous Functions

Anonymous functions, sometimes known as lambda expressions or simply lambdas, are available in Clojure. Here are a couple of syntaxes for lambdas:

    ((fn [a b]
       (* a b)) 9 100)
    
    ;=> 900
    
    (#(* % 3) 2)
    
    ;=> 6

Using the hash syntax we can also give rest parameters. He we have done so, also using the reduce function to sequentially apply the addition.

    (#(reduce + %&) 1 2 3 4 5)
    
    ;=> 15 

The above is a contrived example to show the lambda using a rest parameter. We will revisit reduce below.

Threading

Nesting operations within operations can become difficult to read. The threading syntax can make things clearer.

Thread first and thread last are two different syntaxes to represent this. Thread first uses a ->, Whereas thread last uses a .

    (defn not-threaded
      [a]
      (even? (* 7 (inc a))))
    
    
    (defn thread-first
      [a]
      (->
        (inc a)
        (* 7)
        (even?)))
    
    
    (defn thread-last
      [a]
      (->>
        (even?)
        (* 7)
        (inc a)))


Tests

Writing and running tests

Tests are usually put in test directory, at the same level as the src directory in a Clojure project.

Naming convention is to have the same name as the src file, appended with _test.

    my_project/test/my_code.clj
    
    my_project/src/my_code_test.clj

Tests are simply functions that take no arguments. They are declared like so:

    (deftest my-test
      (println "Here is a test!"))

Tests can be run from the REPL:

    (run-tests)

Also, all a project’s tests can be run from a shell.

    $ lein test

Asserts

An assert in a test can be written with an is. Additional structure can be added with a testing block. Details of the test can be added with an optional string argument to the is.

    (defn my-test
      (testing "let's begin our tests!"
        (is (= 1 2) "tests if one is equal to two")))
    
    ;=> FAIL in (my-test)
    ;=> let's begin our tests!
    ;=> tests if one is equal to two
    ;=> expected: (= 1 2))
    ;=> actual: (not (= 1 2))

lein-test-refresh

This is a Leiningen plugin that live-refreshes tests after you save a file.

To install add this to your plugins in ~/.lein/profiles.clj

    [com.jakemccrary/lein-test-refresh "0.23.0"]

To run it in the shell:

    $ lein test-refresh

test.check

Functional Tools

Apply

The apply function allows us to treat the items in a sequence as arguments to a function. For example.

    (max 2 4 6 8 10)
    
    ;=> 10
    
    (apply max [2 4 6 8 10])
    
    ;=> 10

Partial

The partial function can be used to make higher order functions.

    (def splitter (partial * 0.5))
    
    (splitter 3)
    
    ;=> 1.5

Map

Applies a function to each item in a sequence. If more than one sequence is passed then each corresponding value by index is passed as an argument simultaneously. Returns a lazy sequence.

    (map inc [1 2 3 4 5])
    
    ;=> (2 3 4 5 6)
    
    (map + [1 2 3] [4 5 6])
    
    ;=> (5 7 9)

Reduce

Applies a two argument function across a sequence. Takes the outcome of the previous function all and the next consecutive item in the sequence. This is done repeatedly until the sequence is empty.

    (reduce + [1 2 3 4 5]) 
    
    ;=> 15
    
    (reduce + [])          
    
    ;=> 0
    
    (reduce + 1 [2 3])     
    
    ;=> 6

Filter

Filters a sequence by a predicate.

    (filter even? (range 10))
    
    ;=> (0 2 4 6 8)
    
    
    (filter #(= (count %) 1)
      ["a" "aa" "b" "n" "f" "lisp" "clojure" "q" ""])
    
    ;=> ("a" "b" "n" "f" "q")

Comp

This is used to compose functions from multiple other functions.

    ((comp inc *) 2 3)
    
    ;=> 7

In a quasi-mathematical notation, we can compose functions…

… to create a new function, g, such that:

Note the above will not be rendered correctly in GitHub flavour markdown.

Memoize

This will save the outcome of a function call provided with specified arguments.

    user=> (defn myfunc[a] (println "doing some work") (+ a 10))
    
    user=> (def myfunc-memo (memoize myfunc))
    
    user=> (myfunc-memo 1)
    
    ;=> doing some work
    ;=> 11
    
    user=> (myfunc-memo 1)
    
    ;=> 11
    
    user=> (myfunc-memo 20)
    
    ;=> doing some work
    ;=> 30
    
    user=> (myfunc-memo 20)
    
    ;=> 30


Namespaces

What are namespaces?

Namespaces in Clojure are simmilar to namespaces in C++. They can contain mappings between symbols and values. The current namespace can be refered to like so:

    *ns*

We can see a namespace’s name with the ns-name function. If we were to use this in the REPL, by default we’d get the following response.

    (ns-name *ns*)
    
    ;=> user

When an object is stored with def, this is added to the current namespace.

Again, we can see this in the REPL:

    (def my-obj {:a "value 1" :b "value 2"})
    
    ;=> user/my-obj

We can all of a namespace’s mappings of symbols using ns-interns

    (ns-interns *ns*)
    
    ;=> my-obj #'user/my-obj


Creating namespaces

A namespace can be made with create-ns.

    (create-ns my-new-namespace)

However, it can be more convenient to switch to a new namespace once it’s made. This can be done in a single command, like so:

    (in-ns my-super-namespace)

If the namespace name already exists, the above will simply cause you to move into the given namespace.

Alias

The alias function can be used to give an abbreviated version of a namespace in order to refer to fully qualified symbols more easly.

Suppose we are in the my-super-namespace created above, and we want to refer to the object we previously made in the user namespace using an alias:

    (clojure.core/alias 'u 'user)
    
    u/my-obj
    
    ;=> {:a "value 1" b: "value 2"}


Macros

What are Macros?

Macros allow the compiler to be extended with user code. Several of Clojure’s core constructs which would be primatives in other languages are in fact macros in Clojure. For example when is a macro. We can see this by using macroexpand.

    (macroexpand '(when (pos? a) (println "positive") (/ b a)))
    
    ;=> (if (pos? a) (do (println "positive") (/ a b)))

Here we can see that when is in fact a macro that uses if and do.

Macros are like functions, but they take arguments without evaluating them. Here is a superflous ‘macro’, which is really just behaving like a function.

    (defmacro plus-func
      [a b]
      (+ a b))
    
    (plus-func 1 2)
    
    ;=> 3

It serves no perpose for this function to be declared with defmacro. It should be just a regular function.

Since macros allow us to take arguments without evaluating them, we can use them to alter the expected behaviour of lists.

    (defmacro infix
    ; Here destructuring is used to name the items in the passed list.
      [[first-arg second-arg third-arg]]
      (second-arg first-arg third-arg))
    
    ; Now we can perform simple additions with infix, rather than prefix notation.
    (infix (1 + 2))
    
    ;=> 3	
Written on January 9, 2019