From 4ea70bf39971eeb742a5de689a9ad8ba63559ecb Mon Sep 17 00:00:00 2001 From: Andrey Orst Date: Sat, 14 Nov 2020 16:51:51 +0300 Subject: Feature(doc): upload autogenerated documentation. --- README.org | 517 +------------------------------------------------------------ 1 file changed, 1 insertion(+), 516 deletions(-) (limited to 'README.org') diff --git a/README.org b/README.org index 6204a59..e30d62c 100644 --- a/README.org +++ b/README.org @@ -11,519 +11,4 @@ Even though it is project is experimental, the goals of this project are: - Be close to the platform, e.g. implement functions in a way that is efficient to use in Lua VM, - Be well documented library, with good test coverage. - -* Macros -List of macros provided by the library. - -** 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. - -#+begin_src fennel - >> (local foo (with-meta (fn [...] (let [[x y z] [...]] (+ x y z))) - {:fnl/arglist [:x :y :z :...] - :fnl/docstring "sum first three values"})) - >> (doc foo) - (foo x y z ...) - sum first three values -#+end_src - -When metadata feature is not enabled, returns the value without additional metadata. - -*** =meta= -Get metadata table from object: - -#+begin_src fennel - >> (meta (with-meta {} {:meta "data"})) - { - :meta "data" - } -#+end_src - -** =def= and =defonce= -=def= is wrappers around =local= which can declare variables inside namespace, and as local at the same time: - -#+begin_src fennel - >> (def ns {}) - >> (def a 10) - >> a - 10 - >> (def ns.a 20) - >> a - 20 - >> ns.a - 20 -#+end_src - -Both =ns.a= and =a= refer to the same value. - -=defonce= ensures that the binding isn't overridden by another =defonce=: - -#+begin_src fennel - >> (defonce ns {}) - >> (defonce ns.a 42) - >> (defonce ns 10) - >> ns - {:a 42} - >> a - 42 -#+end_src - -Both =def= and =defonce= support literal metadata table as first argument, or a :dynamic keyword, that uses Fennel =var= instead of =local=: - -#+begin_src fennel - >> (def {:dynamic true} a 10) - >> (set a 20) - >> a - 20 - >> (defonce :dynamic b 40) - >> (set b 42) - >> b - 42 -#+end_src - -Documentation string can be attached to value via =:doc= keyword. -However it is not recommended to attach metadata to everything except tables and functions: - -#+begin_src fennel - ;; 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}) -#+end_src - -** =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: - -#+begin_src fennel - (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] -#+end_src - -Both variants support up to one arity with =& more=: - -#+begin_src fennel - (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 -#+end_src - -One extra capability of =fn*= supports the same semantic as =def= regarding namespaces: - -#+begin_src fennel - (local ns {}) - - (fn* ns.plus - ([] 0) - ([x] x) - ([x y] (+ x y)) - ([x y & zs] (apply plus (+ x y) zs))) - - ns -#+end_src - -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=: - -#+begin_src fennel - >> (local ns (require :module)) - >> ns - {add #} -#+end_src - -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. - -#+begin_src fennel - (local plus - (do (fn plus [...] - ;; plus body - ) - (set ns.plus plus) - plus)) -#+end_src - -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: - -#+begin_src fennel - (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 -#+end_src - -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. - -#+begin_src fennel - (if-let [val (test)] - (print val) - :fail) -#+end_src - -Expanded form: - -#+begin_src fennel - (let [tmp (test)] - (if tmp - (let [val tmp] - (print val)) - :fail)) -#+end_src - -=when-let= is mostly the same, except doesn't have false branch and accepts any amount of forms: - -#+begin_src fennel - (when-let [val (test)] - (print val) - val) -#+end_src - -Expanded form: - -#+begin_src fennel - (let [tmp (test)] - (if tmp - (let [val tmp] - (print val) - val))) -#+end_src - -** =if-some= and =when-some= -Much like =if-let= and =when-let=, except tests expression for not being =nil=. - -#+begin_src fennel - (when-some [val (foo)] - (print (.. "val is not nil: " val)) - val) -#+end_src - -** =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. - -#+begin_src fennel - (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} -#+end_src - -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: - -#+begin_src fennel - (local a []) - (into a {:a 1 :b 2}) ;; => [["b" 2] ["a" 1]] - - (local b {}) - (into b {:a 1 :b 2}) ;; => [["b" 2] ["a" 1]] -#+end_src - -However, if target table is not empty, its type can be deduced: - -#+begin_src fennel - (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]] -#+end_src - -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: - -#+begin_src fennel - (defmulti fac (fn [x] x)) - - (defmethod fac 0 [_] 1) - (defmethod fac :default [x] (* x (fac (- x 1)))) - - (fac 4) ;; => 24 -#+end_src - -=:default= is a special method which gets called when no other methods were found for given dispatch value. - - -* Functions -Here are some important functions from the library. -Full set can be examined by requiring the module. - -** =seq= -=seq= produces a sequential table from any kind of table in linear time. -Works mostly like in Clojure, but, since Fennel doesn't have list object, it returns sequential table or =nil=: - -#+begin_src fennel - (seq [1 2 3 4 5]) ;; => [1 2 3 4 5] - (seq {:a 1 :b 2 :c 3 :d 4}) - ;; => [["d" 4] ["a" 1] ["b" 2] ["c" 3]] - (seq []) ;; => nil - (seq {}) ;; => nil -#+end_src - -See =into= on how to transform such sequence back into associative table. - -** =first=, =last=, =butlast=, and =rest= -=first= returns first value of a table. -It call =seq= on it, so this takes linear time for any kind of table. -As a consequence, associative tables are supported: - -#+begin_src fennel - (first [1 2 3]) ;; => 1 - (first {:host "localhost" :port 2344 :options {}}) - ;; => ["host" "localhost"] -#+end_src - -=last= returns the last argument from table: - -#+begin_src fennel - (last [1 2 3]) ;; => 3 - (last {:a 1 :b 2}) ;; => [:b 2] -#+end_src - -=butlast= returns everything from the table, except the last item: - -#+begin_src fennel - (butlast [1 2 3]) ;; => [1 2] -#+end_src - -=rest= works the same way, but returns everything except first item of a table. - -#+begin_src fennel - (rest [1 2 3]) ;; => [2 3] - (rest {:host "localhost" :port 2344 :options {}}) - ;; => [["port" 2344] ["options" {}]] -#+end_src - -All these functions call =seq= on its argument, therefore expect everything to happen in linear time. -Because of that these functions are expensive, therefore should be avoided when table type is known beforehand, and the table can be manipulated with =.= or =get=. - -** =conj= and =cons= -Append and prepend item to the table. -Unlike Clojure, =conj=, and =cons= modify table passed to these functions. -This is done both to avoid copying of whole thing, and because Fennel doesn't have immutability guarantees. - -=cons= accepts value as its first argument and table as second, and puts value to the front of the table: - -#+begin_src fennel - (cons 1 [2 3]) ;; => [1 2 3] -#+end_src - -=conj= accepts table as its first argument and any amount of values afterwards. -It puts values in order given into the table: - -#+begin_src fennel - (conj [] 1 2 3) ;; => [1 2 3] -#+end_src - -It is also possible to add items to associative table: - -#+begin_src fennel - (conj {:a 1} [:b 2]) ;; => {:a 1 :b 2} - (conj {:a 1} [:b 2] [:a 0]) ;; => {:a 0 :b 2} -#+end_src - -Both functions return the resulting table, so it is possible to nest calls to both of these. -As an example, here's a classic map function: - -#+begin_src fennel - (fn map [f col] - (if-some [val (first col)] - (cons (f val) (map f (rest col))) - [])) -#+end_src - -=col= is not modified by the =map= function described above, but the =[]= table in the =else= branch of =is-some= is eventually modified by the stack of calls to =cons=. -However this library provides more efficient versions of map, that support arbitrary amount of tables. - -** =mapv= -Mapping function over table. -In Clojure we have a =seq= abstraction, that allows us to use single =mapv= on both vectors, and hash tables. -In this library the =seq= function is implemented in a similar way, so you can expect =mapv= to behave similarly to Clojure: - -#+begin_src fennel - (fn cube [x] (* x x x)) - (mapv cube [1 2 3]) ;; => [1 8 27] - - (mapv #(* $1 $2) [1 2 3] [1 -1 0]) ;; => [1 -2 0] - - (mapv (fn [f-name s-name company position] - (.. f-name " " s-name " works as " position " at " company)) - ["Bob" "Alice"] - ["Smith" "Watson"] - ["Happy Days co." "Coffee With You"] - ["secretary" "chief officer"]) - ;; => ["Bob Smith works as secretary at Happy Days co." - ;; "Alice Watson works as chief officer at Coffee With You"] - - (mapv (fn [[k v]] [(string.upper k) v]) {:host "localhost" :port 1344}) - ;; => [["HOST" "localhost"] ["PORT" 1344]] -#+end_src - -** =reduce= and =reduce-kv= -Ordinary reducing functions. -Work the same as in Clojure, except doesn't yield transducer when only function was passed. - -#+begin_src fennel - (fn add [a b] (+ a b)) - (reduce add [1 2 3 4 5]) ;; => 15 - (reduce add 10 [1 2 3 4 5]) ;; => 25 -#+end_src - -=reduce-kv= expects function that accepts 3 arguments and initial value. -Then it maps function over the associative map, by passing initial value as a first argument, key as second argument, and value as third argument. - -#+begin_src fennel - (reduce-kv (fn [acc key val] - (if (or (= key :a) (= key :c)) - (+ acc val) acc)) - 0 - {:a 10 :b -20 :c 10}) - ;; => 20 -#+end_src - -** Predicate functions -Set of functions, that are small but useful with =mapv= or =reduce=. -These are commonly used so it makes sense to have that, without defining via anonymous function or =#= shorthand every time. - -- =map?= - check if table is an associative table. - Returns =false= for empty table. -- =seq?= - check if table is a sequential table - Returns =false= for empty table. - -Other predicates are self-explanatory: - -- =assoc?= -- =boolean?= -- =double?= -- =empty?= -- =even?= -- =false?= -- =int?= -- =neg?= -- =nil?= -- =odd?= -- =pos?= -- =string?= -- =true?= -- =zero?= - -** =eq= -Deep compare values. -If given two tables, recursively calls =eq= on each field until one of the tables exhausted. -Other values are compared with default equality operator. - -** =comp= -Compose functions into one function. - -#+begin_src fennel - (fn square [x] (^ x 2)) - (fn inc [x] (+ x 1)) - - ((comp square inc) 5) ;; => 36 -#+end_src - -# LocalWords: Luajit VM arity runtime multi Cljlib fn mapv kv REPL -# LocalWords: namespaced namespace eq metatable Lua defonce arglist -# LocalWords: namespaces defmulti defmethod metamethod butlast -# LocalWords: prepend LocalWords docstring - -** =every?= and =not-any?= -=every?= checks if predicate is true for every item in the table. -=not-any?= checks if predicate is false foe every item in the table. - -#+begin_src fennel - >> (every? pos-int? [1 2 3 4]) - true - >> (not-any? pos-int? [-1 -2 -3 4.2]) - true -#+end_src +Documentation is autogenerated with [[https://gitlab.com/andreyorst/fenneldoc][Fenneldoc]] and can be found [[https://gitlab.com/andreyorst/fennel-cljlib/-/tree/master/doc][here]]. -- cgit v1.2.3