summaryrefslogtreecommitdiff
path: root/README.org
diff options
context:
space:
mode:
Diffstat (limited to 'README.org')
-rw-r--r--README.org517
1 files changed, 1 insertions, 516 deletions
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 #<function 0xbada55code>}
-#+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]].