summaryrefslogtreecommitdiff
path: root/doc/cljlib-macros.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/cljlib-macros.md')
-rw-r--r--doc/cljlib-macros.md581
1 files changed, 0 insertions, 581 deletions
diff --git a/doc/cljlib-macros.md b/doc/cljlib-macros.md
deleted file mode 100644
index a29fb0f..0000000
--- a/doc/cljlib-macros.md
+++ /dev/null
@@ -1,581 +0,0 @@
-# Cljlib-macros.fnl (0.3.0)
-Macros for Cljlib that implement various facilities from Clojure.
-
-**Table of contents**
-
-- [`fn*`](#fn*)
-- [`try`](#try)
-- [`def`](#def)
-- [`defonce`](#defonce)
-- [`defmulti`](#defmulti)
-- [`defmethod`](#defmethod)
-- [`into`](#into)
-- [`empty`](#empty)
-- [`when-meta`](#when-meta)
-- [`with-meta`](#with-meta)
-- [`meta`](#meta)
-- [`if-let`](#if-let)
-- [`when-let`](#when-let)
-- [`if-some`](#if-some)
-- [`when-some`](#when-some)
-
-## `fn*`
-Function signature:
-
-```
-(fn* name docstring? ([arglist*] body)*)
-```
-
-Create (anonymous) function of fixed arity.
-Supports multiple arities by defining bodies as lists:
-
-### Examples
-Named function of fixed arity 2:
-
-``` fennel
-(fn* f [a b] (+ a b))
-```
-
-Function of fixed arities 1 and 2:
-
-``` fennel
-(fn* ([x] x)
- ([x y] (+ x y)))
-```
-
-Named function of 2 arities, one of which accepts 0 arguments, and the
-other one or more arguments:
-
-``` fennel
-(fn* f
- ([] nil)
- ([x & xs]
- (print x)
- (f (unpack xs))))
-```
-
-Note, that this function is recursive, and calls itself with less and
-less amount of arguments until there's no arguments, and terminates
-when the zero-arity body is called.
-
-Named functions accept additional documentation string before the
-argument list:
-
-``` fennel
-(fn* cube
- "raise `x` to power of 3"
- [x]
- (^ x 3))
-
-(fn* greet
- "greet a `person`, optionally specifying default `greeting`."
- ([person] (print (.. "Hello, " person "!")))
- ([greeting person] (print (.. greeting ", " person "!"))))
-```
-
-Argument lists follow the same destruction rules as per `let`.
-Variadic arguments with `...` are not supported use `& rest` instead.
-Note that only one arity with `&` is supported.
-
-##### Namespaces
-If function name contains namespace part, defines local variable
-without namespace part, then creates function with this name, sets
-this function to the namespace, and returns it.
-
-This roughly means, that instead of writing this:
-
-``` fennel
-(local ns {})
-
-(fn f [x] ;; we have to define `f` without `ns`
- (if (> x 0) (f (- x 1)))) ;; because we're going to use it in `g`
-
-(set ns.f f)
-
-(fn ns.g [x] (f (* x 100))) ;; `g` can be defined as `ns.g` as it is only exported
-
-ns
-```
-
-It is possible to write:
-
-``` fennel
-(local ns {})
-
-(fn* ns.f [x]
- (if (> x 0) (f (- x 1))))
-
-(fn* ns.g [x] (f (* x 100))) ;; we can use `f` here no problem
-
-ns
-```
-
-It is still possible to call `f` and `g` in current scope without `ns`
-part, so functions can be reused inside the module, and `ns` will hold
-both functions, so it can be exported from the module.
-
-Note that `fn` will not create the `ns` for you, hence this is just a
-syntax sugar. Functions deeply nested in namespaces require exising
-namespace tables:
-
-``` fennel
-(local ns {:strings {}
- :tables {}})
-
-(fn* ns.strings.join
- ([s1 s2] (.. s1 s2))
- ([s1 s2 & strings]
- (join (join s1 s2) (unpack strings)))) ;; call `join` resolves to ns.strings.join
-
-(fn* ns.tables.join
- ([t1 t2]
- (let [res []]
- (each [_ v (ipairs t1)] (table.insert res v))
- (each [_ v (ipairs t2)] (table.insert res v))
- res))
- ([t1 t2 & tables]
- (join (join t1 t2) (unpack tables)))) ;; call to `join` resolves to ns.tables.join
-```
-
-Note that this creates a collision and local `join` overrides `join`
-from `ns.strings`, so the latter must be fully qualified
-`ns.strings.join` when called outside of the function:
-
-``` fennel
-(ns.strings.join "a" "b" "c")
-;; => abc
-(join ["a"] ["b"] ["c"] ["d" "e"])
-;; => ["a" "b" "c" "d" "e"]
-(join "a" "b" "c")
-;; {}
-```
-
-## `try`
-Function signature:
-
-```
-(try body* catch-clause* finally-clause?)
-```
-
-General purpose try/catch/finally macro.
-
-(try expression* catch-clause* finally-clause?)
-
-Wraps its body in `pcall` and checks the return value with `match`
-macro.
-
-Catch-clause is written either as (catch symbol body*), thus acting as
-catch-all, or (catch value body*) for catching specific errors. It is
-possible to have several `catch` clauses. If no `catch` clauses
-specified, an implicit catch-all clause is created.
-
-Finally-clause is optional, and written as (finally body*). If
-present, it must be the last clause in the `try` form, and the only
-`finally` clause. Note that `finally` clause is for side effects
-only, and runs either after succesful run of `try` body, or after any
-`catch` clause body, before returning the result. If no `catch`
-clause is provided `finally` runs in implicit catch-all clause, and
-trows error to upper scope using `error` function.
-
-To throw error from `try` to catch it with `catch` clause use `error`
-or `assert` functions.
-
-### Examples
-Catch all errors, ignore those and return fallback value:
-
-``` fennel
-(fn add [x y]
- (try
- (+ x y)
- (catch _ 0)))
-
-(add nil 1) ;; => 0
-```
-
-Catch error and do cleanup:
-
-``` fennel
->> (let [tbl []]
- (try
- (table.insert tbl "a")
- (table.insert tbl "b" "c")
- (catch _
- (each [k _ (pairs tbl)]
- (tset tbl k nil))))
- tbl)
-{}
-```
-
-Always run some side effect action:
-
-``` fennel
->> (local res (try 10 (finally (print "side-effect!")))
-side-effect!
-nil
->> rese0
->> (local res (try (error 10) (catch 10 nil) (finally (print "side-effect!")))
-side-effect!
-nil
->> res
-nil
-```
-
-
-## `def`
-Function signature:
-
-```
-(def attr-map? name expr)
-```
-
-Wrapper around `local` which can
-declare variables inside namespace, and as local at the same time
-similarly to [`fn*`](#fn*):
-
-``` fennel
-(def ns {})
-(def a 10) ;; binds `a` to `10`
-
-(def ns.b 20) ;; binds `ns.b` and `b` to `20`
-```
-
-`a` is a `local`, and both `ns.b` and `b` refer to the same value.
-
-Additionally metadata can be attached to values, by providing
-attribute map or keyword as first parameter. Only one keyword is
-supported, which is `:mutable`, which allows mutating variable with
-`set` later on:
-
-``` fennel
-;; Bad, will override existing documentation for 299792458 (if any)
-(def {:doc "speed of light in m/s"} c 299792458)
-(set c 0) ;; => error, can't mutate `c`
-
-(def :mutable address "Lua St.") ;; same as (def {:mutable true} address "Lua St.")
-(set address "Lisp St.") ;; can mutate `address`
-```
-
-However, attaching documentation metadata to anything other than
-tables and functions considered bad practice, due to how Lua
-works. More info can be found in [`with-meta`](#with-meta)
-description.
-
-## `defonce`
-Function signature:
-
-```
-(defonce attr-map? name expr)
-```
-
-Works the same as [`def`](#def), but ensures that later `defonce`
-calls will not override existing bindings:
-
-``` fennel
-(defonce a 10)
-(defonce a 20)
-(print a) ;; => prints 10
-```
-
-## `defmulti`
-Function signature:
-
-```
-(defmulti name docstring? dispatch-fn attr-map?)
-```
-
-Create multifunction with
-runtime dispatching based on results from `dispatch-fn`. Returns an
-empty table with `__call` metamethod, that calls `dispatch-fn` on its
-arguments. Amount of arguments passed, should be the same as accepted
-by `dispatch-fn`. Looks for multimethod based on result from
-`dispatch-fn`.
-
-By default, multifunction has no multimethods, see
-[`multimethod`](#multimethod) on how to add one.
-
-## `defmethod`
-Function signature:
-
-```
-(defmethod multifn dispatch-val fnspec)
-```
-
-Attach new method to multi-function dispatch value. accepts the `multi-fn`
-as its first argument, the dispatch value as second, and function tail
-starting from argument list, followed by function body as in
-[`fn*`](#fn).
-
-### Examples
-Here are some examples how multimethods can be used.
-
-#### Factorial example
-Key idea here is that multimethods can call itself with different
-values, and will dispatch correctly. Here, `fac` recursively calls
-itself with less and less number until it reaches `0` and dispatches
-to another multimethod:
-
-``` fennel
-(defmulti fac (fn [x] x))
-
-(defmethod fac 0 [_] 1)
-(defmethod fac :default [x] (* x (fac (- x 1))))
-
-(fac 4) ;; => 24
-```
-
-`:default` is a special method which gets called when no other methods
-were found for given dispatch value.
-
-#### Multi-arity dispatching
-Multi-arity function tails are also supported:
-
-``` fennel
-(defmulti foo (fn* ([x] [x]) ([x y] [x y])))
-
-(defmethod foo [10] [_] (print "I've knew I'll get 10"))
-(defmethod foo [10 20] [_ _] (print "I've knew I'll get both 10 and 20"))
-(defmethod foo :default ([x] (print (.. "Umm, got" x)))
- ([x y] (print (.. "Umm, got both " x " and " y))))
-```
-
-Calling `(foo 10)` will print `"I've knew I'll get 10"`, and calling
-`(foo 10 20)` will print `"I've knew I'll get both 10 and 20"`.
-However, calling `foo` with any other numbers will default either to
-`"Umm, got x"` message, when called with single value, and `"Umm, got
-both x and y"` when calling with two values.
-
-#### Dispatching on object's type
-We can dispatch based on types the same way we dispatch on values.
-For example, here's a naive conversion from Fennel's notation for
-tables to Lua's one:
-
-``` fennel
-(defmulti to-lua-str (fn [x] (type x)))
-
-(defmethod to-lua-str :number [x] (tostring x))
-(defmethod to-lua-str :table [x] (let [res []]
- (each [k v (pairs x)]
- (table.insert res (.. "[" (to-lua-str k) "] = " (to-lua-str v))))
- (.. "{" (table.concat res ", ") "}")))
-(defmethod to-lua-str :string [x] (.. "\"" x "\""))
-(defmethod to-lua-str :default [x] (tostring x))
-```
-
-And if we call it on some table, we'll get a valid Lua table:
-
-``` fennel
-(print (to-lua-str {:a {:b 10}}))
-;; prints {["a"] = {["b"] = 10}}
-
-(print (to-lua-str [:a :b :c [:d {:e :f}]]))
-;; prints {[1] = "a", [2] = "b", [3] = "c", [4] = {[1] = "d", [2] = {["e"] = "f"}}}
-```
-
-Which we can then reformat as we want and use in Lua if we want.
-
-## `into`
-Function signature:
-
-```
-(into to from)
-```
-
-Transform one table into another. Mutates first table.
-
-Transformation happens in runtime, but type deduction happens in
-compile time if possible. This means, that if literal values passed
-to `into` this will have different effects for associative tables and
-vectors:
-
-``` fennel
-(into [1 2 3] [4 5 6]) ;; => [1 2 3 4 5 6]
-(into {:a 1 :c 2} {:a 0 :b 1}) ;; => {:a 0 :b 1 :c 2}
-```
-
-Conversion between different table types is also supported:
-
-``` fennel
-(into [] {:a 1 :b 2 :c 3}) ;; => [[:a 1] [:b 2] [:c 3]]
-(into {} [[:a 1] [:b 2]]) ;; => {:a 1 :b 2}
-```
-
-Same rules apply to runtime detection of table type, except that this
-will not work for empty tables:
-
-``` fennel
-(local empty-table {})
-(into empty-table {:a 1 :b 2}) ;; => [[:a 1] [:b 2]]
-``` fennel
-
-If table is empty, `into` defaults to sequential table, because it
-allows safe conversion from both sequential and associative tables.
-
-Type for non empty tables hidden in variables can be deduced at
-runtime, and this works as expected:
-
-``` fennel
-(local t1 [1 2 3])
-(local t2 {:a 10 :c 3})
-(into t1 {:a 1 :b 2}) ;; => [1 2 3 [:a 1] [:b 2]]
-(into t2 {:a 1 :b 2}) ;; => {:a 1 :b 2 :c 3}
-```
-
-`cljlib.fnl` module provides two additional functions `vector` and
-`hash-map`, that can create empty tables, which can be distinguished
-at runtime:
-
-``` fennel
-(into (vector) {:a 1 :b 2}) ;; => [[:a 1] [:b 2]]
-(into (hash-map) [[:a 1 :b 2]]) ;; => {:a 1 :b 2}
-```
-
-## `empty`
-Function signature:
-
-```
-(empty x)
-```
-
-Return empty table of the same kind as input table `x`, with
-additional metadata indicating its type.
-
-### Example
-Creating a generic `map` function, that will work on any table type,
-and return result of the same type:
-
-``` fennel
-(fn map [f tbl]
- (let [res []]
- (each [_ v (ipairs (into [] tbl))]
- (table.insert res (f v)))
- (into (empty tbl) res)))
-
-(map (fn [[k v]] [(string.upper k) v]) {:a 1 :b 2 :c 3})
-;; => {:A 1 :B 2 :C 3}
-(map #(* $ $) [1 2 3 4])
-;; [1 4 9 16]
-```
-See [`into`](#into) for more info on how conversion is done.
-
-## `when-meta`
-Function signature:
-
-```
-(when-meta [& body])
-```
-
-Wrapper that compiles away if metadata support was not enabled. What
-this effectively means, is that everything that is wrapped with this
-macro will disappear from the resulting Lua code if metadata is not
-enabled when compiling with `fennel --compile` without `--metadata`
-switch.
-
-## `with-meta`
-Function signature:
-
-```
-(with-meta value meta)
-```
-
-Attach metadata to a value. When metadata feature is not enabled,
-returns the value without additional metadata.
-
-``` fennel
->> (local foo (with-meta (fn [...] (let [[x y z] [...]] (+ x y z)))
- {:fnl/arglist ["x" "y" "z" "..."]
- :fnl/docstring "sum first three values"}))
->> (doc foo)
-(foo x y z ...)
- sum first three values
-```
-
-## `meta`
-Function signature:
-
-```
-(meta value)
-```
-
-Get `value` metadata. If value has no metadata, or metadata
-feature is not enabled returns `nil`.
-
-### Example
-
-``` fennel
->> (meta (with-meta {} {:meta "data"}))
-;; => {:meta "data"}
-```
-
-### Note
-There are several important gotchas about using metadata.
-
-First, note that this works only when used with Fennel, and only when
-`(require fennel)` works. For compiled Lua library this feature is
-turned off.
-
-Second, try to avoid using metadata with anything else than tables and
-functions. When storing function or table as a key into metatable,
-its address is used, while when storing string of number, the value is
-used. This, for example, may cause documentation collision, when
-you've set some variable holding a number value to have certain
-docstring, and later you've defined another variable with the same
-value, but different docstring. While this isn't a major breakage, it
-may confuse if someone will explore your code in the REPL with `doc`.
-
-Lastly, note that prior to Fennel 0.7.1 `import-macros` wasn't
-respecting `--metadata` switch. So if you're using Fennel < 0.7.1
-this stuff will only work if you use `require-macros` instead of
-`import-macros`.
-
-## `if-let`
-Function signature:
-
-```
-(if-let [binding test] then-branch else-branch)
-```
-
-If test is logical true,
-evaluates `then-branch` with binding-form bound to the value of test,
-if not, yields `else-branch`.
-
-## `when-let`
-Function signature:
-
-```
-(when-let [binding test] & body)
-```
-
-If test is logical true,
-evaluates `body` in implicit `do`.
-
-## `if-some`
-Function signature:
-
-```
-(if-some [binding test] then-branch else-branch)
-```
-
-If test is non-`nil`, evaluates
-`then-branch` with binding-form bound to the value of test, if not,
-yields `else-branch`.
-
-## `when-some`
-Function signature:
-
-```
-(when-some [binding test] & body)
-```
-
-If test is non-`nil`,
-evaluates `body` in implicit `do`.
-
-
----
-
-Copyright (C) 2020 Andrey Orst
-
-License: [MIT](https://gitlab.com/andreyorst/fennel-cljlib/-/raw/master/LICENSE)
-
-
-<!-- Generated with Fenneldoc 0.0.6
- https://gitlab.com/andreyorst/fenneldoc -->