diff options
| -rw-r--r-- | cljlib.fnl | 38 | ||||
| -rw-r--r-- | doc/cljlib.md | 1214 | ||||
| -rw-r--r-- | doc/tests/test.md | 98 | ||||
| -rw-r--r-- | tests/test.fnl | 41 |
4 files changed, 779 insertions, 612 deletions
@@ -154,6 +154,9 @@ Applying `print` to different arguments: (fn* core.inc "Increase number by one" [x] (+ x 1)) (fn* core.dec "Decrease number by one" [x] (- x 1)) +(local utility-doc-order + [:apply :add :sub :mul :div :le :lt :ge :gt :inc :dec]) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;; Tests and predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -353,6 +356,11 @@ Number is rounded with `math.floor` and compared with original number." (if (not (empty? x)) x)) +(local predicate-doc-order + [:map? :vector? :multifn? :set? :nil? :zero? :pos? + :neg? :even? :odd? :string? :boolean? :true? :false? + :int? :pos-int? :neg-int? :double? :empty? :not-empty]) + ;;;;;;;;;;;;;;;;;;;;;; Sequence manipuletion functions ;;;;;;;;;;;;;;;;;;;;;;;;; @@ -806,6 +814,10 @@ Basic `zipmap` implementation: (when-some [tbl (seq tbl)] (reduce consj (empty []) tbl))) +(local sequence-doc--order [:vector :seq :kvseq :first :rest :last :butlast + :conj :disj :cons :concat :reduce :reduced :reduce-kv + :mapv :filter :every? :some :not-any? :range :reverse]) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Equality ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -905,6 +917,9 @@ use." (tset memo args res) res)))))) +(local function-manipulation-doc-order + [:identity :comp :complement :constantly :memoize]) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Hash table extras ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -985,6 +1000,9 @@ found in the table." ([tbl key & keys] (apply dissoc (dissoc tbl key) keys))) +(local hash-table-doc-order + [:assoc :hash-map :get :get-in :keys :vals :find :dissoc]) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Multimethods ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1024,6 +1042,9 @@ that would apply to that value, or `nil` if none apply and no default." (. multifn :default)) (error (.. (tostring multifn) " is not a multifn") 2))) +(local multimethods-doc-order + [:remove-method :remove-all-methods :methods :get-method]) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Sets ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1229,16 +1250,27 @@ syntax. Use `hash-set` function instead." :__call #(if (. Set $2) $2) :__len (set-length Set) :__index #(match $2 - :cljlib/empty #(hash-set) - _ (if (. Set $2) $2)) + :cljlib/empty #(hash-set) + _ (if (. Set $2) $2)) :__newindex (set-newindex Set) :__ipairs set-ipairs :__pairs set-ipairs :__name "hashed set" :__fennelview viewset}))) +(local set-doc-order + [:ordered-set :hash-set]) + -core +(doto core + (tset :_DOC_ORDER (concat utility-doc-order + [:eq] + predicate-doc-order + sequence-doc--order + function-manipulation-doc-order + hash-table-doc-order + multimethods-doc-order + set-doc-order))) ;; LocalWords: cljlib Clojure's clj lua PUC mapv concat Clojure fn zs ;; LocalWords: defmulti multi arity eq metadata prepending variadic diff --git a/doc/cljlib.md b/doc/cljlib.md index d37d14d..623fc38 100644 --- a/doc/cljlib.md +++ b/doc/cljlib.md @@ -37,92 +37,78 @@ functions](https://clojure.org/guides/learn/functions#_multi_arity_functions). **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) -- [`disj`](#disj) -- [`dissoc`](#dissoc) +- [`add`](#add) +- [`sub`](#sub) +- [`mul`](#mul) - [`div`](#div) -- [`double?`](#double?) -- [`empty?`](#empty?) -- [`eq`](#eq) -- [`even?`](#even?) -- [`every?`](#every?) -- [`false?`](#false?) -- [`filter`](#filter) -- [`find`](#find) -- [`first`](#first) +- [`le`](#le) +- [`lt`](#lt) - [`ge`](#ge) -- [`get`](#get) -- [`get-in`](#get-in) -- [`get-method`](#get-method) - [`gt`](#gt) -- [`hash-map`](#hash-map) -- [`hash-set`](#hash-set) -- [`identity`](#identity) - [`inc`](#inc) -- [`int?`](#int?) -- [`keys`](#keys) -- [`kvseq`](#kvseq) -- [`last`](#last) -- [`le`](#le) -- [`lt`](#lt) +- [`dec`](#dec) +- [`eq`](#eq) - [`map?`](#map?) -- [`mapv`](#mapv) -- [`memoize`](#memoize) -- [`methods`](#methods) -- [`mul`](#mul) +- [`vector?`](#vector?) - [`multifn?`](#multifn?) -- [`neg-int?`](#neg-int?) -- [`neg?`](#neg?) +- [`set?`](#set?) - [`nil?`](#nil?) -- [`not-any?`](#not-any?) -- [`not-empty`](#not-empty) +- [`zero?`](#zero?) +- [`pos?`](#pos?) +- [`neg?`](#neg?) +- [`even?`](#even?) - [`odd?`](#odd?) -- [`ordered-set`](#ordered-set) +- [`string?`](#string?) +- [`boolean?`](#boolean?) +- [`true?`](#true?) +- [`false?`](#false?) +- [`int?`](#int?) - [`pos-int?`](#pos-int?) -- [`pos?`](#pos?) -- [`range`](#range) +- [`neg-int?`](#neg-int?) +- [`double?`](#double?) +- [`empty?`](#empty?) +- [`not-empty`](#not-empty) +- [`vector`](#vector) +- [`seq`](#seq) +- [`kvseq`](#kvseq) +- [`first`](#first) +- [`rest`](#rest) +- [`last`](#last) +- [`butlast`](#butlast) +- [`conj`](#conj) +- [`disj`](#disj) +- [`cons`](#cons) +- [`concat`](#concat) - [`reduce`](#reduce) -- [`reduce-kv`](#reduce-kv) - [`reduced`](#reduced) -- [`remove-all-methods`](#remove-all-methods) -- [`remove-method`](#remove-method) -- [`rest`](#rest) -- [`reverse`](#reverse) -- [`seq`](#seq) -- [`set?`](#set?) +- [`reduce-kv`](#reduce-kv) +- [`mapv`](#mapv) +- [`filter`](#filter) +- [`every?`](#every?) - [`some`](#some) -- [`string?`](#string?) -- [`sub`](#sub) -- [`true?`](#true?) +- [`not-any?`](#not-any?) +- [`range`](#range) +- [`reverse`](#reverse) +- [`identity`](#identity) +- [`comp`](#comp) +- [`complement`](#complement) +- [`constantly`](#constantly) +- [`memoize`](#memoize) +- [`assoc`](#assoc) +- [`hash-map`](#hash-map) +- [`get`](#get) +- [`get-in`](#get-in) +- [`keys`](#keys) - [`vals`](#vals) -- [`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. +- [`find`](#find) +- [`dissoc`](#dissoc) +- [`remove-method`](#remove-method) +- [`remove-all-methods`](#remove-all-methods) +- [`methods`](#methods) +- [`get-method`](#get-method) +- [`ordered-set`](#ordered-set) +- [`hash-set`](#hash-set) ## `apply` Function signature: @@ -152,136 +138,118 @@ Applying `print` to different arguments: ;; => 1 2 3 4 5 6 7 8 9 ``` -## `assoc` +## `add` Function signature: ``` -(assoc - ([tbl k v]) - ([tbl k v & kvs])) +(add + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) ``` -Associate key `k` with value `v` in `tbl`. +Sum arbitrary amount of numbers. -## `boolean?` +## `sub` Function signature: ``` -(boolean? [x]) +(sub + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) ``` -Test if `x` is a Boolean +Subtract arbitrary amount of numbers. -## `butlast` +## `mul` Function signature: ``` -(butlast [col]) +(mul + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) ``` -Returns everything but the last element of a table as a new - table. Calls `seq` on its argument. +Multiply arbitrary amount of numbers. -## `comp` +## `div` Function signature: ``` -(comp - ([f]) - ([f g]) - ([f g & fs])) +(div + ([a]) + ([a b]) + ([a b c]) + ([a b c d]) + ([a b c d & rest])) ``` -Compose functions. +Divide arbitrary amount of numbers. -## `complement` +## `le` Function signature: ``` -(complement [f]) +(le + ([x]) + ([x y]) + ([x y & more])) ``` -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. +Returns true if nums are in monotonically non-decreasing order -## `concat` +## `lt` Function signature: ``` -(concat +(lt ([x]) ([x y]) - ([x y & xs])) + ([x y & more])) ``` -Concatenate tables. +Returns true if nums are in monotonically decreasing order -## `conj` +## `ge` 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} +(ge + ([x]) + ([x y]) + ([x y & more])) ``` -See [`hash-map`](#hash-map) for creating empty associative tables. +Returns true if nums are in monotonically non-increasing order -## `cons` +## `gt` Function signature: ``` -(cons [x tbl]) +(gt + ([x]) + ([x y]) + ([x y & more])) ``` -Insert `x` to `tbl` at the front. Modifies `tbl`. +Returns true if nums are in monotonically increasing order -## `constantly` +## `inc` Function signature: ``` -(constantly [x]) +(inc [x]) ``` -Returns a function that takes any number of arguments and returns `x`. +Increase number by one ## `dec` Function signature: @@ -292,238 +260,213 @@ Function signature: Decrease number by one -## `disj` +## `eq` Function signature: ``` -(disj - ([s]) - ([s k]) - ([s k & ks])) +(eq + ([x]) + ([x y]) + ([x y & xs])) ``` -Remove key `k` from set `s`. +Deep compare values. -## `dissoc` +## `map?` Function signature: ``` -(dissoc - ([tbl]) - ([tbl key]) - ([tbl key & keys])) +(map? [tbl]) ``` -Remove `key` from table `tbl`. +Check whether `tbl` is an associative table. -## `div` -Function signature: +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`. -``` -(div - ([a]) - ([a b]) - ([a b c]) - ([a b c d]) - ([a b c d & rest])) -``` +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. -Divide arbitrary amount of numbers. +### Examples +Non empty tables: -## `double?` -Function signature: +``` fennel +(assert (map? {:a 1 :b 2})) -``` -(double? [x]) +(local some-table {:key :value}) +(assert (map? some-table)) ``` -Test if `x` is a number with floating point data. - -## `empty?` -Function signature: +Empty tables: +``` fennel +(local some-table {}) +(assert (not (map? some-table))) ``` -(empty? [x]) -``` - -Check if collection is empty. -## `eq` -Function signature: +Empty tables created with [`hash-map`](#hash-map) will pass the test: +``` fennel +(local some-table (hash-map)) +(assert (map? some-table)) ``` -(eq - ([x]) - ([x y]) - ([x y & xs])) -``` - -Deep compare values. -## `even?` +## `vector?` Function signature: ``` -(even? [x]) +(vector? [tbl]) ``` -Test if value is even. +Check whether `tbl` is an sequential table. -## `every?` -Function signature: +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`. -``` -(every? [pred tbl]) -``` +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. -Test if every item in `tbl` satisfies the `pred`. +### Examples +Non empty vector: -## `false?` -Function signature: +``` fennel +(assert (vector? [1 2 3 4])) +(local some-table [1 2 3]) +(assert (vector? some-table)) ``` -(false? [x]) + +Empty tables: + +``` fennel +(local some-table []) +(assert (not (vector? some-table))) ``` -Test if `x` is `false` +Empty tables created with [`vector`](#vector) will pass the test: -## `filter` +``` fennel +(local some-table (vector)) +(assert (vector? some-table)) +``` + +## `multifn?` Function signature: ``` -(filter [pred col]) +(multifn? [mf]) ``` -Returns a sequential table of the items in `col` for which `pred` - returns logical true. +Test if `mf` is an instance of `multifn`. -## `find` +`multifn` is a special kind of table, created with `defmulti` macros +from `cljlib-macros.fnl`. + +## `set?` Function signature: ``` -(find [tbl key]) +(set? [s]) ``` -Returns the map entry for `key`, or `nil` if key not present. -## `first` + +## `nil?` Function signature: ``` -(first [col]) +(nil? + ([x])) ``` -Return first element of a table. Calls `seq` on its argument. +Test if value is nil. -## `ge` +## `zero?` Function signature: ``` -(ge - ([x]) - ([x y]) - ([x y & more])) +(zero? [x]) ``` -Returns true if nums are in monotonically non-increasing order +Test if value is equal to zero. -## `get` +## `pos?` Function signature: ``` -(get - ([tbl key]) - ([tbl key not-found])) +(pos? [x]) ``` -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. +Test if `x` is greater than zero. -## `get-in` +## `neg?` Function signature: ``` -(get-in - ([tbl keys]) - ([tbl keys not-found])) +(neg? [x]) ``` -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. +Test if `x` is less than zero. -## `get-method` +## `even?` Function signature: ``` -(get-method [multifn dispatch-val]) +(even? [x]) ``` -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. +Test if value is even. -## `gt` +## `odd?` Function signature: ``` -(gt - ([x]) - ([x y]) - ([x y & more])) +(odd? [x]) ``` -Returns true if nums are in monotonically increasing order +Test if value is odd. -## `hash-map` +## `string?` Function signature: ``` -(hash-map - ([& kvs])) +(string? [x]) ``` -Create associative table from keys and values +Test if `x` is a string. -## `hash-set` +## `boolean?` Function signature: ``` -(hash-set [& xs]) +(boolean? [x]) ``` -Create hash set. - -Set is a collection of unique elements, which sore purpose is only to -tell you if something is in the set or not. - -Hash set differs from ordered set in that the keys are do not have any -particular order. New items are added at the arbitrary position by -using [`conj`](#con) or `tset` functions, and items can be removed -with [`disj`](#disj) or `tset` functions. Rest semantics are the same -as for [`ordered-set`](#ordered-set) - -**Note**: Hash set prints as `#{a b c}`, but this construct is not -supported by the Fennel reader, so you can't create sets with this -syntax. Use `hash-set` function instead. +Test if `x` is a Boolean -## `identity` +## `true?` Function signature: ``` -(identity [x]) +(true? [x]) ``` -Returns its argument. +Test if `x` is `true` -## `inc` +## `false?` Function signature: ``` -(inc [x]) +(false? [x]) ``` -Increase number by one +Test if `x` is `false` ## `int?` Function signature: @@ -536,371 +479,235 @@ Test if `x` is a number without floating point data. Number is rounded with `math.floor` and compared with original number. -## `keys` +## `pos-int?` Function signature: ``` -(keys [tbl]) +(pos-int? [x]) ``` -Returns a sequence of the table's keys, in the same order as [`seq`](#seq). +Test if `x` is a positive integer. -## `kvseq` +## `neg-int?` Function signature: ``` -(kvseq [tbl]) +(neg-int? [x]) ``` -Transforms any table kind to key-value sequence. +Test if `x` is a negetive integer. -## `last` +## `double?` Function signature: ``` -(last [col]) +(double? [x]) ``` -Returns the last element of a table. Calls `seq` on its argument. +Test if `x` is a number with floating point data. -## `le` +## `empty?` Function signature: ``` -(le - ([x]) - ([x y]) - ([x y & more])) +(empty? [x]) ``` -Returns true if nums are in monotonically non-decreasing order +Check if collection is empty. -## `lt` +## `not-empty` Function signature: ``` -(lt - ([x]) - ([x y]) - ([x y & more])) +(not-empty [x]) ``` -Returns true if nums are in monotonically decreasing order +If `x` is empty, returns `nil`, otherwise `x`. -## `map?` +## `vector` Function signature: ``` -(map? [tbl]) +(vector [& args]) ``` -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`. +Constructs sequential table out of it's arguments. -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. +Sets additional metadata for function [`vector?`](#vector?) 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)) +(local v (vector 1 2 3 4)) +(assert (eq v [1 2 3 4])) ``` -## `mapv` +## `seq` Function signature: ``` -(mapv - ([f col]) - ([f col1 col2]) - ([f col1 col2 col3]) - ([f col1 col2 col3 & cols])) +(seq [col]) ``` -Maps function `f` over one or more collections. +Create sequential table. -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. +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 -Map `string.upcase` over the string: +Sequential tables remain as is: ``` fennel -(mapv string.upper "string") -;; => ["S" "T" "R" "I" "N" "G"] +(seq [1 2 3 4]) +;; [1 2 3 4] ``` -Map [`mul`](#mul) over two tables: +Associative tables are transformed to format like this `[[key1 value1] +... [keyN valueN]]` and order is non deterministic: ``` fennel -(mapv mul [1 2 3 4] [1 0 -1]) -;; => [1 0 -3] +(seq {:a 1 :b 2 :c 3}) +;; [[:b 2] [:a 1] [:c 3]] ``` -Basic `zipmap` implementation: +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 -(fn zipmap [keys vals] - (into {} (mapv vector keys vals))) - -(zipmap [:a :b :c] [1 2 3 4]) +(apply conj (hash-map) [:c 3] [[:a 1] [:b 2]]) ;; => {: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. - -## `multifn?` -Function signature: - -``` -(multifn? [mf]) -``` - -Test if `mf` is an instance of `multifn`. - -`multifn` is a special kind of table, created with `defmulti` macros -from `cljlib-macros.fnl`. - -## `neg-int?` -Function signature: - -``` -(neg-int? [x]) -``` - -Test if `x` is a negetive integer. - -## `neg?` +## `kvseq` Function signature: ``` -(neg? [x]) +(kvseq [tbl]) ``` -Test if `x` is less than zero. +Transforms any table kind to key-value sequence. -## `nil?` +## `first` Function signature: ``` -(nil? - ([x])) +(first [col]) ``` -Test if value is nil. +Return first element of a table. Calls `seq` on its argument. -## `not-any?` +## `rest` Function signature: ``` -(not-any? [pred tbl]) +(rest [col]) ``` -Test if no item in `tbl` satisfy the `pred`. +Returns table of all elements of a table but the first one. Calls + `seq` on its argument. -## `not-empty` +## `last` Function signature: ``` -(not-empty [x]) +(last [col]) ``` -If `x` is empty, returns `nil`, otherwise `x`. +Returns the last element of a table. Calls `seq` on its argument. -## `odd?` +## `butlast` Function signature: ``` -(odd? [x]) +(butlast [col]) ``` -Test if value is odd. +Returns everything but the last element of a table as a new + table. Calls `seq` on its argument. -## `ordered-set` +## `conj` Function signature: ``` -(ordered-set [& xs]) +(conj + ([tbl]) + ([tbl x]) + ([tbl x & xs])) ``` -Create ordered set. - -Set is a collection of unique elements, which sore purpose is only to -tell you if something is in the set or not. - -`ordered-set` is follows the argument insertion order, unlike sorted -sets, which apply some sorting algorithm internally. New items added -at the end of the set. Ordered set supports removal of items via -`tset` and [`disj`](#disj). To add element to the ordered set use -`tset` or [`conj`](#conj). Both operations modify the set. - -**Note**: Hash set prints as `#{a b c}`, but this construct is not -supported by the Fennel reader, so you can't create sets with this -syntax. Use `hash-set` function instead. - -Below are some examples of how to create and manipulate sets. - -#### Create ordered set: -Ordered sets are created by passing any amount of elements desired to -be in the set: +Insert `x` as a last element of a table `tbl`. -``` fennel ->> (ordered-set) -###{} ->> (ordered-set :a :c :b) -###{"a" "c" "b"} -``` +If `tbl` is a sequential table or empty table, inserts `x` and +optional `xs` as final element in the table. -Duplicate items are not added: +If `tbl` is an associative table, that satisfies [`map?`](#map?) test, +insert `[key value]` pair into the table. -``` fennel ->> (ordered-set) -###{} ->> (ordered-set :a :c :a :a :a :a :c :b) -###{"a" "c" "b"} -``` +Mutates `tbl`. -#### Check if set contains desired value: -Sets are functions of their keys, so simply calling a set with a -desired key will either return the key, or `nil`: +### Examples +Adding to sequential tables: ``` fennel ->> (local oset (ordered-set [:a :b :c] [:c :d :e] :e :f)) ->> (oset [:a :b :c]) -[:a :b :c] ->> (. oset :e) -:e ->> (oset [:a :b :f]) -nil +(conj [] 1 2 3 4) +;; => [1 2 3 4] +(conj [1 2 3] 4 5) +;; => [1 2 3 4 5] ``` -#### Add items to existing set: -To add element to the set use [`conj`](#conj) or `tset` +Adding to associative tables: ``` fennel ->> (local oset (ordered-set :a :b :c)) ->> (conj oset :d :e) ->> oset -###{"a" "b" "c" "d" "e"} +(conj {:a 1} [:b 2] [:c 3]) +;; => {:a 1 :b 2 :c 3} ``` -##### Remove items from the set: -To add element to the set use [`disj`](#disj) or `tset` +Note, that passing literal empty associative table `{}` will not work: ``` fennel ->> (local oset (ordered-set :a :b :c)) ->> (disj oset :b) ->> oset -###{"a" "c"} ->> (tset oset :a nil) ->> oset -###{"c"} +(conj {} [:a 1] [:b 2]) +;; => [[:a 1] [:b 2]] +(conj (hash-map) [:a 1] [:b 2]) +;; => {:a 1 :b 2} ``` -#### Equality semantics -Both `ordered-set` and [`hash-set`](#hash-set) implement `__eq` metamethod, -and are compared for having the same keys without particular order and -same size: - -``` fennel ->> (= (ordered-set :a :b) (ordered-set :b :a)) -true ->> (= (ordered-set :a :b) (ordered-set :b :a :c)) -false ->> (= (ordered-set :a :b) (hash-set :a :b)) -true -``` +See [`hash-map`](#hash-map) for creating empty associative tables. -## `pos-int?` +## `disj` Function signature: ``` -(pos-int? [x]) +(disj + ([s]) + ([s k]) + ([s k & ks])) ``` -Test if `x` is a positive integer. +Remove key `k` from set `s`. -## `pos?` +## `cons` Function signature: ``` -(pos? [x]) +(cons [x tbl]) ``` -Test if `x` is greater than zero. +Insert `x` to `tbl` at the front. Modifies `tbl`. -## `range` +## `concat` Function signature: ``` -(range - ([upper]) - ([lower upper]) - ([lower upper step])) +(concat + ([x]) + ([x y]) + ([x y & xs])) ``` -return range of of numbers from `lower` to `upper` with optional `step`. +Concatenate tables. ## `reduce` Function signature: @@ -936,6 +743,38 @@ Reduce sequence of numbers with [`add`](#add) ;; => 20 ``` +## `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. + ## `reduce-kv` Function signature: @@ -981,65 +820,99 @@ Reduce table by adding values from keys that start with letter `a`: ;; => 3 ``` -## `reduced` +## `mapv` Function signature: ``` -(reduced [x]) +(mapv + ([f col]) + ([f col1 col2]) + ([f col1 col2 col3]) + ([f col1 col2 col3 & cols])) ``` -Wraps `x` in such a way so [`reduce`](#reduce) will terminate early -with this value. +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 -Stop reduction is result is higher than `10`: +Map `string.upcase` over the string: ``` fennel -(reduce (fn [res x] - (if (>= res 10) - (reduced res) - (+ res x))) - [1 2 3]) -;; => 6 +(mapv string.upper "string") +;; => ["S" "T" "R" "I" "N" "G"] +``` -(reduce (fn [res x] - (if (>= res 10) - (reduced res) - (+ res x))) - [1 2 3 4 :nil]) -;; => 10 +Map [`mul`](#mul) over two tables: + +``` fennel +(mapv mul [1 2 3 4] [1 0 -1]) +;; => [1 0 -3] ``` -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. +Basic `zipmap` implementation: -## `remove-all-methods` +``` fennel +(fn zipmap [keys vals] + (into {} (mapv vector keys vals))) + +(zipmap [:a :b :c] [1 2 3 4]) +;; => {:a 1 :b 2 :c 3} +``` + +## `filter` Function signature: ``` -(remove-all-methods [multifn]) +(filter [pred col]) ``` -Removes all of the methods of multimethod +Returns a sequential table of the items in `col` for which `pred` + returns logical true. -## `remove-method` +## `every?` Function signature: ``` -(remove-method [multifn dispatch-val]) +(every? [pred tbl]) ``` -Remove method from `multifn` for given `dispatch-val`. +Test if every item in `tbl` satisfies the `pred`. -## `rest` +## `some` Function signature: ``` -(rest [col]) +(some [pred tbl]) ``` -Returns table of all elements of a table but the first one. Calls - `seq` on its argument. +Test if any item in `tbl` satisfies the `pred`. + +## `not-any?` +Function signature: + +``` +(not-any? [pred tbl]) +``` + +Test if no item in `tbl` satisfy the `pred`. + +## `range` +Function signature: + +``` +(range + ([upper]) + ([lower upper]) + ([lower upper step])) +``` + +return range of of numbers from `lower` to `upper` with optional `step`. ## `reverse` Function signature: @@ -1050,94 +923,115 @@ Function signature: Returns table with same items as in `tbl` but in reverse order. -## `seq` +## `identity` Function signature: ``` -(seq [col]) +(identity [x]) ``` -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. +Returns its argument. -### Examples -Sequential tables remain as is: +## `comp` +Function signature: -``` fennel -(seq [1 2 3 4]) -;; [1 2 3 4] +``` +(comp + ([f]) + ([f g]) + ([f g & fs])) ``` -Associative tables are transformed to format like this `[[key1 value1] -... [keyN valueN]]` and order is non deterministic: +Compose functions. -``` fennel -(seq {:a 1 :b 2 :c 3}) -;; [[:b 2] [:a 1] [:c 3]] +## `complement` +Function signature: + +``` +(complement [f]) ``` -See `into` macros for transforming this back to associative table. -Additionally you can use [`conj`](#conj) and [`apply`](#apply) with -[`hash-map`](#hash-map): +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. + +## `constantly` +Function signature: -``` fennel -(apply conj (hash-map) [:c 3] [[:a 1] [:b 2]]) -;; => {:a 1 :b 2 :c 3} +``` +(constantly [x]) ``` -## `set?` +Returns a function that takes any number of arguments and returns `x`. + +## `memoize` Function signature: ``` -(set? [s]) +(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. +## `assoc` +Function signature: -## `some` +``` +(assoc + ([tbl k v]) + ([tbl k v & kvs])) +``` + +Associate key `k` with value `v` in `tbl`. + +## `hash-map` Function signature: ``` -(some [pred tbl]) +(hash-map + ([& kvs])) ``` -Test if any item in `tbl` satisfies the `pred`. +Create associative table from keys and values -## `string?` +## `get` Function signature: ``` -(string? [x]) +(get + ([tbl key]) + ([tbl key not-found])) ``` -Test if `x` is a string. +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. -## `sub` +## `get-in` Function signature: ``` -(sub - ([a]) - ([a b]) - ([a b c]) - ([a b c d]) - ([a b c d & rest])) +(get-in + ([tbl keys]) + ([tbl keys not-found])) ``` -Subtract arbitrary amount of numbers. +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. -## `true?` +## `keys` Function signature: ``` -(true? [x]) +(keys [tbl]) ``` -Test if `x` is `true` +Returns a sequence of the table's keys, in the same order as [`seq`](#seq). ## `vals` Function signature: @@ -1148,74 +1042,180 @@ Function signature: Returns a sequence of the table's values, in the same order as [`seq`](#seq). -## `vector` +## `find` Function signature: ``` -(vector [& args]) +(find [tbl key]) ``` -Constructs sequential table out of it's arguments. +Returns the map entry for `key`, or `nil` if key not present. -Sets additional metadata for function [`vector?`](#vector?) to work. +## `dissoc` +Function signature: -### Examples +``` +(dissoc + ([tbl]) + ([tbl key]) + ([tbl key & keys])) +``` + +Remove `key` from table `tbl`. + +## `remove-method` +Function signature: -``` fennel -(local v (vector 1 2 3 4)) -(assert (eq v [1 2 3 4])) +``` +(remove-method [multifn dispatch-val]) ``` -## `vector?` +Remove method from `multifn` for given `dispatch-val`. + +## `remove-all-methods` Function signature: ``` -(vector? [tbl]) +(remove-all-methods [multifn]) ``` -Check whether `tbl` is an sequential table. +Removes all of the methods of multimethod -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`. +## `methods` +Function signature: -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. +``` +(methods [multifn]) +``` -### Examples -Non empty vector: +Given a multimethod, returns a map of dispatch values -> dispatch fns + +## `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. + +## `ordered-set` +Function signature: + +``` +(ordered-set [& xs]) +``` + +Create ordered set. + +Set is a collection of unique elements, which sore purpose is only to +tell you if something is in the set or not. + +`ordered-set` is follows the argument insertion order, unlike sorted +sets, which apply some sorting algorithm internally. New items added +at the end of the set. Ordered set supports removal of items via +`tset` and [`disj`](#disj). To add element to the ordered set use +`tset` or [`conj`](#conj). Both operations modify the set. + +**Note**: Hash set prints as `#{a b c}`, but this construct is not +supported by the Fennel reader, so you can't create sets with this +syntax. Use `hash-set` function instead. + +Below are some examples of how to create and manipulate sets. + +#### Create ordered set: +Ordered sets are created by passing any amount of elements desired to +be in the set: ``` fennel -(assert (vector? [1 2 3 4])) +>> (ordered-set) +###{} +>> (ordered-set :a :c :b) +###{"a" "c" "b"} +``` -(local some-table [1 2 3]) -(assert (vector? some-table)) +Duplicate items are not added: + +``` fennel +>> (ordered-set) +###{} +>> (ordered-set :a :c :a :a :a :a :c :b) +###{"a" "c" "b"} ``` -Empty tables: +#### Check if set contains desired value: +Sets are functions of their keys, so simply calling a set with a +desired key will either return the key, or `nil`: ``` fennel -(local some-table []) -(assert (not (vector? some-table))) +>> (local oset (ordered-set [:a :b :c] [:c :d :e] :e :f)) +>> (oset [:a :b :c]) +[:a :b :c] +>> (. oset :e) +:e +>> (oset [:a :b :f]) +nil ``` -Empty tables created with [`vector`](#vector) will pass the test: +#### Add items to existing set: +To add element to the set use [`conj`](#conj) or `tset` ``` fennel -(local some-table (vector)) -(assert (vector? some-table)) +>> (local oset (ordered-set :a :b :c)) +>> (conj oset :d :e) +>> oset +###{"a" "b" "c" "d" "e"} ``` -## `zero?` +##### Remove items from the set: +To add element to the set use [`disj`](#disj) or `tset` + +``` fennel +>> (local oset (ordered-set :a :b :c)) +>> (disj oset :b) +>> oset +###{"a" "c"} +>> (tset oset :a nil) +>> oset +###{"c"} +``` + +#### Equality semantics +Both `ordered-set` and [`hash-set`](#hash-set) implement `__eq` metamethod, +and are compared for having the same keys without particular order and +same size: + +``` fennel +>> (= (ordered-set :a :b) (ordered-set :b :a)) +true +>> (= (ordered-set :a :b) (ordered-set :b :a :c)) +false +>> (= (ordered-set :a :b) (hash-set :a :b)) +true +``` + +## `hash-set` Function signature: ``` -(zero? [x]) +(hash-set [& xs]) ``` -Test if value is equal to zero. +Create hash set. + +Set is a collection of unique elements, which sore purpose is only to +tell you if something is in the set or not. + +Hash set differs from ordered set in that the keys are do not have any +particular order. New items are added at the arbitrary position by +using [`conj`](#con) or `tset` functions, and items can be removed +with [`disj`](#disj) or `tset` functions. Rest semantics are the same +as for [`ordered-set`](#ordered-set) + +**Note**: Hash set prints as `#{a b c}`, but this construct is not +supported by the Fennel reader, so you can't create sets with this +syntax. Use `hash-set` function instead. --- diff --git a/doc/tests/test.md b/doc/tests/test.md new file mode 100644 index 0000000..c74d501 --- /dev/null +++ b/doc/tests/test.md @@ -0,0 +1,98 @@ +# Test.fnl + +**Table of contents** + +- [`deftest`](#deftest) +- [`testing`](#testing) +- [`assert-eq`](#assert-eq) +- [`assert-ne`](#assert-ne) +- [`assert-is`](#assert-is) +- [`assert-not`](#assert-not) + +## `deftest` +Function signature: + +``` +(deftest name ...) +``` + +Simple way of grouping tests. + +## `testing` +Function signature: + +``` +(testing description ...) +``` + +Print test description and run it. + +## `assert-eq` +Function signature: + +``` +(assert-eq expr1 expr2 msg) +``` + +Like `assert`, except compares results of two expressions on equality. +Generates formatted message if `msg` is not set to other message. + +### Example +Compare two expressions: + +``` fennel +>> (assert-eq 1 (+1 1)) +runtime error: equality assertion failed + Left: 1 + Right: 3 +``` + +Deep compare values: + +``` fennel +>> (assert-eq [1 {[2 3] [4 5 6]}] [1 {[2 3] [4 5]}]) +runtime error: equality assertion failed + Left: [1 { + [2 3] [4 5 6] + }] + Right: [1 { + [2 3] [4 5] + }] +``` + +## `assert-ne` +Function signature: + +``` +(assert-ne expr1 expr2 msg) +``` + +Assert for unequality. Same as [`assert-eq`](#assert-eq). + +## `assert-is` +Function signature: + +``` +(assert-is expr msg) +``` + +Assert for truth. Same as inbuilt `assert`, except generates more + verbose message if `msg` is not set. + +``` fennel +>> (assert-is (= 1 2 3)) +runtime error: assertion failed for (= 1 2 3) +``` + +## `assert-not` +Function signature: + +``` +(assert-not expr msg) +``` + +Assert for not truth. Works the same as [`assert-is`](#assert-is). + + +<!-- Generated with Fenneldoc 0.0.5 + https://gitlab.com/andreyorst/fenneldoc --> diff --git a/tests/test.fnl b/tests/test.fnl index 0fcd750..f3db701 100644 --- a/tests/test.fnl +++ b/tests/test.fnl @@ -35,6 +35,31 @@ the tables uses tables as keys." (fn test.assert-eq [expr1 expr2 msg] + "Like `assert`, except compares results of two expressions on equality. +Generates formatted message if `msg` is not set to other message. + +# Example +Compare two expressions: + +``` fennel +>> (assert-eq 1 (+1 1)) +runtime error: equality assertion failed + Left: 1 + Right: 3 +``` + +Deep compare values: + +``` fennel +>> (assert-eq [1 {[2 3] [4 5 6]}] [1 {[2 3] [4 5]}]) +runtime error: equality assertion failed + Left: [1 { + [2 3] [4 5 6] + }] + Right: [1 { + [2 3] [4 5] + }] +```" `(let [left# ,expr1 right# ,expr2 (res# view#) (pcall require :fennelview) @@ -47,6 +72,7 @@ the tables uses tables as keys." (fn test.assert-ne [expr1 expr2 msg] + "Assert for unequality. Same as [`assert-eq`](#assert-eq)." `(let [left# ,expr1 right# ,expr2 (res# view#) (pcall require :fennelview) @@ -59,16 +85,24 @@ the tables uses tables as keys." (fn test.assert-is [expr msg] + "Assert for truth. Same as inbuilt `assert`, except generates more + verbose message if `msg` is not set. + +``` fennel +>> (assert-is (= 1 2 3)) +runtime error: assertion failed for (= 1 2 3) +```" `(assert ,expr (.. "assertion failed for " (or ,msg ,(tostring expr))))) (fn test.assert-not [expr msg] + "Assert for not truth. Works the same as [`assert-is`](#assert-is)." `(assert (not ,expr) (.. "assertion failed for " (or ,msg ,(tostring expr))))) (fn test.deftest [name ...] - "Simple way of grouping tests" + "Simple way of grouping tests." `(do ,...)) (fn test.testing @@ -77,4 +111,7 @@ the tables uses tables as keys." `(do (io.stderr:write (.. "testing: " ,description "\n")) ,...)) -test +(doto test + (tset :_DOC_ORDER #[:deftest :testing + :assert-eq :assert-ne + :assert-is :assert-not])) |