diff --git a/fennel.html.markdown b/fennel.html.markdown
new file mode 100644
index 00000000..b6331a63
--- /dev/null
+++ b/fennel.html.markdown
@@ -0,0 +1,775 @@
+---
+language: fennel
+filename: learnfennel.fnl
+contributors:
+ - ["Jesse Wattenbarger", "https://github.com/jjwatt"]
+---
+
+Fennel is a programming language that brings together the simplicity,
+speed, and reach of Lua with the flexibility of a lisp syntax and
+macro system.
+
+
+```fennel
+;; Comments start with semicolons.
+
+;; Fennel is written in lists of things inside parentheses, separated
+;; by whitespace.
+
+;; The first thing in parentheses is a function or macro to call, and
+;; the rest are the arguments.
+
+;; ------------------------- ;;
+;; 1. Primitives & Operators ;;
+;; ------------------------- ;;
+
+;; (local ...) defines a var inside the whole file's scope.
+(local s "walternate") ; Immutable strings like Python.
+
+;; local supports destructuring and multiple value binding.
+;; (covered later).
+
+;; Strings are utf8 byte arrays
+"λx:(μα.α→α).xx" ; can include Unicode characters
+
+;; .. will create a string out of it's arguments.
+;; It will coerce numbers but nothing else.
+(.. "Hello" " " "World") ; => "Hello World"
+
+;; (print ...) will print all arguments with tabs in between
+(print "Hello" "World") ; "Hello World" printed to screen
+
+(local num 42) ;; Numbers can be integer or floating point.
+
+;; Equality is =
+(= 1 1) ; => true
+(= 2 1) ; => false
+
+;; Nesting forms works as you expect
+(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
+
+;; Comparisons
+(> 1 2) ; => false
+(< 1 2) ; => true
+(>= 1 1) ; => true
+(<= 1 2) ; => true
+(not= 1 2) ; => true
+
+;; TODO: find some bitwise operator examples
+;; (lshift 1) ; => 2
+
+;; -------- ;;
+;; 2. Types ;;
+;; -------- ;;
+
+;; Fennel uses Lua's types for booleans, strings & numbers.
+;; Use `type` to inspect them.
+(type 1) ; => "number"
+(type 1.0) ; => "number"
+(type "") ; => "string"
+(type false) ; => "boolean"
+(type nil) ; => "nil"
+
+;; Booleans
+true ; for true
+false ; for false
+(not true) ; => false
+(and 0 false) ; => false
+(or false 0) ; => 0
+
+;; All values other than nil or false are treated as true.
+
+;;,--------
+;;| Binding
+;;`--------
+
+;; Use `let` to bind local vars to values.
+;; Local binding: `me` is bound to "Bob" only within the (let ...)
+(let [me "Bob"]
+ (print "returning Bob")
+ me) ; => "Bob"
+;; Outside the body of the let, the bindings it introduces are no
+;; longer visible. The last form in the body is used as the return
+;; value. `set` does not work on `let` bound locals.
+
+;; `local` introduces a new local inside an existing scope. Similar to
+;; let but without a body argument. Recommended for use at the
+;; top-level of a file for locals which will be used throughout the
+;; file. `set` does not work on `locals`
+(local tau-approx 6.28318)
+
+;; `var` introduces a new local inside an existing scope which may have its
+;; value changed. Identical to local apart from allowing set to work
+;; on it. `set` works on `vars`
+(var x 83)
+
+;; `set` local variable or table field
+;; Changes the value of a variable introduced with `var`. Will not work
+;; on `globals` or `let`/`local`-bound locals.
+(set x (+ x 91)) ; var
+
+;; Can also be used to change a field of a table, even if the table is
+;; bound with `let` or `local`. If the table field name is static, use
+;; `tbl.field`; if the field name is dynamic, use `(. tbl field)`.
+(let [t {:a 4 :b 8}] ; static table field
+ (set t.a 2) t) ; => {:a 2 :b 8}
+
+;; In any context where you can make a new binding, you can use
+;; multiple value binding. Otherwise, you will only capture the first
+;; value.
+(let [x (values 1 2 3)]
+ x) ; => 1
+
+;; `global` set global variable
+;; Sets a global variable to a new value. Note that there is no
+;; distinction between introducing a new global and changing the value
+;; of an existing one. This supports destructuring and multiple-value
+;; binding.
+(global tbl {:a 4 :b 8})
+(global prettyprint (fn [x] (print (fennel.view x))))
+
+;;,--------------------------
+;;| Prettyprint and .fennelrc
+;;`--------------------------
+;; `prettyprint` is a good function to have in your repl
+
+;; Return values in the repl will get pretty-printed, but calling
+;; (print tbl) will emit output like table: 0x55a3a8749ef0. If you
+;; don't already have one, it's recommended for debugging to define a
+;; printer function which calls `fennel.view` on its argument before
+;; printing it.
+(local fennel (require :fennel))
+(fn _G.pp [x] (print
+ (fennel.view x)))
+;; Notice that adding it to `_G` puts the function in the global
+;; table, similar to what `global` would do. The fennel documentation
+;; says that using `_G` is the preferred method of defining and using
+;; globals.
+;; If you add this definition to your ~/.fennelrc
+;; file it will be available in the standard repl.
+(pp tbl)
+
+;;,--------------------------------
+;;| Collections & Sequences: Tables
+;;`--------------------------------
+
+;; Tables are the only compound data structure in Lua and fennel.
+;; Similar to php arrays or js objects, they are
+;; hash-lookup dicts that can also be used as lists.
+
+;; tables can be treated as sequential or non-sequential: as hashmaps
+;; or lists/arrays.
+
+;; Using tables as dictionaries / maps:
+(local t {:key1 "value1" :key2 false})
+
+;; String keys can use dot notation:
+(print t.key1) ;; Prints "value1"
+
+;; Setting table keys and values
+(tset t :newKey {}) ;; Adds a new key/value pair.
+(tset t :key2 nil) ;; Removes key2 from the table.
+;; Literal notation for any (non-nil) value as key
+
+;; length string or table length
+(+ (length [1 2 3 nil 8]) (length "abc")) ; => 8
+
+;; . table lookup looks up a given key in a table. Multiple arguments
+;; will perform nested lookup.
+(. t :key1)
+
+(let [t {:a [2 3 4]}] (. t :a 2)) ; => 3
+
+;; If the field name is a string known at compile time, you don't need
+;; this and can just use table.field (dot notation).
+
+;; Nil-safe ?. table lookup
+;; Looks up a given key in a table. Multiple arguments will perform
+;; nested lookup. If any subsequent keys is not presnet, will
+;; short-circuit to nil.
+(?. t :key1) ; => "value"
+(let [t {:a [2 3 4]}] (?. t :a 4 :b)) ; => nil
+(let [t {:a [2 3 4 {:b 42}]}] (?. t :a 4 :b)) ; => 42
+
+;; The table module
+(let [t [1 2 3]]
+ (table.insert t 2 "a") ; t is now [1 "a" 2 3]
+ (table.insert t "last") ; now [1 "a" 2 3 "last"]
+ (print (table.remove t)) ; prints "last"
+ (table.remove t 1) ; t is now ["a" 2 3]
+ (print (table.concat t ", "))) ; prints "a, 2, 3"
+
+;; The table.sort function sorts a table in-place, as a
+;; side-effect. It takes an optional comparator function which should
+;; return true when its first argument is less than the second.
+
+;; The table.unpack function returns all the elements in the table as
+;; multiple values. Note that table.unpack is just unpack in Lua 5.1.
+;; This will make `unpack` work in both.
+(var unpack (or _G.unpack table.unpack))
+;; See "prettyprint" section about printing tables
+
+
+;; --------------------- ;;
+;; 3. Basic Flow Control ;;
+;; --------------------- ;;
+;; `if` checks a condition and evaluates the corresponding body.
+;; Accepts any number of condition/body pairs. If an odd number of
+;; args is given, the last value is treated as a catch-all "else,"
+;; similar to cond in other lisps.
+(let [x (math.random 64)]
+ (if (= 0 (% x 10))
+ "multiple of ten"
+ (= 0 (% x 2))
+ "even"
+ "I dunno, something else"))
+;; All values other than nil or false are treated as true.
+
+;; `when` takes a single condition and evalutes the rest as a body if
+;; it's truthy. Intended for side-effects. The last form is the return
+;; value.
+(when launch-missiles?
+ (power-on)
+ (open-doors)
+ (fire))
+
+;;,------------------
+;;| Loops & Iteration
+;;`------------------
+
+;; each: general iteration
+;; `each` runs the body once for each value provided by the iterator.
+(each [key value (pairs mytbl)]
+ (print "executing key")
+ (print (f value)))
+
+;; Any loop can be terminated early by placing an &until clause at the
+;; end of the bindings
+(local out [])
+(each [_ value (pairs tbl) &until (< max-len (length out))]
+ (table.insert out value))
+
+;; `for` is a numeric loop with start, stop and optional step.
+(for [i 1 10 2]
+ (log-number i)
+ (print i)) ;; print odd numbers under 10
+
+;; Like each, loops using for can also be terminated early with an
+;; &until clause
+(var x 0)
+(for [i 1 128 &until (maxed-out? x)]
+ (set x (+ x i)))
+
+;; while loops over a body until a condition is met
+;; Returns nil.
+(var done? false)
+(while (not done?)
+ (print :not-done)
+ (when (< 0.95 (math.random))
+ (set done? true)))
+;; while uses the native lua while loop
+
+;; `do` evaluate multiple forms returning last value
+;; Accepts any number of forms and evaluates all of them in order,
+;; returning the last value. This is used for inserting side-effects
+;; into a form which accepts only a single value, such as in a body of
+;; an if when multiple clauses make it so you can't use when. Some
+;; lisps call this begin or progn.
+(if launch-missiles?
+ (do
+ (power-on)
+ (open-doors)
+ (fire))
+ false-alarm?
+ (promote lt-petrov))
+
+;; Many functions and macros like fn & let have an implicit do at the
+;; start, so you don't have to add it to use multiple forms.
+
+;;,-----------------------
+;;| `collect` & `icollect`
+;;`-----------------------
+;; icollect, collect: table comprehension macros
+
+;; The icollect macro takes a "iterator binding table" in the same
+;; format that `each` takes, and returns a sequential table containing
+;; all the values produced by each iteration of the macro's body. This
+;; is similar to how map works in several other languages, but it is a
+;; macro, not a function.
+
+;; If the value is nil, it is omitted from the return table. This is
+;; analogous to filter in other languages.
+(icollect [_ v (ipairs [1 2 3 4 5 6])]
+ (if (< 2 v) (* v v)))
+;; -> [9 16 25 36]
+;; equivalent to:
+(let [tbl []]
+ (each [_ v (ipairs [1 2 3 4 5 6])]
+ (tset tbl (+ (length tbl) 1) (if (< 2 v) (* v v))))
+ tbl)
+
+;; The `collect` macro is almost identical, except that the body should
+;; return two things: a key and a value.
+(collect [k v (pairs {:apple "red" :orange "orange" :lemon "yellow"})]
+ (if (not= v "yellow")
+ (values (.. "color-" v) k)))
+;; -> {:color-orange "orange" :color-red "apple"}
+;; equivalent to:
+(let [tbl {}]
+ (each [k v (pairs {:apple "red" :orange "orange"})]
+ (if (not= v "yellow")
+ (match (values (.. "color-" v) k)
+ (key value) (tset tbl key value))))
+ tbl)
+
+;; If the key and value are given directly in the body of collect and
+;; not nested in an outer form, then the `values` call can be omitted
+;; for brevity
+(collect [k v (pairs {:a 85 :b 52 :c 621 :d 44})]
+ k (* v 5))
+;; -> {:a 425 :b 260 :c 3105 :d 220}
+
+;; If the index and value are given directly in the body of collect and
+;; not nested in an outer form, then the values can be omitted for
+;; brevity
+(icollect [_ x (ipairs [2 3]) &into [9]]
+ (* x 11))
+;; -> [9 22 33]
+
+;; accumulate
+;; Runs through an iterator and performs accumulation, similar to fold
+;; and reduce commonly used in functional programming languages. Like
+;; collect and icollect, it takes an iterator binding table and an
+;; expression as its arguments. The difference is that in accumulate,
+;; the first two items in the binding table are used as an
+;; "accumulator" variable and its initial value. For each iteration
+;; step, it evaluates the given expression and its value becomes the
+;; next accumulator variable. accumulate returns the final value of
+;; the accumulator variable.
+(accumulate [sum 0
+ i n (ipairs [10 20 30 40])]
+ (+ sum n)) ; -> 100
+
+;; `faccumulate` range accumulation Identical to accumulate, but
+;; instead of taking an iterator and the same bindings as `each`, it
+;; accepts the same bindings as `for` and will iterate the numerical
+;; range. Accepts `&until` just like `for` and `accumulate`.
+(faccumulate [n 0 i 1 5] (+ n i)) ; => 15
+
+;; `fcollect` range comprehension Similarly to `icollect`, `fcollect`
+;; provides a way of building a sequential table. Unlike `icollect`,
+;; instead of an iterator it traverses a range, as accepted by the
+;; `for` special. The `&into` and `&until` clauses work the same as in
+;; `icollect.`
+(fcollect [i 0 10 2]
+ (if (> i 2) (* i i)))
+;; -> [16 36 64 100]
+;; equivalent to:
+(let [tbl {}]
+ (for [i 0 10 2]
+ (if (> i 2)
+ (table.insert tbl (* i i))))
+ tbl)
+
+;; `values` Returns multiple values from a function. Usually used to
+;; signal failure by returning nil followed by a message.
+(fn [filename]
+ (if (valid-file-name? filename)
+ (open-file filename)
+ (values nil (.. "Invalid filename: " filename))))
+
+;; See the Destructuring and Matching sections for more advanced Flow
+;; Control.
+
+;; ------------ ;;
+;; 4. Functions ;;
+;; ------------ ;;
+
+;; Use fn to create new functions. A function always returns its last
+;; statement.
+(fn [] "Hello World") ; => #
+
+; (You need extra parens to call it)
+((fn [] "Hello World")) ; => "Hello World"
+
+;; Assign a function to a local variable
+(local hello-world (fn [] "Hello World"))
+(hello-world) ; => "Hello World"
+
+;; You can use fn and name a function.
+(fn hello-world [] "Hello World") ; => "Hello World"
+
+;; The [] is the list of arguments to the function.
+(fn hello [name]
+ (.. "Hello " name))
+(hello "Steve") ; => "Hello Steve"
+
+;; Will accept any number of arguments. ones in excess of the declared
+;; ones are ignored, and if not enough arguments are supplied to cover
+;; the declared ones, the remaining ones are given values of nil.
+
+;; Providing a name that's a table field will cause it to be inserted
+;; in a table instead of bound as a local
+(local functions {})
+
+(fn functions.p [x y z]
+ (print (* x (+ y z))))
+
+;; equivalent to:
+(set functions.p (fn [x y z]
+ (print (* x (+ y z)))))
+
+;; Like Lua, functions in Fennel support tail-call optimization,
+;; allowing (among other things) functions to recurse indefinitely
+;; without overflowing the stack, provided the call is in a tail
+;; position.
+(fn factorial [x acc]
+ (if (= 0 x)
+ acc
+ (factorial (- x 1) (* x acc))))
+(factorial 5 1) ;; -> 120
+;; The final form in this and all other function forms is used as the
+;; return value.
+
+;; (lambda [...])
+;; Creates a function like fn does, but throws an error at runtime if
+;; any of the listed arguments are nil, unless its identifier begins
+;; with ?.
+(lambda [x ?y z]
+ (print (- x (* (or ?y 1) z))))
+
+;; Note that the Lua runtime will fill in missing arguments with nil
+;; when they are not provided by the caller, so an explicit nil
+;; argument is no different than omitting an argument.
+
+;; Docstrings and metadata
+;; The fn, lambda, λ and macro forms accept an optional docstring.
+(fn pxy [x y]
+ "Print the sum of x and y"
+ (print (+ x y)))
+
+;; Hash function literal shorthand
+
+;; hashfn is a special function that you can abbreviate as #
+;; #foo expands to (hashfn foo)
+
+;; Hash functions are anonymous functions of one form, with implicitly
+;; named arguments.
+
+#(+ $1 $2) ;; same as
+(hashfn (+ $1 $2)) ; implementation detail; don't use directly
+;; same as
+(fn [a b] (+a b))
+
+;; A lone $ in a hash function is treated as ana alias for $1.
+#(+ $ 1)
+
+#$ ; same as (fn [x] x) (aka the identity function
+#val ; same as (fn [] val)
+#[$1 $2 $3] ; same as (fn [a b c] [a b c])
+
+;; ---------------------------------------------;;
+;; 5. Destructuring, Binding & Pattern Matching ;;
+;; ---------------------------------------------;;
+;; Any time you bind a local, you can destructure it if the value is a
+;; table or a function call which returns multiple values
+(let [(x y z) (unpack [10 9 8])]
+ (+ x y z)) ; => 27
+
+(let [[a b c] [1 2 3]]
+ (+ a b c)) ; => 6
+;; If a table key is a string with the same name as the local you want
+;; to bind to, you can use shorthand of just : for the key name
+;; followed by the local name. This works for both creating tables and
+;; destructuring them.
+(let [{:msg message : val} {:msg "hello there" :val 19}]
+ (print message)
+ val) ; prints "hello there" and returns 19
+
+;; When destructuring a sequential table, you can capture all the
+;; remainder of the table in a local by using &
+(let [[a b & c] [1 2 3 4 5 6]]
+ (table.concat c ",")) ; => "3,4,5,6"
+
+;; When destructuring a non-sequential table, you can capture the
+;; original table along with the destructuring by using &as
+(let [{:a a :b b &as all} {:a 1 :b 2 :c 3 :d 4}]
+ (+ a b all.c all.d)) ; => 10
+
+;;,-----------------------
+;;| Multiple value binding
+;;`-----------------------
+;; In most contexts where you can make a new binding, you can use
+;; multiple value binding.
+(let [x (values 1 2 3)] x) ; = > 1
+(let [(file-handle message code) (io.open "fooblah.blah")]
+ message) ; => "fooblah.blah: No such file or directory"
+(do
+ (local (_ _ z) (table.unpack [:a :b :c :d :e]))
+ z) ; => c
+
+;; tset sets the field of a given table to a new value.
+(let [tbl {:d 32}
+ field :d]
+ (tset tbl field 19) tbl) ; => {:d 19}
+;; You can provide multiple successive field names to perform
+;; nested sets.
+(let [tbl {:a
+ {:b {}}}
+ field :c]
+ (tset tbl :a :b field "d") tbl) ; => .. .. {:a {:b {:c "d"}}}
+
+;;,------------------------
+;;| `case` pattern matching
+;;`------------------------
+;; Evaluates its first argument, then searches thru the subsequent
+;; pattern/body clauses to find one where the pattern matches the
+;; value, and evaluates the corresponding body. Pattern matching can
+;; be thought of as a combination of destructuring and conditionals.
+(case mytable
+ 59 :will-never-match-hopefully
+ [9 q 5] (print :q q)
+ [1 a b] (+ a b))q
+
+;; Patterns can be tables, literal values, or symbols. Any symbol is
+;; implicitly checked to be not nil. Symbols can be repeated in an
+;; expression to check for the same value.
+(case mytable
+ ;; the first and second values of mytable are not nil and are the same value
+ [a a] (* a 2)
+ ;; the first and second values are not nil and are not the same value
+ [a b] (+ a b))
+
+;; It's important to note that expressions are checked in order! In
+;; the above example, since [a a] is checked first
+
+;; You may allow a symbol to optionally be nil by prefixing it with ?.
+(case mytable
+ ;; not-nil, maybe-nil
+ [a ?b] :maybe-one-maybe-two-values
+ ;; maybe-nil == maybe-nil, both are nil or both are the same value
+ [?a ?a] :maybe-none-maybe-two-same-values
+ ;; maybe-nil, maybe-nil
+ [?a ?b] :maybe-none-maybe-one-maybe-two-values)
+
+;; Symbols prefixed by an _ are ignored and may stand in as positional
+;; placeholders or markers for "any" value - including a nil value. A
+;; single _ is also often used at the end of a case expression to
+;; define an "else" style fall-through value.
+(case mytable
+ ;; not-nil, anything
+ [a _b] :maybe-one-maybe-two-values
+ ;; anything, anything (different to the previous ?a example!)
+ ;; note this is effectively the same as []
+ [_a _a] :maybe-none-maybe-one-maybe-two-values
+ ;; anything, anything
+ ;; this is identical to [_a _a] and in this example would never actually match.
+ [_a _b] :maybe-none-maybe-one-maybe-two-values
+ ;; when no other clause matched, in this case any non-table value
+ _ :no-match)
+
+;; You can match with multiple return values with
+;; parenthesis, like you can with destructuring `let`
+(case (io.open "/some/file")
+ (nil msg) (report-error msg)
+ f (read-file f))
+
+;;,--------------
+;;| Guard Clauses
+;;`--------------
+;; If you need to match on something more general than
+;; a structure, use guard clauses:
+(case [91 12 53]
+ (where [a b c] (= 5 a)) :will-not-match
+ (where [a b c]
+ (= 0 (math.fmod
+ (+ a b c) 2))
+ (= 91 a))
+ c) ; -> 53
+;; Each form after the pattern is a condition. All conditions must
+;; evaluate to true for the pattern to match.
+
+;; If several patterns share the same body & guards, such patterns can
+;; be with the `or` special in the `where` clause.
+(case [5 1 2]
+ (where (or [a 3 9] [a 1 2]) (= 5 a))
+ "Either [5 3 9] or [5 1 2]"
+ _ "anything else")
+
+;; Symbols bound inside a case pattern are independent from any
+;; existing symbols in the current scope, Sometimes it may be
+;; desirable to match against an existing value in the outer scope. To
+;; do this we can "pin" a binding inside the pattern with an existing
+;; outer binding with the unary (= binding-name) form. The unary (=
+;; binding-name) form is only valid in a case pattern and must be
+;; inside a (where) guard.
+(let [x 1]
+ (case [1]
+ ;; 1 == 1
+ (where [(= x)]) x
+ _ :no-match)) ; -> 1
+;; Pinning is only required inside the pattern. Outer bindings are
+;; automatically available inside guards and bodies as long as the
+;; name has not been rebound in the pattern.
+
+;; Note: The case macro can be used in place of the if-let macro from
+;; Clojure. The reason Fennel doesn't have if-let is that case makes
+;; it redundant.
+
+;;,-------------------------
+;;| `match` pattern matching
+;;`-------------------------
+;; match is conceptually equivalent to case, except symbols in the
+;; patterns are always pinned with outer-scope symbols if they exist.
+(let [x 95]
+ (match [52 85 95]
+ [b a a] :no ; because a=85 and a=95
+ [x y z] :no ; because x=95 and x=52
+ [a b x] :yes)) ; a and b are fresh values while x=95 and x=95
+
+;;,-----------------------------------------------------
+;;| `case-try` & `match-try` for matching multiple steps
+;;`-----------------------------------------------------
+;; Evaluates a series of pattern matching steps. The value from the
+;; first expression is matched against the first pattern. If it
+;; matches, the first body is evaluated and its value is matched
+;; against the second pattern, etc.
+;;
+;; If there is a (catch pat1 body1 pat2 body2 ...) form at the end,
+;; any mismatch from the steps will be tried against these patterns in
+;; sequence as a fallback just like a normal case. If no catch pattern
+;; matches, nil is returned.
+;;
+;; If there is no catch, the mismatched value will be returned as the
+;; value of the entire expression.
+(fn handle [conn token]
+ (case-try (conn:receive :*l)
+ input (parse input)
+ (command-name params (= token)) (commands.get command-name)
+ command (pcall command (table.unpack params))
+ (catch
+ (_ :timeout) nil
+ (_ :closed) (pcall disconnect conn "connection closed")
+ (_ msg) (print "Error handling input" msg))))
+
+;; This is useful when you want to perform a series of steps, any of
+;; which could fail. The catch clause lets you keep all your error
+;; handling in one place. Note that there are two ways to indicate
+;; failure in Fennel and Lua: using the assert/error functions or
+;; returning nil followed by some data representing the failure. This
+;; form only works on the latter, but you can use pcall to transform
+;; error calls into values.
+
+;; `match-try` for matching multiple steps
+;; Unlike case-try, match-try will pin values in a given catch block
+;; with those in the original steps.
+(fn handle [conn token]
+ (match-try (conn:receive :*l)
+ input (parse input)
+ (command-name params token) (commands.get command-name)
+ command (pcall command (table.unpack params))
+ (catch
+ (_ :timeout) nil
+ (_ :closed) (pcall disconnect conn "connection closed")
+ (_ msg) (print "Error handling input" msg))))
+
+
+;; ---------;;
+;; 6. Other ;;
+;; ---------;;
+
+;; The `:` method call
+;; Looks up a function in a table and calls it with the table as its
+;; first argument. This is a common idiom in many Lua APIs, including
+;; some built-in ones. Just like Lua, you can perform a method call by
+;; calling a function name where : separates the table variable and
+;; method name.
+(let [f (assert (io.open "hello" "w"))]
+ (f:write "world")
+ (f:close))
+
+;; If the name of the method or the table containing it isn't fixed,
+;; you can use : followed by the table and then the method's name to
+;; allow it to be a dynamic string instead
+(let [f (assert (io.open "hello" "w"))
+ method1 :write
+ method2 :close]
+ (: f method1 "world")
+ (: f method2))
+
+;; Unlike Lua, there's nothing special about defining functions that
+;; get called this way; typically it is given an extra argument called
+;; self but this is just a convention; you can name it anything.
+(local t {})
+(fn t.enable [self]
+ (set self.enabled? true))
+(t:enable)
+
+
+;; ->, ->>, -?> and -?>> threading macros The -> macro takes its first
+;; value and splices it into the second form as the first
+;; argument. The result of evaluating the second form gets spliced
+;; into the first argument of the third form, and so on.
+(-> 52
+ (+ 91 2) ; (+ 52 91 2)
+ (- 8) ; (- (+ 52 91 2) 8)
+ (print "is the answer")) ; (print (- (+ 52 91 2) 8) "is the answer")
+
+;; The ->> macro works the same, except it splices it into the last
+;; position of each form instead of the first. -?> and -?>>, the
+;; thread maybe macros, are similar to -> & ->> but they also do
+;; checking after the evaluation of each threaded form. If the result
+;; is false or nil then the threading stops and the result is
+;; returned. -?> splices the threaded value as the first argument,
+;; like ->, and -?>> splices it into the last position, like ->>.
+;; This example shows how to use them to avoid accidentally indexing a
+;; nil value
+(-?> {:a {:b {:c 42}}}
+ (. :a)
+ (. :missing)
+ (. :c)) ; -> nil
+(-?>> :a
+ (. {:a :b})
+ (. {:b :missing})
+ (. {:c 42})) ; -> nil
+;; While -> and ->> pass multiple values thru without any trouble, the
+;; checks in -?> and -?>> prevent the same from happening there
+;; without performance overhead, so these pipelines are limited to a
+;; single value.
+
+;; doto
+;; Similarly, the doto macro splices the first value into subsequent
+;; forms. However, it keeps the same value and continually splices the
+;; same thing in rather than using the value from the previous form
+;; for the next form.
+(doto (io.open "/tmp/err.log")
+ (: :write contents)
+ (: :close))
+;; equivalent to:
+(let [x (io.open "/tmp/err.log")]
+ (: x :write contents)
+ (: x :close)
+ x)
+
+;; tail!
+;; the tail! form asserts that its argument is called in a tail
+;; position. You can use this when the code depends on tail call
+;; optimization; that way if the code is changed so that the recursive
+;; call is no longer in the tail position, it will cause a compile
+;; error instead of overflowing the stack later on large data sets.
+(fn process-all [data i]
+ (case (process (. data i))
+ :done (print "Process completed.")
+ :next (process-all data (+ i 1))
+ :skip (do (tail! (process-all data (+ i 2)))
+;; ^^^^^ Compile error: Must be in tail position
+ (print "Skipped" (+ i 1)))))
+
+```
+### Further Reading
+
+The [fennel website] (https://fennel-lang.org/) is the best resource
+on fennel. It links to the [fennel setup guide]
+(https://fennel-lang.org/setup) and to the [fennel reference manual]
+(https://fennel-lang.org/reference). This docuement borrows heavily in
+parts from the fennel reference manual.