Here are my notes regarding Clojure; please add things I missed and more important things in the same writing style.

## Basics ##

Clojure
- lisp heritage
    - lisp dialect with functional emphasis
    - well designed thanks to that
    - prefix notation
        - operator comes first
- clojure.jar is the compiler
    - takes code in clojure, compiles to JVM java machine language
- hosted language
    - JVM provides garbage collection and more
- general-purpose language
    - code represented as data structures
    - suited for distributed and parallel computing
    - immutable data, functions, structures
- no fixed types, giving flexibility
    - programming to abstractions
        - so long as a data structure fits sequence operations, it will work with any of the operations that target sequences
        - due to polymorphism
            - base functions with the same name call different function bodies based on what they receive

Leiningen
- builds and manages projects for clojure
    - lein new app alkjalf TO create new project
    - lein uberjar TO create new package

REPL
- read evaluate print loop
- quick feedback cycle
- like a local ssh for a running clojure process



## Concepts ##

Immutability
- cannot be changed, altered, modified once created
    - ensures data integrity and consistency
- once created, cannot be modified - any modifications result in a new data structure
    - vectors, maps, sets are data structures that follow immutability
        - created with conj, assoc, hash-map
        - anything that "updates" just returns a new structure with different values
    - functions also immutable
    - immutability can be technically circumvented with atoms but they still follow rules
- def permanently binds a name to a value
    - defn binds a name to a function

Truthiness/Falsiness
- many functions work on interpreting values based on whether they are truthy or falsey
- true and non-nil values are considered truthy
- false and nil are considered falsey, no concept of undefined

Simplicity
- It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. —Alan Perlis


** Functional Programming
- emphasizes functions as primary building blocks of programs
    - functions can be passed as arguments and returned as values
    - avoiding state and mutable data as much as possible in favour of pure functions
        - pure functions always return the same result, and have no side-effects
- essentially everything is math and numbers, which are pure

** Higher Order Functions
- take other functions as arguments
    - or return them as results
- function that wraps over another function and returns a larger function
- many higher order functions already exist
    - eg. map, filter, reduce

Concurrency
- software trnsactional memory
    - allows multiple threads to share data without locks
    - creates transaction log
        - if attempt is made to modify, check if data has been modified
        - if so, thread can retry or abort
- clojure reduces risk of deadlocks or race conditions returning different results
    - writers don't block readers and commuters
    - readers don't block writers, other readers, or commuters
    - i/o though should be avoided in transactions since they will be retried
- concurrency commands for transactions
    - dosync for multiple operations in one transaction
    - ref-set and alter to atomically modify reference
        - ref-set-validator and alter-validator calls function before reference is changed
    - commute to atomically modify without blocking
- STM and macros assist concurrent application development

Debugging
- identify problem
    - LOOK AT REPL, LINE NUMBER IS USUALLY GIVEN
    - examine log files, run tests, use debugging tools
- isolate issue
    - use REPL to evaluate expressions
- fix problem by changing code
- test to make sure it's fixed
- write things step by step not in big chunks
- "ooooo cannot be cast to clojure.lang.IFn" means something that is not a function is trying to be called as one

Lazy Evaluation
- returns the recipe to generate requested elements
- evaluation is delayed until value needed
    - improves performance by avoiding unnecessary computation
    - evaluates a block at a time, only getting what's needed
    - used for infinite data structures
- lazy-seq is the main function, but many functions return lazy sequences
- opposite is eager

Destructuring
- concisely bind names to values in a collection to write more human-readable code
- supports vectors ([a b c]), maps ({:keys [a b c]}), and more complex nested patterns
    - eg put brackets or braces the defn parameter
        - (defn get-eggs [{first-egg :egg1 second-egg :egg2}]
        - (println first-egg second-egg))
        - (get-eggs {:egg1 "brown" :egg2 "bluebird"}) => "brownbluebird"
    - can also use [{:keys [egg1 egg2]} :as egg-basket]


## Tools ##

Functions
- takes input, performs operations, returns result
    - can take different inputs and return different results
    - immutable, like everything
        - same input always gives same result
- evaluated at runtime
    - executed while program is running
    - evaluated each time it is called
- function call is an operation where operator is a function
    - or a function expression (expression that returns a function)
