From d8b1692acf1528215408b472d0b6649122860dac Mon Sep 17 00:00:00 2001 From: Andrey Orst Date: Sun, 15 Nov 2020 16:43:54 +0300 Subject: feature(doc): more examples in documentation --- cljlib.fnl | 280 ++++++++++++- doc/0.1.0/cljlib-macros.md | 288 ------------- doc/0.1.0/cljlib.md | 674 ------------------------------ doc/cljlib-macros.md | 288 +++++++++++++ doc/cljlib.md | 991 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1540 insertions(+), 981 deletions(-) delete mode 100644 doc/0.1.0/cljlib-macros.md delete mode 100644 doc/0.1.0/cljlib.md create mode 100644 doc/cljlib-macros.md create mode 100644 doc/cljlib.md diff --git a/cljlib.fnl b/cljlib.fnl index d429d09..d5c65c5 100644 --- a/cljlib.fnl +++ b/cljlib.fnl @@ -8,13 +8,35 @@ (require-macros :cljlib-macros) (fn* core.vector - "Constructs sequential table out of it's arguments." + "Constructs sequential table out of it's arguments. + +Sets additional metadata for function [`vector?`](#vector?) to work. + +# Examples + +``` fennel +(local v (vector 1 2 3 4)) +(assert (eq v [1 2 3 4])) +```" [& args] (setmetatable args {:cljlib/table-type :seq})) (fn* core.apply "Apply `f` to the argument list formed by prepending intervening -arguments to `args`." +arguments to `args`, adn `f` must support variadic amount of +arguments. + +# Examples +Applying `print` to different arguments: + +``` fennel +(apply print [1 2 3 4]) +;; prints 1 2 3 4 +(apply print 1 [2 3 4]) +;; => 1 2 3 4 +(apply print 1 2 3 4 5 6 [7 8 9]) +;; => 1 2 3 4 5 6 7 8 9 +```" ([f args] (f (unpack args))) ([f a args] (f a (unpack args))) ([f a b args] (f a b (unpack args))) @@ -34,24 +56,89 @@ arguments to `args`." ;; predicate functions (fn* core.map? - "Check whether `tbl` is an associative table." + "Check whether `tbl` is an associative table. + +Non empty associative tables are tested for two things: +- `next` returns the key-value pair, +- key, that is returned by the `next` is not equal to `1`. + +Empty tables can't be analyzed with this method, and `map?` will +return `false`. If you need this test pass for empty table, see +[`hash-map`](#hash-map) for creating tables that have additional +metadata attached for this test to work. + +# Examples +Non empty tables: + +``` fennel +(assert (map? {:a 1 :b 2})) + +(local some-table {:key :value}) +(assert (map? some-table)) +``` + +Empty tables: + +``` fennel +(local some-table {}) +(assert (not (map? some-table))) +``` + +Empty tables created with [`hash-map`](#hash-map) will pass the test: + +``` fennel +(local some-table (hash-map)) +(assert (map? some-table)) +```" [tbl] (if (= (type tbl) :table) (if-let [t (fast-table-type tbl)] (= t :table) (let [(k _) (next tbl)] (and (not= k nil) - (or (not= (type k) :number) - (not= k 1))))))) + (not= k 1)))))) (fn* core.vector? - "Check whether `tbl` is an sequential table." + "Check whether `tbl` is an sequential table. + +Non empty sequential tables are tested for two things: +- `next` returns the key-value pair, +- key, that is returned by the `next` is equal to `1`. + +Empty tables can't be analyzed with this method, and `vector?` will +always return `false`. If you need this test pass for empty table, +see [`vector`](#vector) for creating tables that have additional +metadata attached for this test to work. + +# Examples +Non empty vector: + +``` fennel +(assert (vector? [1 2 3 4])) + +(local some-table [1 2 3]) +(assert (vector? some-table)) +``` + +Empty tables: + +``` fennel +(local some-table []) +(assert (not (vector? some-table))) +``` + +Empty tables created with [`vector`](#vector) will pass the test: + +``` fennel +(local some-table (hash-map)) +(assert (vector? some-table)) +```" [tbl] (if (= (type tbl) :table) (if-let [t (fast-table-type tbl)] (= t :seq) (let [(k _) (next tbl)] - (and (not= k nil) (= (type k) :number) (= k 1)))))) + (and (not= k nil) (= k 1)))))) (fn* core.nil? @@ -60,7 +147,7 @@ arguments to `args`." ([x] (= x nil))) (fn* core.zero? - "Test if value is zero." + "Test if value is equal to zero." [x] (= x 0)) @@ -105,7 +192,9 @@ arguments to `args`." (= x false)) (fn* core.int? - "Test if `x` is a number without floating point data." + "Test if `x` is a number without floating point data. + +Number is rounded with `math.floor` and compared with original number." [x] (and (= (type x) :number) (= x (math.floor x)))) @@ -146,10 +235,36 @@ arguments to `args`." (fn* core.seq "Create sequential table. + Transforms original table to sequential table of key value pairs stored as sequential tables in linear time. If `col` is an -associative table, returns `[[key1 value1] ... [keyN valueN]]` table. -If `col` is sequential table, returns its shallow copy." +associative table, returns sequential table of vectors with key and +value. If `col` is sequential table, returns its shallow copy. + +# Examples +Sequential tables remain as is: + +``` fennel +(seq [1 2 3 4]) +;; [1 2 3 4] +``` + +Associative tables are transformed to format like this `[[key1 value1] +... [keyN valueN]]` and order is non deterministic: + +``` fennel +(seq {:a 1 :b 2 :c 3}) +;; [[:b 2] [:a 1] [:c 3]] +``` + +See `into` macros for transforming this back to associative table. +Additionally you can use [`conj`](#conj) and [`apply`](#apply) with +[`hash-map`](#hash-map): + +``` fennel +(apply conj (hash-map) [:c 3] [[:a 1] [:b 2]]) +;; => {:a 1 :b 2 :c 3} +```" [col] (let [res (empty [])] (match (type col) @@ -205,7 +320,43 @@ If `col` is sequential table, returns its shallow copy." col))) (fn* core.conj - "Insert `x` as a last element of indexed table `tbl`. Modifies `tbl`" + "Insert `x` as a last element of a table `tbl`. + +If `tbl` is a sequential table or empty table, inserts `x` and +optional `xs` as final element in the table. + +If `tbl` is an associative table, that satisfies [`map?`](#map?) test, +insert `[key value]` pair into the table. + +Mutates `tbl`. + +# Examples +Adding to sequential tables: + +``` fennel +(conj [] 1 2 3 4) +;; => [1 2 3 4] +(conj [1 2 3] 4 5) +;; => [1 2 3 4 5] +``` + +Adding to associative tables: + +``` fennel +(conj {:a 1} [:b 2] [:c 3]) +;; => {:a 1 :b 2 :c 3} +``` + +Note, that passing literal empty associative table `{}` will not work: + +``` fennel +(conj {} [:a 1] [:b 2]) +;; => [[:a 1] [:b 2]] +(conj (hash-map) [:a 1] [:b 2]) +;; => {:a 1 :b 2} +``` + +See [`hash-map`](#hash-map) for creating empty associative tables." ([] (empty [])) ([tbl] tbl) ([tbl x] @@ -255,7 +406,22 @@ result of calling f with no arguments. If coll has only 1 item, it is returned and f is not called. If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc. If coll contains no items, returns -val and f is not called. Calls `seq` on `col`." +val and f is not called. Calls `seq` on `col`. + +Early termination is possible with the use of [`reduced`](#reduced) +function. + +# Examples +Reduce sequence of numbers with [`add`](#add) + +``` fennel +(reduce add [1 2 3 4]) +;; => 10 +(reduce add 10 [1 2 3 4]) +;; => 20 +``` + +" ([f col] (let [col (or (seq col) (empty []))] (match (length col) @@ -278,7 +444,29 @@ val and f is not called. Calls `seq` on `col`." (fn* core.reduced "Wraps `x` in such a way so [`reduce`](#reduce) will terminate early - with this value." +with this value. + +# Examples +Stop reduction is result is higher than `10`: + +``` fennel +(reduce (fn [res x] + (if (>= res 10) + (reduced res) + (+ res x))) + [1 2 3]) +;; => 6 + +(reduce (fn [res x] + (if (>= res 10) + (reduced res) + (+ res x))) + [1 2 3 4 :nil]) +;; => 10 +``` + +Note that in second example we had `:nil` in the array, which is not a +valid number, but we've terminated right before we've reached it." [x] (setmetatable {} {:cljlib/reduced {:status :ready @@ -292,7 +480,36 @@ applying `f` to `val`, the first key and the first value in `tbl`, then applying `f` to that result and the 2nd key and value, etc. If `tbl` contains no entries, returns `val` and `f` is not called. Note that reduce-kv is supported on sequential tables and strings, where -the keys will be the ordinals." +the keys will be the ordinals. + +Early termination is possible with the use of [`reduced`](#reduced) +function. + +# Examples +Reduce associative table by adding values from all keys: + +``` fennel +(local t {:a1 1 + :b1 2 + :a2 2 + :b2 3}) + +(reduce-kv #(+ $1 $3) 0 t) +;; => 8 +``` + +Reduce table by adding values from keys that start with letter `a`: + +``` fennel +(local t {:a1 1 + :b1 2 + :a2 2 + :b2 3}) + +(reduce-kv (fn [res k v] (if (= (string.sub k 1 1) :a) (+ res v) res)) + 0 t) +;; => 3 +```" [f val tbl] (var res val) (each [_ [k v] (pairs (or (seq tbl) (empty [])))] @@ -308,12 +525,37 @@ the keys will be the ordinals." (fn* core.mapv "Maps function `f` over one or more collections. -Accepts arbitrary amount of tables, calls `seq` on each of it. -Function `f` must take the same amount of parameters as the amount of -tables passed to `mapv`. Applies `f` over first value of each +Accepts arbitrary amount of collections, calls `seq` on each of it. +Function `f` must take the same amount of arguments as the amount of +tables, passed to `mapv`. Applies `f` over first value of each table. Then applies `f` to second value of each table. Continues until any of the tables is exhausted. All remaining values are -ignored. Returns a table of results." +ignored. Returns a sequential table of results. + +# Examples +Map `string.upcase` over the string: + +``` fennel +(mapv string.upper \"string\") +;; => [\"S\" \"T\" \"R\" \"I\" \"N\" \"G\"] +``` + +Map [`mul`](#mul) over two tables: + +``` fennel +(mapv mul [1 2 3 4] [1 0 -1]) +;; => [1 0 -3] +``` + +Basic `zipmap` implementation: + +``` fennel +(fn zipmap [keys vals] + (into {} (mapv vector keys vals))) + +(zipmap [:a :b :c] [1 2 3 4]) +;; => {:a 1 :b 2 :c 3} +```" ([f col] (local res (empty [])) (each [_ v (ipairs (or (seq col) (empty [])))] diff --git a/doc/0.1.0/cljlib-macros.md b/doc/0.1.0/cljlib-macros.md deleted file mode 100644 index d99dad6..0000000 --- a/doc/0.1.0/cljlib-macros.md +++ /dev/null @@ -1,288 +0,0 @@ -# Cljlib-macros.fnl -Macro module for Fennel Cljlib. - -## Metadata macros -Metadata in Fennel is a pretty tough subject, as there's no such thing as metadata in Lua. -Therefore, the metadata usage in Fennel is more limited compared to Clojure. -This library provides some facilities for metadata management, which are experimental and should be used with care. - -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`. - - -### `when-meta` -This macros is a 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`. - - -### `with-meta` -Attach metadata to a value. - - >> (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 - -When metadata feature is not enabled, returns the value without additional metadata. - - -### `meta` -Get metadata table from object: - - >> (meta (with-meta {} {:meta "data"})) - { - :meta "data" - } - - -## `def` and `defonce` -`def` is wrappers around `local` which can declare variables inside namespace, and as local at the same time: - - >> (def ns {}) - >> (def a 10) - >> a - 10 - >> (def ns.a 20) - >> a - 20 - >> ns.a - 20 - -Both `ns.a` and `a` refer to the same value. - -`defonce` ensures that the binding isn't overridden by another `defonce`: - - >> (defonce ns {}) - >> (defonce ns.a 42) - >> (defonce ns 10) - >> ns - {:a 42} - >> a - 42 - -Both `def` and `defonce` support literal metadata table as first argument, or a :dynamic keyword, that uses Fennel `var` instead of `local`: - - >> (def {:dynamic true} a 10) - >> (set a 20) - >> a - 20 - >> (defonce :dynamic b 40) - >> (set b 42) - >> b - 42 - -Documentation string can be attached to value via `:doc` keyword. -However it is not recommended to attach metadata to everything except tables and functions: - - ;; Bad, may overlap with existing documentation for 299792458, if any - >> (def {:doc "The speed of light in m/s"} c 299792458) - >> (doc c) - c - The speed of light in m/s - - ;; OK - >> (def {:doc "default connection options"} - defaults {:port 1234 - :host localhost}) - - -## `fn*` -Clojure's `fn` equivalent. -Returns a function of fixed amount of arguments by doing runtime dispatch based on argument count. -Capable of producing multi-arity functions: - - (fn* square "square number" [x] (^ x 2)) - - (square 9) ;; => 81.0 - (square 1 2) ;; => error - - (fn* range - "Returns increasing sequence of numbers from `lower' to `upper'. - If `lower' is not provided, sequence starts from zero. - Accepts optional `step'" - ([upper] (range 0 upper 1)) - ([lower upper] (range lower upper 1)) - ([lower upper step] - (let [res []] - (for [i lower (- upper step) step] - (table.insert res i)) - res))) - - (range 10) ;; => [0 1 2 3 4 5 6 7 8 9] - (range -10 0) ;; => [-10 -9 -8 -7 -6 -5 -4 -3 -2 -1] - (range 0 1 0.2) ;; => [0.0 0.2 0.4 0.6 0.8] - -Both variants support up to one arity with `& more`: - - (fn* vec [& xs] xs) - - (vec 1 2 3) ;; => [1 2 3] - - (fn* add - "sum two or more values" - ([] 0) - ([a] a) - ([a b] (+ a b)) - ([a b & more] (add (+ a b) (unpack more)))) - - (add) ;; => 0 - (add 1) ;; => 1 - (add 1 2) ;; => 3 - (add 1 2 3 4) ;; => 10 - -One extra capability of `fn*` supports the same semantic as `def` regarding namespaces: - - (local ns {}) - - (fn* ns.plus - ([] 0) - ([x] x) - ([x y] (+ x y)) - ([x y & zs] (apply plus (+ x y) zs))) - - ns - -Note, that `plus` is used without `ns` part, e.g. not `ns.plus`. -If we `require` this code from file in the REPL, we will see that our `ns` has single function `plus`: - - >> (local ns (require :module)) - >> ns - {add #} - -This is possible because `fn*` separates the namespace part from the function name, and creates a `local` variable with the same name as function, then defines the function within lexical scope of `do`, sets `namespace.foo` to it and returns the function object to the outer scope. - - (local plus - (do (fn plus [...] - ;; plus body - ) - (set ns.plus plus) - plus)) - -See `core.fnl` for more examples. - - -## `fn+` -Works similarly to Fennel's `fn`, by creating ordinary function without arity semantics, except does the namespace automation like `fn*`, and has the same order of arguments as the latter: - - (local ns {}) - - ;; module & file-local functions - (fn+ ns.double - "double the number" - [x] - (* x 2)) - - (fn+ ns.triple - [x] - (* x 3)) - - ;; no namespace, file-local function - (fn+ quadruple - [x] - (* x 4)) - - ;; anonymous file-local function - (fn+ [x] (* x 5)) - - ns - -See `core.fnl` for more examples. - - -## `if-let` and `when-let` -When test expression is not `nil` or `false`, evaluates the first body form with the `name` bound to the result of the expressions. - - (if-let [val (test)] - (print val) - :fail) - -Expanded form: - - (let [tmp (test)] - (if tmp - (let [val tmp] - (print val)) - :fail)) - -`when-let` is mostly the same, except doesn't have false branch and accepts any amount of forms: - - (when-let [val (test)] - (print val) - val) - -Expanded form: - - (let [tmp (test)] - (if tmp - (let [val tmp] - (print val) - val))) - - -## `if-some` and `when-some` -Much like `if-let` and `when-let`, except tests expression for not being `nil`. - - (when-some [val (foo)] - (print (.. "val is not nil: " val)) - val) - - -## `into` -Clojure's `into` function is implemented as macro, because Fennel has no runtime distinction between `[]` and `{}` tables, since Lua also doesn't feature this feature. -However we can do this at compile time. - - (into [1 2 3] [4 5 6]) ;; => [1 2 3 4 5 6] - (into [] {:a 1 :b 2 :c 3 :d 4}) ;; => [["d" 4] ["a" 1] ["b" 2] ["c" 3]] - (into {} [[:d 4] [:a 1] [:b 2] [:c 3]]) ;; => {:a 1 :b 2 :c 3 :d 4} - (into {:a 0 :e 5} {:a 1 :b 2 :c 3 :d 4}) ;; => {:a 1 :b 2 :c 3 :d 4 :e 5} - -Because the type check at compile time it will only respect the type when literal representation is used. -If a variable holding the table, its type is checked at runtime. -Empty tables default to sequential ones: - - (local a []) - (into a {:a 1 :b 2}) ;; => [["b" 2] ["a" 1]] - - (local b {}) - (into b {:a 1 :b 2}) ;; => [["b" 2] ["a" 1]] - -However, if target table is not empty, its type can be deduced: - - (local a {:c 3}) - (into a {:a 1 :b 2}) ;; => {:a 1 :b 2 :c 3} - - (local b [1]) - (into b {:a 1 :b 2}) ;; => [1 ["b" 2] ["a" 1]] - -Note that when converting associative table into sequential table order is determined by the `pairs` function. -Also note that if variable stores the table has both integer key 1, and other associative keys, the type will be the same as of sequential table. - - -## `defmulti` and `defmethod` -A bit more simple implementations of Clojure's `defmulti` and `defmethod`. -`defmulti` macros returns an empty table with `__call` metamethod, that calls dispatching function on its arguments. -Methods are defined inside `multimethods` table, which is also stored in the metatable. - -`defmethod` adds a new method to the metatable of given `multifn`. -It accepts the multi-fn table as its first argument, the dispatch value as second, and Fennel's arglist followed by the body: - - (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. diff --git a/doc/0.1.0/cljlib.md b/doc/0.1.0/cljlib.md deleted file mode 100644 index 1226591..0000000 --- a/doc/0.1.0/cljlib.md +++ /dev/null @@ -1,674 +0,0 @@ -# Cljlib.fnl -Fennel-cljlib - functions from Clojure's core.clj implemented on top of Fennel. - -Documentation for version: 0.1.0 - -## `add` -Function signature: - -``` -(add - ([a]) - ([a b]) - ([a b c]) - ([a b c d]) - ([a b c d & rest])) -``` - -Sum arbitrary amount of numbers. - -## `apply` -Function signature: - -``` -(apply - ([f args]) - ([f a args]) - ([f a b args]) - ([f a b c args]) - ([f a b c d & args])) -``` - -Apply `f` to the argument list formed by prepending intervening -arguments to `args`. - -## `assoc` -Function signature: - -``` -(assoc - ([tbl k v]) - ([tbl k v & kvs])) -``` - -Associate key `k` with value `v` in `tbl`. - -## `boolean?` -Function signature: - -``` -(boolean? [x]) -``` - -Test if `x` is a Boolean - -## `butlast` -Function signature: - -``` -(butlast [col]) -``` - -Returns everything but the last element of a table as a new - table. Calls `seq` on its argument. - -## `comp` -Function signature: - -``` -(comp - ([f]) - ([f g]) - ([f g & fs])) -``` - -Compose functions. - -## `complement` -Function signature: - -``` -(complement [f]) -``` - -Takes a function `f` and returns the function that takes the same -amount of arguments as `f`, has the same effect, and returns the -oppisite truth value. - -## `concat` -Function signature: - -``` -(concat - ([x]) - ([x y]) - ([x y & xs])) -``` - -Concatenate tables. - -## `conj` -Function signature: - -``` -(conj - ([tbl]) - ([tbl x]) - ([tbl x & xs])) -``` - -Insert `x` as a last element of indexed table `tbl`. Modifies `tbl` - -## `cons` -Function signature: - -``` -(cons [x tbl]) -``` - -Insert `x` to `tbl` at the front. Modifies `tbl`. - -## `constantly` -Function signature: - -``` -(constantly [x]) -``` - -Returns a function that takes any number of arguments and returns `x`. - -## `dec` -Function signature: - -``` -(dec [x]) -``` - -Decrease number by one - -## `div` -Function signature: - -``` -(div - ([a]) - ([a b]) - ([a b c]) - ([a b c d]) - ([a b c d & rest])) -``` - -Divide arbitrary amount of numbers. - -## `double?` -Function signature: - -``` -(double? [x]) -``` - -Test if `x` is a number with floating point data. - -## `empty?` -Function signature: - -``` -(empty? [x]) -``` - -Check if collection is empty. - -## `eq` -Function signature: - -``` -(eq - ([x]) - ([x y]) - ([x y & xs])) -``` - -Deep compare values. - -## `even?` -Function signature: - -``` -(even? [x]) -``` - -Test if value is even. - -## `every?` -Function signature: - -``` -(every? [pred tbl]) -``` - -Test if every item in `tbl` satisfies the `pred`. - -## `false?` -Function signature: - -``` -(false? [x]) -``` - -Test if `x` is `false` - -## `filter` -Function signature: - -``` -(filter [pred col]) -``` - -Returns a sequential table of the items in `col` for which `pred` - returns logical true. - -## `first` -Function signature: - -``` -(first [col]) -``` - -Return first element of a table. Calls `seq` on its argument. - -## `ge` -Function signature: - -``` -(ge - ([x]) - ([x y]) - ([x y & more])) -``` - -Returns true if nums are in monotonically non-increasing order - -## `get` -Function signature: - -``` -(get - ([tbl key]) - ([tbl key not-found])) -``` - -Get value from the table by accessing it with a `key`. -Accepts additional `not-found` as a marker to return if value wasn't -found in the table. - -## `get-in` -Function signature: - -``` -(get-in - ([tbl keys]) - ([tbl keys not-found])) -``` - -Get value from nested set of tables by providing key sequence. -Accepts additional `not-found` as a marker to return if value wasn't -found in the table. - -## `get-method` -Function signature: - -``` -(get-method [multifn dispatch-val]) -``` - -Given a multimethod and a dispatch value, returns the dispatch `fn` -that would apply to that value, or `nil` if none apply and no default. - -## `gt` -Function signature: - -``` -(gt - ([x]) - ([x y]) - ([x y & more])) -``` - -Returns true if nums are in monotonically increasing order - -## `hash-map` -Function signature: - -``` -(hash-map - ([& kvs])) -``` - -Create associative table from keys and values - -## `identity` -Function signature: - -``` -(identity [x]) -``` - -Returns its argument. - -## `inc` -Function signature: - -``` -(inc [x]) -``` - -Increase number by one - -## `int?` -Function signature: - -``` -(int? [x]) -``` - -Test if `x` is a number without floating point data. - -## `kvseq` -Function signature: - -``` -(kvseq [tbl]) -``` - -Transforms any table kind to key-value sequence. - -## `last` -Function signature: - -``` -(last [col]) -``` - -Returns the last element of a table. Calls `seq` on its argument. - -## `le` -Function signature: - -``` -(le - ([x]) - ([x y]) - ([x y & more])) -``` - -Returns true if nums are in monotonically non-decreasing order - -## `lt` -Function signature: - -``` -(lt - ([x]) - ([x y]) - ([x y & more])) -``` - -Returns true if nums are in monotonically decreasing order - -## `map?` -Function signature: - -``` -(map? [tbl]) -``` - -Check whether `tbl` is an associative table. - -## `mapv` -Function signature: - -``` -(mapv - ([f col]) - ([f col1 col2]) - ([f col1 col2 col3]) - ([f col1 col2 col3 & cols])) -``` - -Maps function `f` over one or more collections. - -Accepts arbitrary amount of tables, calls `seq` on each of it. -Function `f` must take the same amount of parameters as the amount of -tables passed to `mapv`. Applies `f` over first value of each -table. Then applies `f` to second value of each table. Continues until -any of the tables is exhausted. All remaining values are -ignored. Returns a table of results. - -## `memoize` -Function signature: - -``` -(memoize [f]) -``` - -Returns a memoized version of a referentially transparent function. -The memoized version of the function keeps a cache of the mapping from -arguments to results and, when calls with the same arguments are -repeated often, has higher performance at the expense of higher memory -use. - -## `methods` -Function signature: - -``` -(methods [multifn]) -``` - -Given a multimethod, returns a map of dispatch values -> dispatch fns - -## `mul` -Function signature: - -``` -(mul - ([a]) - ([a b]) - ([a b c]) - ([a b c d]) - ([a b c d & rest])) -``` - -Multiply arbitrary amount of numbers. - -## `neg-int?` -Function signature: - -``` -(neg-int? [x]) -``` - -Test if `x` is a negetive integer. - -## `neg?` -Function signature: - -``` -(neg? [x]) -``` - -Test if `x` is less than zero. - -## `nil?` -Function signature: - -``` -(nil? - ([x])) -``` - -Test if value is nil. - -## `not-any?` -Function signature: - -``` -(not-any? pred tbl) -``` - -Test if no item in `tbl` satisfy the `pred`. - -## `not-empty` -Function signature: - -``` -(not-empty [x]) -``` - -If `x` is empty, returns `nil`, otherwise `x`. - -## `odd?` -Function signature: - -``` -(odd? [x]) -``` - -Test if value is odd. - -## `pos-int?` -Function signature: - -``` -(pos-int? [x]) -``` - -Test if `x` is a positive integer. - -## `pos?` -Function signature: - -``` -(pos? [x]) -``` - -Test if `x` is greater than zero. - -## `range` -Function signature: - -``` -(range - ([upper]) - ([lower upper]) - ([lower upper step])) -``` - -return range of of numbers from `lower` to `upper` with optional `step`. - -## `reduce` -Function signature: - -``` -(reduce - ([f col]) - ([f val col])) -``` - -Reduce collection `col` using function `f` and optional initial value `val`. - -`f` should be a function of 2 arguments. If val is not supplied, -returns the result of applying f to the first 2 items in coll, then -applying f to that result and the 3rd item, etc. If coll contains no -items, f must accept no arguments as well, and reduce returns the -result of calling f with no arguments. If coll has only 1 item, it is -returned and f is not called. If val is supplied, returns the result -of applying f to val and the first item in coll, then applying f to -that result and the 2nd item, etc. If coll contains no items, returns -val and f is not called. Calls `seq` on `col`. - -## `reduce-kv` -Function signature: - -``` -(reduce-kv [f val col]) -``` - -Reduces an associative table using function `f` and initial value `val`. - -`f` should be a function of 3 arguments. Returns the result of -applying `f` to `val`, the first key and the first value in `coll`, -then applying `f` to that result and the 2nd key and value, etc. If -`coll` contains no entries, returns `val` and `f` is not called. Note -that reduce-kv is supported on sequential tables and strings, where -the keys will be the ordinals. - -## `remove-all-methods` -Function signature: - -``` -(remove-all-methods [multifn]) -``` - -Removes all of the methods of multimethod - -## `remove-method` -Function signature: - -``` -(remove-method [multifn dispatch-val]) -``` - -Remove method from `multifn` for given `dispatch-val`. - -## `rest` -Function signature: - -``` -(rest [seq]) -``` - -Returns table of all elements of a table but the first one. Calls - `seq` on its argument. - -## `reverse` -Function signature: - -``` -(reverse [tbl]) -``` - -Returns table with same items as in `tbl` but in reverse order. - -## `seq` -Function signature: - -``` -(seq [col]) -``` - -Create sequential table. -Transforms original table to sequential table of key value pairs -stored as sequential tables in linear time. If `col` is an -associative table, returns `[[key1 value1] ... [keyN valueN]]` table. -If `col` is sequential table, returns its shallow copy. - -## `seq?` -Function signature: - -``` -(seq? [tbl]) -``` - -Check whether `tbl` is an sequential table. - -## `some` -Function signature: - -``` -(some [pred tbl]) -``` - -Test if any item in `tbl` satisfies the `pred`. - -## `string?` -Function signature: - -``` -(string? [x]) -``` - -Test if `x` is a string. - -## `sub` -Function signature: - -``` -(sub - ([a]) - ([a b]) - ([a b c]) - ([a b c d]) - ([a b c d & rest])) -``` - -Subtract arbitrary amount of numbers. - -## `true?` -Function signature: - -``` -(true? [x]) -``` - -Test if `x` is `true` - -## `vector` -Function signature: - -``` -(vector [& args]) -``` - -Constructs sequential table out of it's arguments. - -## `zero?` -Function signature: - -``` -(zero? [x]) -``` - -Test if value is zero. - - - \ No newline at end of file diff --git a/doc/cljlib-macros.md b/doc/cljlib-macros.md new file mode 100644 index 0000000..d99dad6 --- /dev/null +++ b/doc/cljlib-macros.md @@ -0,0 +1,288 @@ +# Cljlib-macros.fnl +Macro module for Fennel Cljlib. + +## Metadata macros +Metadata in Fennel is a pretty tough subject, as there's no such thing as metadata in Lua. +Therefore, the metadata usage in Fennel is more limited compared to Clojure. +This library provides some facilities for metadata management, which are experimental and should be used with care. + +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`. + + +### `when-meta` +This macros is a 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`. + + +### `with-meta` +Attach metadata to a value. + + >> (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 + +When metadata feature is not enabled, returns the value without additional metadata. + + +### `meta` +Get metadata table from object: + + >> (meta (with-meta {} {:meta "data"})) + { + :meta "data" + } + + +## `def` and `defonce` +`def` is wrappers around `local` which can declare variables inside namespace, and as local at the same time: + + >> (def ns {}) + >> (def a 10) + >> a + 10 + >> (def ns.a 20) + >> a + 20 + >> ns.a + 20 + +Both `ns.a` and `a` refer to the same value. + +`defonce` ensures that the binding isn't overridden by another `defonce`: + + >> (defonce ns {}) + >> (defonce ns.a 42) + >> (defonce ns 10) + >> ns + {:a 42} + >> a + 42 + +Both `def` and `defonce` support literal metadata table as first argument, or a :dynamic keyword, that uses Fennel `var` instead of `local`: + + >> (def {:dynamic true} a 10) + >> (set a 20) + >> a + 20 + >> (defonce :dynamic b 40) + >> (set b 42) + >> b + 42 + +Documentation string can be attached to value via `:doc` keyword. +However it is not recommended to attach metadata to everything except tables and functions: + + ;; Bad, may overlap with existing documentation for 299792458, if any + >> (def {:doc "The speed of light in m/s"} c 299792458) + >> (doc c) + c + The speed of light in m/s + + ;; OK + >> (def {:doc "default connection options"} + defaults {:port 1234 + :host localhost}) + + +## `fn*` +Clojure's `fn` equivalent. +Returns a function of fixed amount of arguments by doing runtime dispatch based on argument count. +Capable of producing multi-arity functions: + + (fn* square "square number" [x] (^ x 2)) + + (square 9) ;; => 81.0 + (square 1 2) ;; => error + + (fn* range + "Returns increasing sequence of numbers from `lower' to `upper'. + If `lower' is not provided, sequence starts from zero. + Accepts optional `step'" + ([upper] (range 0 upper 1)) + ([lower upper] (range lower upper 1)) + ([lower upper step] + (let [res []] + (for [i lower (- upper step) step] + (table.insert res i)) + res))) + + (range 10) ;; => [0 1 2 3 4 5 6 7 8 9] + (range -10 0) ;; => [-10 -9 -8 -7 -6 -5 -4 -3 -2 -1] + (range 0 1 0.2) ;; => [0.0 0.2 0.4 0.6 0.8] + +Both variants support up to one arity with `& more`: + + (fn* vec [& xs] xs) + + (vec 1 2 3) ;; => [1 2 3] + + (fn* add + "sum two or more values" + ([] 0) + ([a] a) + ([a b] (+ a b)) + ([a b & more] (add (+ a b) (unpack more)))) + + (add) ;; => 0 + (add 1) ;; => 1 + (add 1 2) ;; => 3 + (add 1 2 3 4) ;; => 10 + +One extra capability of `fn*` supports the same semantic as `def` regarding namespaces: + + (local ns {}) + + (fn* ns.plus + ([] 0) + ([x] x) + ([x y] (+ x y)) + ([x y & zs] (apply plus (+ x y) zs))) + + ns + +Note, that `plus` is used without `ns` part, e.g. not `ns.plus`. +If we `require` this code from file in the REPL, we will see that our `ns` has single function `plus`: + + >> (local ns (require :module)) + >> ns + {add #} + +This is possible because `fn*` separates the namespace part from the function name, and creates a `local` variable with the same name as function, then defines the function within lexical scope of `do`, sets `namespace.foo` to it and returns the function object to the outer scope. + + (local plus + (do (fn plus [...] + ;; plus body + ) + (set ns.plus plus) + plus)) + +See `core.fnl` for more examples. + + +## `fn+` +Works similarly to Fennel's `fn`, by creating ordinary function without arity semantics, except does the namespace automation like `fn*`, and has the same order of arguments as the latter: + + (local ns {}) + + ;; module & file-local functions + (fn+ ns.double + "double the number" + [x] + (* x 2)) + + (fn+ ns.triple + [x] + (* x 3)) + + ;; no namespace, file-local function + (fn+ quadruple + [x] + (* x 4)) + + ;; anonymous file-local function + (fn+ [x] (* x 5)) + + ns + +See `core.fnl` for more examples. + + +## `if-let` and `when-let` +When test expression is not `nil` or `false`, evaluates the first body form with the `name` bound to the result of the expressions. + + (if-let [val (test)] + (print val) + :fail) + +Expanded form: + + (let [tmp (test)] + (if tmp + (let [val tmp] + (print val)) + :fail)) + +`when-let` is mostly the same, except doesn't have false branch and accepts any amount of forms: + + (when-let [val (test)] + (print val) + val) + +Expanded form: + + (let [tmp (test)] + (if tmp + (let [val tmp] + (print val) + val))) + + +## `if-some` and `when-some` +Much like `if-let` and `when-let`, except tests expression for not being `nil`. + + (when-some [val (foo)] + (print (.. "val is not nil: " val)) + val) + + +## `into` +Clojure's `into` function is implemented as macro, because Fennel has no runtime distinction between `[]` and `{}` tables, since Lua also doesn't feature this feature. +However we can do this at compile time. + + (into [1 2 3] [4 5 6]) ;; => [1 2 3 4 5 6] + (into [] {:a 1 :b 2 :c 3 :d 4}) ;; => [["d" 4] ["a" 1] ["b" 2] ["c" 3]] + (into {} [[:d 4] [:a 1] [:b 2] [:c 3]]) ;; => {:a 1 :b 2 :c 3 :d 4} + (into {:a 0 :e 5} {:a 1 :b 2 :c 3 :d 4}) ;; => {:a 1 :b 2 :c 3 :d 4 :e 5} + +Because the type check at compile time it will only respect the type when literal representation is used. +If a variable holding the table, its type is checked at runtime. +Empty tables default to sequential ones: + + (local a []) + (into a {:a 1 :b 2}) ;; => [["b" 2] ["a" 1]] + + (local b {}) + (into b {:a 1 :b 2}) ;; => [["b" 2] ["a" 1]] + +However, if target table is not empty, its type can be deduced: + + (local a {:c 3}) + (into a {:a 1 :b 2}) ;; => {:a 1 :b 2 :c 3} + + (local b [1]) + (into b {:a 1 :b 2}) ;; => [1 ["b" 2] ["a" 1]] + +Note that when converting associative table into sequential table order is determined by the `pairs` function. +Also note that if variable stores the table has both integer key 1, and other associative keys, the type will be the same as of sequential table. + + +## `defmulti` and `defmethod` +A bit more simple implementations of Clojure's `defmulti` and `defmethod`. +`defmulti` macros returns an empty table with `__call` metamethod, that calls dispatching function on its arguments. +Methods are defined inside `multimethods` table, which is also stored in the metatable. + +`defmethod` adds a new method to the metatable of given `multifn`. +It accepts the multi-fn table as its first argument, the dispatch value as second, and Fennel's arglist followed by the body: + + (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. diff --git a/doc/cljlib.md b/doc/cljlib.md new file mode 100644 index 0000000..53793f2 --- /dev/null +++ b/doc/cljlib.md @@ -0,0 +1,991 @@ +# Cljlib.fnl(0.1.0) +Fennel-cljlib - functions from Clojure's core.clj implemented on top of Fennel. + +**Table of contents** +- [`add`](#add) +- [`apply`](#apply) +- [`assoc`](#assoc) +- [`boolean?`](#boolean?) +- [`butlast`](#butlast) +- [`comp`](#comp) +- [`complement`](#complement) +- [`concat`](#concat) +- [`conj`](#conj) +- [`cons`](#cons) +- [`constantly`](#constantly) +- [`dec`](#dec) +- [`div`](#div) +- [`double?`](#double?) +- [`empty?`](#empty?) +- [`eq`](#eq) +- [`even?`](#even?) +- [`every?`](#every?) +- [`false?`](#false?) +- [`filter`](#filter) +- [`first`](#first) +- [`ge`](#ge) +- [`get`](#get) +- [`get-in`](#get-in) +- [`get-method`](#get-method) +- [`gt`](#gt) +- [`hash-map`](#hash-map) +- [`identity`](#identity) +- [`inc`](#inc) +- [`int?`](#int?) +- [`kvseq`](#kvseq) +- [`last`](#last) +- [`le`](#le) +- [`lt`](#lt) +- [`map?`](#map?) +- [`mapv`](#mapv) +- [`memoize`](#memoize) +- [`methods`](#methods) +- [`mul`](#mul) +- [`neg-int?`](#neg-int?) +- [`neg?`](#neg?) +- [`nil?`](#nil?) +- [`not-any?`](#not-any?) +- [`not-empty`](#not-empty) +- [`odd?`](#odd?) +- [`pos-int?`](#pos-int?) +- [`pos?`](#pos?) +- [`range`](#range) +- [`reduce`](#reduce) +- [`reduce-kv`](#reduce-kv) +- [`reduced`](#reduced) +- [`remove-all-methods`](#remove-all-methods) +- [`remove-method`](#remove-method) +- [`rest`](#rest) +- [`reverse`](#reverse) +- [`seq`](#seq) +- [`some`](#some) +- [`string?`](#string?) +- [`sub`](#sub) +- [`true?`](#true?) +- [`vector`](#vector) +- [`vector?`](#vector?) +- [`zero?`](#zero?) + +## `add` +Function signature: + +``` +(add + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) +``` + +Sum arbitrary amount of numbers. + +## `apply` +Function signature: + +``` +(apply + ([f args]) + ([f a args]) + ([f a b args]) + ([f a b c args]) + ([f a b c d & args])) +``` + +Apply `f` to the argument list formed by prepending intervening +arguments to `args`, adn `f` must support variadic amount of +arguments. + +### Examples +Applying `print` to different arguments: + +``` fennel +(apply print [1 2 3 4]) +;; prints 1 2 3 4 +(apply print 1 [2 3 4]) +;; => 1 2 3 4 +(apply print 1 2 3 4 5 6 [7 8 9]) +;; => 1 2 3 4 5 6 7 8 9 +``` + +## `assoc` +Function signature: + +``` +(assoc + ([tbl k v]) + ([tbl k v & kvs])) +``` + +Associate key `k` with value `v` in `tbl`. + +## `boolean?` +Function signature: + +``` +(boolean? [x]) +``` + +Test if `x` is a Boolean + +## `butlast` +Function signature: + +``` +(butlast [col]) +``` + +Returns everything but the last element of a table as a new + table. Calls `seq` on its argument. + +## `comp` +Function signature: + +``` +(comp + ([f]) + ([f g]) + ([f g & fs])) +``` + +Compose functions. + +## `complement` +Function signature: + +``` +(complement [f]) +``` + +Takes a function `f` and returns the function that takes the same +amount of arguments as `f`, has the same effect, and returns the +oppisite truth value. + +## `concat` +Function signature: + +``` +(concat + ([x]) + ([x y]) + ([x y & xs])) +``` + +Concatenate tables. + +## `conj` +Function signature: + +``` +(conj + ([tbl]) + ([tbl x]) + ([tbl x & xs])) +``` + +Insert `x` as a last element of a table `tbl`. + +If `tbl` is a sequential table or empty table, inserts `x` and +optional `xs` as final element in the table. + +If `tbl` is an associative table, that satisfies [`map?`](#map?) test, +insert `[key value]` pair into the table. + +Mutates `tbl`. + +### Examples +Adding to sequential tables: + +``` fennel +(conj [] 1 2 3 4) +;; => [1 2 3 4] +(conj [1 2 3] 4 5) +;; => [1 2 3 4 5] +``` + +Adding to associative tables: + +``` fennel +(conj {:a 1} [:b 2] [:c 3]) +;; => {:a 1 :b 2 :c 3} +``` + +Note, that passing literal empty associative table `{}` will not work: + +``` fennel +(conj {} [:a 1] [:b 2]) +;; => [[:a 1] [:b 2]] +(conj (hash-map) [:a 1] [:b 2]) +;; => {:a 1 :b 2} +``` + +See [`hash-map`](#hash-map) for creating empty associative tables. + +## `cons` +Function signature: + +``` +(cons [x tbl]) +``` + +Insert `x` to `tbl` at the front. Modifies `tbl`. + +## `constantly` +Function signature: + +``` +(constantly [x]) +``` + +Returns a function that takes any number of arguments and returns `x`. + +## `dec` +Function signature: + +``` +(dec [x]) +``` + +Decrease number by one + +## `div` +Function signature: + +``` +(div + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) +``` + +Divide arbitrary amount of numbers. + +## `double?` +Function signature: + +``` +(double? [x]) +``` + +Test if `x` is a number with floating point data. + +## `empty?` +Function signature: + +``` +(empty? [x]) +``` + +Check if collection is empty. + +## `eq` +Function signature: + +``` +(eq + ([x]) + ([x y]) + ([x y & xs])) +``` + +Deep compare values. + +## `even?` +Function signature: + +``` +(even? [x]) +``` + +Test if value is even. + +## `every?` +Function signature: + +``` +(every? [pred tbl]) +``` + +Test if every item in `tbl` satisfies the `pred`. + +## `false?` +Function signature: + +``` +(false? [x]) +``` + +Test if `x` is `false` + +## `filter` +Function signature: + +``` +(filter [pred col]) +``` + +Returns a sequential table of the items in `col` for which `pred` + returns logical true. + +## `first` +Function signature: + +``` +(first [col]) +``` + +Return first element of a table. Calls `seq` on its argument. + +## `ge` +Function signature: + +``` +(ge + ([x]) + ([x y]) + ([x y & more])) +``` + +Returns true if nums are in monotonically non-increasing order + +## `get` +Function signature: + +``` +(get + ([tbl key]) + ([tbl key not-found])) +``` + +Get value from the table by accessing it with a `key`. +Accepts additional `not-found` as a marker to return if value wasn't +found in the table. + +## `get-in` +Function signature: + +``` +(get-in + ([tbl keys]) + ([tbl keys not-found])) +``` + +Get value from nested set of tables by providing key sequence. +Accepts additional `not-found` as a marker to return if value wasn't +found in the table. + +## `get-method` +Function signature: + +``` +(get-method [multifn dispatch-val]) +``` + +Given a multimethod and a dispatch value, returns the dispatch `fn` +that would apply to that value, or `nil` if none apply and no default. + +## `gt` +Function signature: + +``` +(gt + ([x]) + ([x y]) + ([x y & more])) +``` + +Returns true if nums are in monotonically increasing order + +## `hash-map` +Function signature: + +``` +(hash-map + ([& kvs])) +``` + +Create associative table from keys and values + +## `identity` +Function signature: + +``` +(identity [x]) +``` + +Returns its argument. + +## `inc` +Function signature: + +``` +(inc [x]) +``` + +Increase number by one + +## `int?` +Function signature: + +``` +(int? [x]) +``` + +Test if `x` is a number without floating point data. + +Number is rounded with `math.floor` and compared with original number. + +## `kvseq` +Function signature: + +``` +(kvseq [tbl]) +``` + +Transforms any table kind to key-value sequence. + +## `last` +Function signature: + +``` +(last [col]) +``` + +Returns the last element of a table. Calls `seq` on its argument. + +## `le` +Function signature: + +``` +(le + ([x]) + ([x y]) + ([x y & more])) +``` + +Returns true if nums are in monotonically non-decreasing order + +## `lt` +Function signature: + +``` +(lt + ([x]) + ([x y]) + ([x y & more])) +``` + +Returns true if nums are in monotonically decreasing order + +## `map?` +Function signature: + +``` +(map? [tbl]) +``` + +Check whether `tbl` is an associative table. + +Non empty associative tables are tested for two things: +- `next` returns the key-value pair, +- key, that is returned by the `next` is not equal to `1`. + +Empty tables can't be analyzed with this method, and `map?` will +return `false`. If you need this test pass for empty table, see +[`hash-map`](#hash-map) for creating tables that have additional +metadata attached for this test to work. + +### Examples +Non empty tables: + +``` fennel +(assert (map? {:a 1 :b 2})) + +(local some-table {:key :value}) +(assert (map? some-table)) +``` + +Empty tables: + +``` fennel +(local some-table {}) +(assert (not (map? some-table))) +``` + +Empty tables created with [`hash-map`](#hash-map) will pass the test: + +``` fennel +(local some-table (hash-map)) +(assert (map? some-table)) +``` + +## `mapv` +Function signature: + +``` +(mapv + ([f col]) + ([f col1 col2]) + ([f col1 col2 col3]) + ([f col1 col2 col3 & cols])) +``` + +Maps function `f` over one or more collections. + +Accepts arbitrary amount of collections, calls `seq` on each of it. +Function `f` must take the same amount of arguments as the amount of +tables, passed to `mapv`. Applies `f` over first value of each +table. Then applies `f` to second value of each table. Continues until +any of the tables is exhausted. All remaining values are +ignored. Returns a sequential table of results. + +### Examples +Map `string.upcase` over the string: + +``` fennel +(mapv string.upper "string") +;; => ["S" "T" "R" "I" "N" "G"] +``` + +Map [`mul`](#mul) over two tables: + +``` fennel +(mapv mul [1 2 3 4] [1 0 -1]) +;; => [1 0 -3] +``` + +Basic `zipmap` implementation: + +``` fennel +(fn zipmap [keys vals] + (into {} (mapv vector keys vals))) + +(zipmap [:a :b :c] [1 2 3 4]) +;; => {:a 1 :b 2 :c 3} +``` + +## `memoize` +Function signature: + +``` +(memoize [f]) +``` + +Returns a memoized version of a referentially transparent function. +The memoized version of the function keeps a cache of the mapping from +arguments to results and, when calls with the same arguments are +repeated often, has higher performance at the expense of higher memory +use. + +## `methods` +Function signature: + +``` +(methods [multifn]) +``` + +Given a multimethod, returns a map of dispatch values -> dispatch fns + +## `mul` +Function signature: + +``` +(mul + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) +``` + +Multiply arbitrary amount of numbers. + +## `neg-int?` +Function signature: + +``` +(neg-int? [x]) +``` + +Test if `x` is a negetive integer. + +## `neg?` +Function signature: + +``` +(neg? [x]) +``` + +Test if `x` is less than zero. + +## `nil?` +Function signature: + +``` +(nil? + ([x])) +``` + +Test if value is nil. + +## `not-any?` +Function signature: + +``` +(not-any? pred tbl) +``` + +Test if no item in `tbl` satisfy the `pred`. + +## `not-empty` +Function signature: + +``` +(not-empty [x]) +``` + +If `x` is empty, returns `nil`, otherwise `x`. + +## `odd?` +Function signature: + +``` +(odd? [x]) +``` + +Test if value is odd. + +## `pos-int?` +Function signature: + +``` +(pos-int? [x]) +``` + +Test if `x` is a positive integer. + +## `pos?` +Function signature: + +``` +(pos? [x]) +``` + +Test if `x` is greater than zero. + +## `range` +Function signature: + +``` +(range + ([upper]) + ([lower upper]) + ([lower upper step])) +``` + +return range of of numbers from `lower` to `upper` with optional `step`. + +## `reduce` +Function signature: + +``` +(reduce + ([f col]) + ([f val col])) +``` + +Reduce collection `col` using function `f` and optional initial value `val`. + +`f` should be a function of 2 arguments. If val is not supplied, +returns the result of applying f to the first 2 items in coll, then +applying f to that result and the 3rd item, etc. If coll contains no +items, f must accept no arguments as well, and reduce returns the +result of calling f with no arguments. If coll has only 1 item, it is +returned and f is not called. If val is supplied, returns the result +of applying f to val and the first item in coll, then applying f to +that result and the 2nd item, etc. If coll contains no items, returns +val and f is not called. Calls `seq` on `col`. + +Early termination is possible with the use of [`reduced`](#reduced) +function. + +### Examples +Reduce sequence of numbers with [`add`](#add) + +``` fennel +(reduce add [1 2 3 4]) +;; => 10 +(reduce add 10 [1 2 3 4]) +;; => 20 +``` + + + +## `reduce-kv` +Function signature: + +``` +(reduce-kv [f val tbl]) +``` + +Reduces an associative table using function `f` and initial value `val`. + +`f` should be a function of 3 arguments. Returns the result of +applying `f` to `val`, the first key and the first value in `tbl`, +then applying `f` to that result and the 2nd key and value, etc. If +`tbl` contains no entries, returns `val` and `f` is not called. Note +that reduce-kv is supported on sequential tables and strings, where +the keys will be the ordinals. + +Early termination is possible with the use of [`reduced`](#reduced) +function. + +### Examples +Reduce associative table by adding values from all keys: + +``` fennel +(local t {:a1 1 + :b1 2 + :a2 2 + :b2 3}) + +(reduce-kv #(+ $1 $3) 0 t) +;; => 8 +``` + +Reduce table by adding values from keys that start with letter `a`: + +``` fennel +(local t {:a1 1 + :b1 2 + :a2 2 + :b2 3}) + +(reduce-kv (fn [res k v] (if (= (string.sub k 1 1) :a) (+ res v) res)) + 0 t) +;; => 3 +``` + +## `reduced` +Function signature: + +``` +(reduced [x]) +``` + +Wraps `x` in such a way so [`reduce`](#reduce) will terminate early +with this value. + +### Examples +Stop reduction is result is higher than `10`: + +``` fennel +(reduce (fn [res x] + (if (>= res 10) + (reduced res) + (+ res x))) + [1 2 3]) +;; => 6 + +(reduce (fn [res x] + (if (>= res 10) + (reduced res) + (+ res x))) + [1 2 3 4 :nil]) +;; => 10 +``` + +Note that in second example we had `:nil` in the array, which is not a +valid number, but we've terminated right before we've reached it. + +## `remove-all-methods` +Function signature: + +``` +(remove-all-methods [multifn]) +``` + +Removes all of the methods of multimethod + +## `remove-method` +Function signature: + +``` +(remove-method [multifn dispatch-val]) +``` + +Remove method from `multifn` for given `dispatch-val`. + +## `rest` +Function signature: + +``` +(rest [col]) +``` + +Returns table of all elements of a table but the first one. Calls + `seq` on its argument. + +## `reverse` +Function signature: + +``` +(reverse [tbl]) +``` + +Returns table with same items as in `tbl` but in reverse order. + +## `seq` +Function signature: + +``` +(seq [col]) +``` + +Create sequential table. + +Transforms original table to sequential table of key value pairs +stored as sequential tables in linear time. If `col` is an +associative table, returns sequential table of vectors with key and +value. If `col` is sequential table, returns its shallow copy. + +### Examples +Sequential tables remain as is: + +``` fennel +(seq [1 2 3 4]) +;; [1 2 3 4] +``` + +Associative tables are transformed to format like this `[[key1 value1] +... [keyN valueN]]` and order is non deterministic: + +``` fennel +(seq {:a 1 :b 2 :c 3}) +;; [[:b 2] [:a 1] [:c 3]] +``` + +See `into` macros for transforming this back to associative table. +Additionally you can use [`conj`](#conj) and [`apply`](#apply) with +[`hash-map`](#hash-map): + +``` fennel +(apply conj (hash-map) [:c 3] [[:a 1] [:b 2]]) +;; => {:a 1 :b 2 :c 3} +``` + +## `some` +Function signature: + +``` +(some [pred tbl]) +``` + +Test if any item in `tbl` satisfies the `pred`. + +## `string?` +Function signature: + +``` +(string? [x]) +``` + +Test if `x` is a string. + +## `sub` +Function signature: + +``` +(sub + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) +``` + +Subtract arbitrary amount of numbers. + +## `true?` +Function signature: + +``` +(true? [x]) +``` + +Test if `x` is `true` + +## `vector` +Function signature: + +``` +(vector [& args]) +``` + +Constructs sequential table out of it's arguments. + +Sets additional metadata for function [`vector?`](#vector?) to work. + +### Examples + +``` fennel +(local v (vector 1 2 3 4)) +(assert (eq v [1 2 3 4])) +``` + +## `vector?` +Function signature: + +``` +(vector? [tbl]) +``` + +Check whether `tbl` is an sequential table. + +Non empty sequential tables are tested for two things: +- `next` returns the key-value pair, +- key, that is returned by the `next` is equal to `1`. + +Empty tables can't be analyzed with this method, and `vector?` will +always return `false`. If you need this test pass for empty table, +see [`vector`](#vector) for creating tables that have additional +metadata attached for this test to work. + +### Examples +Non empty vector: + +``` fennel +(assert (vector? [1 2 3 4])) + +(local some-table [1 2 3]) +(assert (vector? some-table)) +``` + +Empty tables: + +``` fennel +(local some-table []) +(assert (not (vector? some-table))) +``` + +Empty tables created with [`vector`](#vector) will pass the test: + +``` fennel +(local some-table (hash-map)) +(assert (vector? some-table)) +``` + +## `zero?` +Function signature: + +``` +(zero? [x]) +``` + +Test if value is equal to zero. + + + \ No newline at end of file -- cgit v1.2.3