This site runs best with JavaScript enabled.

Day 1: Clojure from the Ground Up

Zac Jones

August 03, 2020


Photo by Evgeni Tcherkasski on Unsplash

These are my notes from Clojure From the Ground up by Kyle Kingsbury. They are intended for the #learninpublic run that I'm doing with ClojureFam (Team Phanes)

Chapters 1-3 are defining the basics of Clojure to get you familiar with how the language functions at a basic level.

Everything is data in Clojure. Lists are the basic construct and functions decompose programs into smaller pieces.

Table of Contents

  1. Chapter 1: Welcome
  2. Chapter 2: Basic Types
  3. Chapter 3: Functions

Chapter 1: Welcome

Resources

Notes

  • true, false, nil form the three poles of the Lisp logical system.
  • values - the nouns of programming
  • symbols: references to other values You can escape an expression by 'quoting' it.
    'inc
    '123
    '"foo"
    '(1 2 3)

What you quote is what you will get returned. It delays interpretation of the expression

  • LISP: LISt Processing

  • Lists are core to clujure. They can contain anything (e.g. nil, strings, numbers etc.)

    '(1 (2 (3 ())))

    Above is a nested list which you can think of as a tree. Language is like a tree.. sentances are comprised of clauses, which can be nested, and each clause may have subjects modified by adjectives, and verbs modified by adverbs, and so on

    A sentence in Lisp is a list. It starts with a verb, and is followed by zero or more objects for that verb to act on.

The entire grammar of Lisp: the structure for every expression in the language. We transform expressions by substituting menaings for symbols, and obtain some result. This is the core of Lambda Calculus, and it is the theoretical basis for almost all computer languages.

Chapter 2: Basic Types

Resources

Types

  • Type: a group of values which work in the same way. A Property that some values share, which allows us to organize the world into sets of similar things.
  • types can overlap and intersect each other
  • Types completely subsume one another
  • Every language has a type system. Some languages strict, other's more lax.
  • Clojure has strong types because operations on improper types will not allowed. It's dynamic because types are enforced when the program is run instead of when it's first read by the computer

Integers

  • Long intergers longs use one bit to store the sign (negative or positive) the other 63 bits represent the size of the number Which means... 9223372036854775807 is the longest number that's represented

Fractional numbers

floats and doubles are approximations the ratio type is how to be exact with these numbers.

(0.99999999999999999)
;;-> 1.0
(type 1/3)
;;-> clojure.lang.Ratio

Mathematical operations

(= 3 3.0)
;;-> false
(== 3 3.0)
;;-> true

The operation comes first but you can subtract, multiple and divide numbers how you would expect.

(- 3 1)
;;-> 2

Strings

  • You can make almost anything into a string with str
  • you can combine things into a string with str
  • #... is clojures way of writing regular expressions
  • re-find and re-matches find occurances of regex in a string

Booleans and logic

  • true is positive
  • false and nil are negative
  • we can reasion about true with and, or, not
  • Boolean logic is important for control flow

Symbols

  • the job of symbols is to refer to things, to point to other values
  • when evaluated, symbols are replaced by corresponding values.

Keywords

  • Keywords are like strings but they are specifically intended for use as labels or identifiers.
  • prepend with :
    (type :cat)
    ;;-> clojure.lang.Keyword
  • useful when paired with other values in a collection e.g. a map

Lists

  • ordered collection
  • Lists are a collection - a group of values
  • quote lists with ' to prevent from being evaluated
  • A collection is a container which provides some structure for the thing it holds - which are called elements or members

Vectors

  • Vectors are not evaluated like lists
  • first, second, and nth work for acessing vectors
  • rest and next return 'everything but the first element' in a vector
    (rest [1 2 3])
    ;;-> (2 3)
  • Vectors have index's
  • Vectors are intended for looking up elements by index
    ([:a :b :c] 1)
    ;;-> :b

Sets

  • unordered collection
  • #{...} defines a set.
  • order doesn't matter in a set, if you want order - use a vector
  • sets contain unique values
  • most common opperation is checking if something is inside a set with contains?
  • You can make a set out of any other collection with set

Maps

  • a data structure which associates keys with values
  • maps are surrounded by braces {...}
  • maps can be used as verbs
  • keywords look themselves up in a map

Chapter 3: Functions

What are verbs?

Resources

let bindings

  • let defines a meaning for a symbol within a specific expression

    (let [cats 5] (str "I have " cats " cats."))
    ;;->"I have 5 cats."
  • the bindings only apply within the let expression itself

  • clojure doesn't care about spacing, alignment, or newlines

  • later bindings can use previous bindings

    (let [cats 3 legs (* 4 cats)] (str legs " legs all together.")) ;;-> "12 legs all together."

Functions

  • almost all verbs in Clojure are functions
  • functions represent unrealized computation - expressions that aren't evaluated yet.
    ((fn [x] (+ x 1)) 2)
    ;;-> 3

x is this functions argument or parameter

  • functions describe the relationship between arguments and return balues
  • there is shorthand for functions too..
    (#(+ % 1) 2)
    ;;->3
    same function as described above
  • functions are meant to delay evaluation. Isolate patterns of computation

Vars

  • def defines a varaible

  • variables are mutable

    user=> (def astronauts []) #'user/astronauts user=> (count astronauts) 0 user=> (def astronauts ["hey" "heyhey"]) #'user/astronauts user=> (count astronauts) 2 user=>

Defining functions

  • creating a function and binding it to a var has it's own shorthand defn

    user=> (def half (fn [number] (/ number 2))) #'user/half user=> (half 6) 3 user=> (defn half [number] (/ number 2)) #'user/half

  • functions don't have to take arguments

  • arity in a function is how many arguments it takes

  • you can handle multiple 'arities' of functions by defining alternate forms

    user=> (defn half
    #_=> ( [] 1/2)
    #_=> ([x] (/ x 2)))
    #'user/half
    user=> (half)
    1/2
    user=> (half 10)
    5
  • Some functions can take any number of args use the & when defining arguments and the rest of the args will be put into the last one

    user=> (defn vargs
    #_=> [x y & more-args]
    #_=> {:x x
    #_=> :y y
    #_=> :more more-args})
    #'user/vargs
    user=> (vargs 1)
    Execution error (ArityException) at user/eval2048 (REPL:1).
    Wrong number of args (1) passed to: user/vargs
    user=> (vargs 1 2)
    {:x 1, :y 2, :more nil}
    user=> (vargs 1 2 3 4 5 6)
    {:x 1, :y 2, :more (3 4 5 6)}
  • Functions take a docstring which you can add to a function

    user=> (defn half
    #_=> "halves a value"
    #_=> ( [] 1/2)
    #_=> ([x] (/ x 2)))
    #'user/half
    user=> (doc half)
    -------------------------
    user/half
    ([] [x])
    halves a value
    nil

How does type work?

user=> (doc type)
-------------------------
clojure.core/type
([x])
Returns the :type metadata of x, or its Class if none
nil
  • almost every function in clojure is made up of simple functions
  • use source to view the source code of a function
Share article