- always returns last evaluated form
- all functions, built-in or created, are evaluated the same
- can support multi-arity, and perform different operations based on number of arguments

Anonymous Functions
- different forms
    - (fn [] body)
    - (def cool-function (fn [] body)) == (defn cool-function [] body)
    - #(body %)
        - % is argument passed in if not defined
            - can do %1 %2 and %& to indicate order of passed functions
- useful for short-lived, throwaway functions or simple ops


Macros
- didn't cover anything about macros yet
- create custom functions to manipulate code at compile time
- can create data structures etc
- can be used to create domain-specific languages
    - express complex operations in a readable way
- can be used to optimize code
    - eliminate unnecessary structures
    - replace with more efficient ones
- create custom control structures
    - eg. looping constructs
- evaluated only once


** Request

** Response

** Dynamic Variables
- 4 different mechanisms for referencing a changing value
- Vars are usually static but can be made dynamic with ^:dynamic and binding (macro)
- Ref is atomic reference type
    - atomic means that every change to Refs made within a transaction occurs or none do
    - isolated (blind to other transactions while running) and consistent (checked with validator)
    - multiple threads can access and modify data safely and consistently
    - managed with transactions
- Agents
    - manage state in non-blocking, asynchronous way
- Atoms also allow multiple threads
    - not transactional
    - any changes are immediately visible to all threads
    - uncoordinated, independent state

Docstrings
- can be defined with the function or the def

Arity
- functions can be defined to do different things depending on how many parameters are given
- variable numbers of parameters are indicated with &
- can use arity overloading to give default values if not given, etc

Let bindings
- provide clarity by letting new names happen
- evaluate expression once, use result for anything
    - important for expensive calculations, side-effect causing i/o etc, or network calls

Regex
- put regex in quotes after a hash mark


## Data Structures ##

Values
- Booleans
    - true or false
- Symbols
    - identifiers for vars or functions
- Strings
    - double quotes
    - concatenated with str function
- Numbers
    - represented directly
    - can do integers, floats, ratio/fraction no problem
- Keywords
    - primarily used as keys in maps
    - mostly defined as :key-name but can be anything

Collections
- Vectors
    - [collection of indexed elements]
    - 
    - used when order matters, eg access by index
    - manipulate with first, rest, conj etc
    - index starts at 0
    - conj adds to end
- Sets
    - #{unordered collection of unique elements}
    - used when unique elements are necessary or to remove repeated elements from vector
        - duplicates ignored when adding
    - set operations like union, intersection, difference
    - can check membership with get or contains?
        - get returns value or nil, contains returns true or false
- Maps
    - {:key-value "pairs"}
    - used to efficiently find and organize data associated with an element
    - keys must be unique
        - keys can map to only one value, but the value can also be a collection of more key-value pairs
- Lists
    - '(linear collections of values)
    - conj adds to beginning of list
    - used mostly for macros or if you need to append to start




## Function Notes ##
- when
    - different from if in that it performs multiple operations without needing to use do
- when-not complements when, providing a body of expressions to evaluate when a condition is false.
    - cond for multiple branches instead of a simple if or when
    - case for efficient dispatch among multiple potential matches

- -> and ->> 
    - threading macros for more readable function chaining

- or returns first truthy value or last value
- and returns first falsey value or last value **

- Reduce
    - applies a function to elements of a collection to reduce to a single value
    - flattens out sub-collections
    
- letfn for defining local functions within a let binding form

- apply
    - passes a collection's elements' vals to the function as separate arguments

- partial
    - takes function and any number of arguments
    - supplies the function and those arguments as the base for the new function

- complement for the opposite of a boolean

- Filter
    - take-while and drop-while can be more performant if data is sorted

- memoize
    - stores arguments passed to a function and the return value
    - subsequent computes just return the result immediately
    - useful for things that take time to run 

## Other Considerations ##

Namespaces
- isolate and organize code in separate, named contexts to avoid name collisions
    - ns to declare
    - :require to bring things in from other namespaces

Java Interop
- can interoperate with java, calling java methods and using java classes
    - can create java objects, call their methods and access fields

Testing
- unit tests commonly written using the clojure.test library
    - testing pure functions is recommended, immutability helps with reliable tests

Error Handling
- uses exceptions for error handling 
    - try and catch blocks similar to java