Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
fennel-reference(5)	       Fennel Reference		   fennel-reference(5)

NAME
       fennel-reference	- Fennel Reference

DESCRIPTION
       This  document  covers  the  syntax, built-in macros, and special forms
       recognized by the Fennel	compiler.  It does not	include	 built-in  Lua
       functions;  see	the  Lua  reference  manual  <https://www.lua.org/man-
       ual/5.1/> or the	 Lua  primer  <https://fennel-lang.org/lua-primer> for
       that.  This is not an introductory text;	see the	tutorial <https://fen-
       nel-lang.org/tutorial> for  that.   If  you already have	a piece	of Lua
       code you	 just  want  to	 see  translated  to  Fennel,  use  antifennel
       <https://fennel-lang.org/see>.

       A  macro	 is  a function	which runs at compile time and transforms some
       Fennel code into	different Fennel.  A special form (or  special)	 is  a
       primitive  construct  which emits Lua code directly.  When you are cod-
       ing, you	don't need to  care  about  the	 difference  between  built-in
       macros and special forms; it is an implementation detail.

       Remember	 that Fennel relies completely on Lua for its runtime.	Every-
       thing Fennel does happens at compile-time, so you will need  to	famil-
       iarize yourself with Lua's standard library functions.  Thankfully it's
       much smaller than almost	any other language.

       The one exception to this compile-time rule is the fennel.view function
       which  returns  a string	representation of any Fennel data suitable for
       printing.  But this is not part of the language itself; it is a library
       function	which can be used from Lua just	as easily.

       Fennel source code should be UTF-8-encoded text.

SYNTAX
       (parentheses): used to delimit lists, which are primarily used  to  de-
       note  calls  to	functions,  macros,  and  specials.   Lists are	a com-
       pile-time construct; they are not used at runtime.  For example:	(print
       "hello world")

       {curly brackets}: used to denote	key/value table	literals,  also	 known
       as  dictionaries.   For	example:  {:a 1	:b 2} In a table if you	have a
       string key followed by a	symbol of the same name	as the string, you can
       use : as	the key	and it will be expanded	to  a  string  containing  the
       name of the following symbol.

	      {: this} ; is shorthand for {:this this}

       [square	brackets]: used	to denote sequential tables, which can be used
       for literal data	structures and also in specials	and macros to  delimit
       where  new  identifiers	are  introduced, such as argument lists	or let
       bindings.  For example: [1 2 3]

       The syntax for numbers is the same as  Lua's  <https://www.lua.org/man-
       ual/5.4/manual.html#3.1>,  except that underscores may be used to sepa-
       rate digits for readability.  Non-ASCII digits are not  yet  supported.
       Infinity	 and negative infinity are represented as .inf and -.inf.  NaN
       and negative Nan	are .nan and -.nan.

       The syntax for strings uses double-quotes " around  the	string's  con-
       tents.  Double quotes inside a string must be escaped with backslashes.
       The  syntax  for	 these	is the same as Lua's <https://www.lua.org/man-
       ual/5.4/manual.html#3.1>, except	that strings may contain newline char-
       acters.	Single-quoted or long bracket strings are not supported.

       Fennel has a lot	fewer restrictions on identifiers than	Lua.   Identi-
       fiers  are  represented by symbols, but identifiers are not exactly the
       same as symbols;	some symbols are used by macros	for things other  than
       identifiers.   Symbols  may  not	 begin with digits or a	colon, but may
       have digits anywhere else.  Beyond that,	any unicode characters are ac-
       cepted as long as they are not unprintable or whitespace,  one  of  the
       delimiter  characters  mentioned	 above,	one of the a prefix characters
       listed below, or	one of these reserved characters:

        single	quote: '

        tilde:	~

        semicolon: ;

        at: @

       Underscores are allowed in identifier names, but	dashes	are  preferred
       as  word	 separators.   By convention, identifiers starting with	under-
       scores are used to indicate that	a local	is bound but not meant	to  be
       used.

       The ampersand character & is allowed in symbols but not in identifiers.
       This  allows  it	to be reserved for macros, like	the behavior of	&as in
       destructuring.

       Symbols that contain a dot . or colon : are considered "multi symbols".
       The part	of the symbol before the first dot or  colon  is  used	as  an
       identifier, and the part	after the dot or colon is a field looked up on
       the local identified.  A	colon is only allowed before the final segment
       of  a  multi  symbol,  so x.y:z is valid	but a:b.c is not.  Colon multi
       symbols can only	be used	for method calls.

       Fennel also supports certain kinds of strings that begin	with  a	 colon
       as  long	as they	don't contain any characters which wouldn't be allowed
       in a symbol, for	example	:fennel-lang.org is another way	of writing the
       string "fennel-lang.org".

       Spaces, tabs, newlines, vertical	tabs, form feeds, and carriage returns
       are counted as whitespace.  Non-ASCII whitespace	characters are not yet
       supported.

       Certain prefixes	are expanded by	the parser into	longhand equivalents:

        #foo expands to (hashfn foo)

        `foo expands to (quote	foo)

        ,foo expands to (unquote foo)

       A semicolon and everything following it up to the end of	the line is  a
       comment.

FUNCTIONS
   fn function
       Creates	a  function  which binds the arguments given inside the	square
       brackets.  Will accept any number of arguments; ones in excess  of  the
       declared	 ones are ignored, and if not enough arguments are supplied to
       cover the declared ones,	the remaining ones are given values of nil.

       Example:

	      (fn pxy [x y]
		(print (+ x y)))

       Giving it a name	is optional; if	one is provided	it will	be bound to it
       as a local.  The	following mean exactly the same	thing;	the  first  is
       preferred  mostly  for  indentation reasons, but	also because it	allows
       recursion:

	      (fn pxy [x y]
		(print (+ x y)))

	      (local pxy (fn [x	y]
			   (print (+ x y))))

       Providing a name	that's a table field will cause	it to be inserted in a
       table instead of	bound as a local:

	      (local functions {})

	      (fn functions.p [x y z]
		(print (* x (+ y z))))

	      ;; equivalent to:
	      (set functions.p (fn [x y	z]
				 (print	(* x (+	y z)))))

       Like Lua, functions in Fennel support tail-call optimization,  allowing
       (among  other  things)  functions to recurse indefinitely without over-
       flowing the stack, provided the call is in a tail position.

       The final form in this and all other function forms is used as the  re-
       turn value.

   lambda/ nil-checked function
       Creates	a function like	fn does, but throws an error at	runtime	if any
       of the listed arguments are nil,	unless its identifier begins with ?.

       Example:

	      (lambda [x ?y z]
		(print (- x (* (or ?y 1) z))))

       Note that the Lua runtime will fill in missing arguments	with nil  when
       they  are  not  provided	 by the	caller,	so an explicit nil argument is
       usually no different than omitting an argument.

       Programmers coming from other languages in which	it is an error to call
       a function with a different number of arguments than it is defined with
       often get tripped up by the behavior of fn.  This is  where  lambda  is
       most useful.

       The  lambda,  case,  case-try,  match  and match-try forms are the only
       place where the ?foo notation is	used by	the compiler to	indicate  that
       a nil value is allowed, but it is a useful notation elsewhere to	commu-
       nicate intent anywhere a	new local is introduced.

       The  form is an alias for lambda	and behaves identically.

   Docstrings and metadata
       The fn, lambda,	and macro forms	accept an optional docstring.

	      (fn pxy [x y]
		"Print the sum of x and	y"
		(print (+ x y)))

	      (	pxyz [x	?y z]
		"Print the sum of x, y,	and z. If y is not provided, defaults to 0."
		(print (+ x (or	?y 0) z)))

       These  are  ignored  by default outside of the REPL, unless metadata is
       enabled from  the  CLI  (---metadata)  or  compiler  options  {useMeta-
       data=true},  in	which  case  they are stored in	a metadata table along
       with the	arglist, enabling viewing function docs	via the	doc macro.

	      ;; this only works in the	repl
	      >> ,doc pxy
	      (pxy x y)
		Print the sum of x and y

       Docstrings and other metadata can also be accessed via functions	on the
       fennel API with fennel.doc and fennel.metadata.

       (Since 1.1.0)

       All forms that accept a docstring will also accept a metadata table  in
       the same	place:

	      (fn add [...]
		{:fnl/docstring	"Add arbitrary amount of numbers."
		 :fnl/arglist [a b & more]}
		(case (values (select :# ...) ...)
		  (0) 0
		  (1 a)	a
		  (2 a b) (+ a b)
		  (_ a b) (add (+ a b) (select 3 ...))))

       Here the	arglist	is overridden by that in the metadata table (note that
       the contents of the table are implicitly	quoted).  Calling ,doc command
       in the REPL prints specified argument list of the next form:

	      >> ,doc add
	      (add a b & more)
		Add arbitrary amount of	numbers.

       (Since 1.3.0)

       Arbitrary metadata keys are allowed in the metadata table syntax:

	      (fn foo []
		{:deprecated "v1.9.0"
		 :fnl/docstring	"*DEPRECATED* use foo2"}
		;; old way to do stuff
		)

	      (fn foo2 [x]
		{:added	"v2.0.0"
		 :fnl/docstring	"Incompatible but better version of foo!"}
		;; do stuff better, now	with x!
		x)

       In this example,	the deprecated and added keys are used to store	a ver-
       sion  of	 a hypothetical	library	on which the functions were deprecated
       or added.  External tooling then	can leverage this information by using
       Fennel's	metadata API:

	      >> (local	{: metadata} (require :fennel))
	      >> (metadata:get foo :deprecated)
	      "v1.9.0"
	      >> (metadata:get foo2 :added)
	      "v2.0.0"

       Such metadata can be any	data literal, including	tables,	with the  only
       restriction  that there are no side effects.  Fennel's lists are	disal-
       lowed as	metadata values.

       (Since 1.3.1)

       For editing convenience,	the metadata table literals are	allowed	 after
       docstrings:

	      (fn some-function	[x ...]
		"Docstring for some-function."
		{:fnl/arglist [x & xs]
		 :other	:metadata}
		(let [xs [...]]
		  ;; ...
		  ))

       In this case, the documentation string is automatically inserted	to the
       metadata	table by the compiler.

       The  whole metadata table can be	obtained by calling metadata:get with-
       out the key argument:

	      >> (local	{: metadata} (require :fennel))
	      >> (metadata:get some-function)
	      {:fnl/arglist ["x" "&" "xs"]
	       :fnl/docstring "Docstring for some-function."
	       :other "metadata"}

       Fennel itself only uses the fnl/docstring and fnl/arglist metadata keys
       but third-party code can	make use of arbitrary keys.

   Hash	function literal shorthand
       It's pretty easy	to create function literals, but  Fennel  provides  an
       even shorter form of functions.	Hash functions are anonymous functions
       of  one	form, with implicitly named arguments.	All of the below func-
       tions are functionally equivalent:

	      (fn [a b]	(+ a b))

	      (hashfn (+ $1 $2)) ; implementation detail; don't	use directly

	      #(+ $1 $2)

       This style of anonymous function	is useful as a parameter to higher or-
       der functions.  It's recommended	only  for  simple  one-line  functions
       that get	passed as arguments to other functions.

       The  current implementation only	allows for hash	functions to use up to
       9 arguments, each named $1 through $9, or those	with  varargs,	delin-
       eated by	$... instead of	the usual ....	A lone $ in a hash function is
       treated as an alias for $1.

       Hash  functions	are defined with the hashfn macro or special character
       #, which	wraps its single argument in a function	literal.  For example,

	      #$3		; same as (fn [x y z] z)
	      #[$1 $2 $3]	; same as (fn [a b c] [a b c])
	      #{:a $1 :b $2}	; same as (fn [a b] {:a	a :b b})
	      #$		; same as (fn [x] x) (aka the identity function)
	      #val		; same as (fn [] val)
	      #[:one :two $...]	; same as (fn [...] ["one" "two" ...])

       Hash arguments can also be used as parts	of multisyms.	For  instance,
       #$.foo  is  a  function which will return the value of the "foo"	key in
       its first argument.

       Unlike regular functions, there is no implicit do in a  hash  function,
       and  thus it cannot contain multiple forms without an explicit do.  The
       body itself is directly used as the return value	rather than  the  last
       element in the body.

   partial partial application
       Returns	a  new function	which works like its first argument, but fills
       the first few arguments in place	with the given ones.  This is  related
       to  currying  but different because calling it will call	the underlying
       function	instead	of waiting till	it has the "correct" number of args.

       Example:

	      (fn add-print [x y] (print (+ x y)))
	      (partial add-print 2)

       This example returns a function which will print	a  number  that	 is  2
       greater than the	argument it is passed.

BINDING
   let scoped locals
       Introduces a new	scope in which a given set of local bindings are used.

       Example:

	      (let [x 89
		    y 198]
		(print (+ x y 12))) ; => 299

       These  locals cannot be changed with set	but they can be	shadowed by an
       inner let or local.  Outside the	body of	the let, the bindings  it  in-
       troduces	 are  no longer	visible.  The last form	in the body is used as
       the return value.

       Any time	you bind a local, you can destructure it if the	value is a ta-
       ble:

       Example:

	      (let [[a b c] [1 2 3]]
		(+ a b c)) ; =>	6

       (Since 1.5.0): If the left-hand side and	the right-hand side  are  both
       table literals, the actual table	allocation will	be optimized away, and
       a will be bound directly	to 1 without any allocation.

       If  a table key is a string with	the same name as the local you want to
       bind to,	you can	use shorthand of just :	for the	key name  followed  by
       the  local name.	 This works for	both creating tables and destructuring
       them.

       Example:

	      (let [{:msg message : val} {:msg "hello there" :val 19}]
		(print message)
		val) ; prints "hello there" and	returns	19

       When destructuring a sequential table, you can capture all the  remain-
       der of the table	in a local by using &:

       Example:

	      (let [[a b & c] [1 2 3 4 5 6]]
		(table.concat c	",")) ;	=> "3,4,5,6"

       (Since 1.3.0): This also	works with function argument lists, but	it has
       a  small	 performance  cost,  so	it's recommended to use	... instead in
       cases that are sensitive	to overhead.

       When destructuring a non-sequential table, you can capture the original
       table along with	the destructuring by using &as:

       Example:

	      (let [{:a	a :b b &as all}	{:a 1 :b 2 :c 3	:d 4}]
		(+ a b all.c all.d)) ; => 10

       For backwards-compatibility, you	can also  bind	multiple  values  with
       parentheses  in	any  context that supports destructuring.  This	is not
       necessary in current versions of	 Fennel,  but  older  versions	before
       1.5.0  did  not	optimize away tables in	destructuring, so this was re-
       quired for efficient binding.

       Example:

	      (let [(x y z) (table.unpack [10 9	8])]
		(+ x y z)) ; =>	27

   local declare local
       Introduces a new	local inside an	existing scope.	 Similar  to  let  but
       without	a  body	 argument.   Recommended for use at the	top-level of a
       file for	locals which will be used throughout the file.

       Example:

	      (local tau-approx	6.28318)

       Supports	destructuring.

   case	pattern	matching
       (Since 1.3.0)

       Evaluates its first argument, then searches thru	 the  subsequent  pat-
       tern/body  clauses to find one where the	pattern	matches	the value, and
       evaluates the corresponding body.  Pattern matching can be  thought  of
       as a combination	of destructuring and conditionals.

       Note: Lua also has "patterns" which are matched against strings similar
       to  how regular expressions work	in other languages; these are two dis-
       tinct concepts with similar names.

       Example:

	      (case mytable
		59	:will-never-match-hopefully
		[9 q 5]	(print :q q)
		[1 a b]	(+ a b))

       In the example above, we	have a mytable value followed  by  three  pat-
       tern/body clauses.

       The first clause	will only match	if mytable is 59.

       The  second clause will match if	mytable	is a table with	9 as its first
       element,	any non-nil value as its second	value and 5 as its third  ele-
       ment; if	it matches, then it evaluates (print :q	q) with	q bound	to the
       second element of mytable.

       The  final clause will only match if mytable has	1 as its first element
       and two non-nil values after it;	if so then it will add up  the	second
       and third elements.

       If no clause matches, the form evaluates	to nil.

       Patterns	 can  be  tables, literal values, or symbols.  Any symbol that
       doesn't start with _ or ? is implicitly checked to be not nil.  Symbols
       can be repeated in an expression	to check for the same value.

       Example:

	      (case mytable
		;; the first and second	values of mytable are the same non-nil value
		[a a] (* a 2)
		;; the first and second	values are not nil and are not the same	value
		[a b] (+ a b))

       It's important to note that expressions are checked in  order!  In  the
       above  example,	since [a a] is checked first, we can be	confident that
       when [a b] is checked, the two values must be different.	 Had the order
       been reversed, [a b] would always match as long as they're  not	nil  -
       even if they have the same value!

       You may allow a symbol to optionally be nil by prefixing	it with	?.

       Example:

	      (case mytable
		;; not-nil, maybe-nil
		[a ?b] :maybe-one-maybe-two-values
		;; maybe-nil ==	maybe-nil, both	are nil	or both	are the	same value
		[?a ?a]	:maybe-none-maybe-two-same-values
		;; maybe-nil, maybe-nil
		[?a ?b]	:maybe-none-maybe-one-maybe-two-values)

       Symbols	prefixed  by  an  _ are	ignored	and may	stand in as positional
       placeholders or markers for "any" value - including  a  nil  value.   A
       single  _  is also often	used at	the end	of a case expression to	define
       an "else" style fall-through value to indicate that local needs	to  be
       non-nil but its value is	not used other than that.

       Example:

	      (case mytable
		;; not-nil, anything
		[a _b] :maybe-one-maybe-two-values
		;; anything, anything (different to the	previous ?a example!)
		;; note	this is	effectively the	same as	[]
		[_a _a]	:maybe-none-maybe-one-maybe-two-values
		;; anything, anything
		;; this	is identical to	[_a _a]	and in this example would never	actually match.
		[_a _b]	:maybe-none-maybe-one-maybe-two-values
		;; when	no other clause	matched, in this case any non-table value
		_ :no-match)

       Tables  can  be nested, and they	may be either sequential ([] style) or
       key/value ({} style) tables.  Sequential	tables will match if they have
       at least	as many	elements as the	pattern.  (To allow an element	to  be
       nil,  see  ?  and  _ as above.)	Tables will never fail to match	due to
       having too many elements	- this means []	matches	any table, not just an
       empty table.  You can use & to capture all the remaining	elements of  a
       sequential table, just like let.

	      (case mytable
		{:subtable [a b	?c] :depth depth} (* b depth)
		_ :unknown)

       You  can	 also  match against multiple return values using parentheses.
       (These cannot be	nested,	but they can contain  tables.)	 This  can  be
       useful for error	checking.

	      (case (io.open "/some/file")
		(nil msg) (report-error	msg)
		f (read-file f))

   Guard Clauses
       Sometimes  you need to match on something more general than a structure
       or specific value.  In these cases you can use guard clauses:

	      (case [91	12 53]
		(where [a b c] (= 5 a))	:will-not-match
		(where [a b c] (= 0 (math.fmod (+ a b c) 2)) (=	91 a)) c) ; -> 53

       In this case the	pattern	should be wrapped in parentheses but the first
       thing in	the parentheses	is the where symbol.  Each form	after the pat-
       tern is a condition; all	the conditions must evaluate to	true for  that
       pattern to match.

       If  several  patterns share the same body and guards, such patterns can
       be combined with	or special in the where	clause:

	      (case [5 1 2]
		(where (or [a 3	9] [a 1	2]) (= 5 a)) "Either [5	3 9] or	[5 1 2]"
		_ "anything else")

       This is essentially equivalent to:

	      (case [5 1 2]
		(where [a 3 9] (= 5 a))	"Either	[5 3 9]	or [5 1	2]"
		(where [a 1 2] (= 5 a))	"Either	[5 3 9]	or [5 1	2]"
		_ "anything else")

       However,	patterns which bind variables should not be combined  with  or
       if  different  variables	 are bound in different	patterns or some vari-
       ables are missing:

	      ;; bad
	      (case [1 2 3]
		;; Will	fail to	compile	because	`b' is not present in the first
		;; pattern but the guard still uses it.
		(where (or [a 1	2] [a b	3]) (< a 0) (< b 1))
		:body)

	      ;; ok
	      (case [1 2 3]
		(where (or [a b	2] [a b	3]) (< a 0) (<=	b 1))
		:body)

   Binding Pinning
       Symbols bound inside a case pattern are independent from	 any  existing
       symbols	in  the	 current scope,	that is	- names	may be re-used without
       consequence.

       Example:

	      (let [x 1]
		(case [:hello]
		  ;; `x` is simply bound to the	first value of [:hello]
		  [x] x)) ; -> :hello

       Sometimes it may	be desirable to	match against an existing value	in the
       outer scope.  To	do this	we can "pin" a binding inside the pattern with
       an existing outer binding with the unary	(=  binding-name)  form.   The
       unary (=	binding-name) form is only valid in a case pattern and must be
       inside a	(where)	guard.

       Example:

	      (let [x 1]
		(case [:hello]
		  ;; 1 != :hello
		  (where [(= x)]) x
		  _ :no-match))	; -> no-match

	      (let [x 1]
		(case [1]
		  ;; 1 == 1
		  (where [(= x)]) x
		  _ :no-match))	; -> 1

	      (let [pass :hunter2]
		(case (user-input)
		  (where (= pass)) :login
		  _ :try-again!))

       Pinning	is only	required inside	the pattern.  Outer bindings are auto-
       matically available inside guards and bodies as long as	the  name  has
       not been	rebound	in the pattern.

       Note: The case macro can	be used	in place of the	if-let macro from Clo-
       jure.   The reason Fennel doesn't have if-let is	that case makes	it re-
       dundant.

   match pattern matching
       match is	conceptually equivalent	to case, except	symbols	 in  the  pat-
       terns are always	pinned with outer-scope	symbols	if they	exist.

       It supports all the same	syntax as described in case except the pin ((=
       binding-name)) expression, as it	is always performed.

	      Be  careful  when	using match that your symbols are not acciden-
	      tally the	same as	any existing symbols!  If you know  you	 don't
	      intend  to  pin any existing symbols you should use the case ex-
	      pression.

	      (let [x 95]
	       (match [52 85 95]
		 [b a a] :no ; because a=85 and	a=95
		 [x y z] :no ; because x=95 and	x=52
		 [a b x] :yes))	; a and	b are fresh values while x=95 and x=95

       Unlike in case, if an existing binding has the value nil, the ?	prefix
       is not necessary	- it would instead create a new	un-pinned binding!

       Example:

	      (let [name nil
		    get-input (fn [] "Dave")]
		(match (get-input)
		  ;; name already exists as nil, "Dave"	!= nil so this *wont* match
		  name (.. "Hello " name)
		  ?no-input (..	"Hello anonymous"))) ; -> "Hello anonymous"

       Note:  Prior  to	 Fennel	0.9.0 the match	macro used infix ? operator to
       test patterns against the guards.  While	 this  syntax  is  still  sup-
       ported, where should be preferred instead:

	      (match [1	2 3]
		(where [a 2 3] (< 0 a))	"new guard syntax"
		([a 2 3] ? (< 0	a)) "obsolete guard syntax")

   case-try for	matching multiple steps
       Evaluates a series of pattern matching steps.  The value	from the first
       expression  is  matched	against	the first pattern.  If it matches, the
       first body is evaluated and its value is	 matched  against  the	second
       pattern,	etc.

       If  there  is  a	(catch pat1 body1 pat2 body2 ...) form at the end, any
       mismatch	from the steps will be tried against  these  patterns  in  se-
       quence  as  a  fallback	just  like a normal case.  If no catch pattern
       matches,	nil is returned.

       If there	is no catch, the mismatched value  will	 be  returned  as  the
       value of	the entire expression.

	      (fn handle [conn token]
		(case-try (conn:receive	:*l)
		  input	(parse input)
		  (where (command-name params (= token))) (commands.get	command-name)
		  command (pcall command (table.unpack params))
		  (catch
		   (_ :timeout)	nil
		   (_ :closed) (pcall disconnect conn "connection closed")
		   (_ msg) (print "Error handling input" msg))))

       This is useful when you want to perform a series	of steps, any of which
       could  fail.  The catch clause lets you keep all	your error handling in
       one place.  Note	that there are two ways	to indicate failure in	Fennel
       and  Lua: using the assert/error	functions or returning nil followed by
       some data representing the failure.  This form only works on  the  lat-
       ter, but	you can	use pcall to transform error calls into	values.

   match-try for matching multiple steps
       Equivalent  to  case-try	but uses match internally.  See	case and match
       for details on the differences between these two	forms.

       Unlike case-try,	match-try will pin values in a given catch block  with
       those in	the original steps.

	      (fn handle [conn token]
		(match-try (conn:receive :*l)
		  input	(parse input)
		  (command-name	params token) (commands.get command-name)
		  command (pcall command (table.unpack params))
		  (catch
		    (_ :timeout) nil
		    (_ :closed)	(pcall disconnect conn "connection closed")
		    (_ msg) (print "Error handling input" msg))))

   var declare local variable
       Introduces  a  new  local  inside  an existing scope which may have its
       value changed.  Identical to local apart	from allowing set to  work  on
       it.

       Example:

	      (var x 83)

       Supports	destructuring.

   set set local variable or table field
       Changes	the value of a variable	introduced with	var.  Will not work on
       globals or let/local-bound locals.  Can also be used to change a	 field
       of a table, even	if the table is	bound with let or local.  If the table
       field  name is static, use tbl.field; if	the field name is dynamic, use
       (. tbl field).

       Examples:

	      (set x (+	x 91)) ; var

	      (let [t {:a 4 :b 8}] ; static table field
		(set t.a 2) t) ; => {:a	2 :b 8}

	      (let [t {:supported-chars	{:x true}}
		    field1 :supported-chars
		    field2 :y] ; dynamic table field
		(set (.	t field1 field2) true) t) ; => {:supported-chars {:x true :y true}}

       This supports destructuring too.

   tset	set table field
       Sets the	field of a given table to a new	value.

       Example:

	      (let [tbl	{:d 32}	field :d]
		(tset tbl field	19) tbl) ; => {:d 19}

       You can provide multiple	successive field names to perform nested sets.
       For example:

	      (let [tbl	{:a {:b	{}}} field :c]
		(tset tbl :a :b	field "d") tbl)	; => {:a {:b {:c "d"}}}

       Since 1.5.0, tset is mostly redundant because set can be	used for table
       fields.	The main exception is that tset	works with doto	and  set  does
       not.

   with-open bind and auto-close file handles
       While  Lua  will	 automatically	close  an  open	 file handle when it's
       garbage collected, GC may not run right away; with-open ensures handles
       are closed immediately, error or	no, without boilerplate.

       The usage is similar to let, except:

        destructuring is disallowed (symbols only on the left-hand side)

        every binding should be a file	handle or other	value  with  a	:close
	 method.

       After executing the body, or upon encountering an error,	with-open will
       invoke  (value:close)  on every bound variable before returning the re-
       sults.

       Normally	the body is implicitly wrapped in a function and run with  xp-
       call  so	 that all bound	handles	are closed before it re-raises the er-
       ror.  However you can use --to-be-closed	to  make  it  use  the	native
       functionality  in  Lua 5.4+ which will do the same thing	without	xpcall
       interfering with	stack traces.

       Example:

	      ;; Basic usage
	      (with-open [fout (io.open	:output.txt :w)	fin (io.open :input.txt)]
		(fout:write "Here is some text!\n")
		((fin:lines))) ; => first line of input.txt

	      ;; This demonstrates that	the file will also be closed upon error.
	      (var fh nil)
	      (local [ok err]
		[(pcall	#(with-open [file (io.open :test.txt :w)]
			   (set	fh file) ; you would normally never do this
			   (error :whoops!)))])
	      (io.type fh) ; =>	"closed	file"
	      [ok err]	   ; =>	[false "<error message and stacktrace>"]

   pick-values emit exactly n values
       Discards	all values after the first n when  dealing  with  multi-values
       (...) and multiple returns.  Useful for composing functions that	return
       multiple	 values	 with variadic functions.  Expands to a	let expression
       that binds and re-emits exactly n values, e.g.

	      (pick-values 2 (func))

       expands to

	      (let [[_0_ _1_] [(func)]]	(values	_0_ _1_))

       Example:

	      (pick-values 0 :a	:b :c :d :e) ; => nil
	      [(pick-values 2 (table.unpack [:a	:b :c]))] ;-> ["a" "b"]

	      (fn add [x y ...]
		(let [sum (+ (or x 0) (or y 0))]
		  (if (= ... nil)
		    sum
		    (add sum ...))))

	      (add (pick-values	2 10 10	10 10))	; => 20
	      (->> [1 2	3 4 5] (table.unpack) (pick-values 3) (add)) ; => 6

       Note: If	n is greater than the number of	values supplied, n values will
       still be	emitted.  This is reflected when using	(select	 "#"  ...)  to
       count varargs, but tables [...] ignore trailing nils:

	      (select :# (pick-values 5	"one" "two")) ;	=> 5
	      [(pick-values 5 "one" "two")]	      ;	=> ["one" "two"]

FLOW CONTROL
   if conditional
       Checks  a  condition  and  evaluates a corresponding body.  Accepts any
       number of condition/body	pairs; if an odd number	of arguments is	given,
       the last	value is treated as a catch-all	"else".	 Similar  to  cond  in
       other lisps.

       Example:

	      (let [x (math.random 64)]
		(if (= 0 (% x 10))
		    "multiple of ten"
		    (= 0 (% x 2))
		    "even"
		    "I dunno, something	else"))

       All values other	than nil or false are treated as true.

   when	single side-effecting conditional
       Takes  a	 single	condition and evaluates	the rest as a body if it's not
       nil or false.  This is intended for side-effects.  The last form	in the
       body is used as the return value.

       Example:

	      (when launch-missiles?
		(power-on)
		(open-doors)
		(fire))

   each	general	iteration
       Runs the	body once for each value provided by the  iterator.   Commonly
       used with ipairs	(for sequential	tables)	or pairs (for any table	in un-
       defined order) but can be used with any iterator.  Returns nil.

       Example:

	      (each [key value (pairs mytbl)]
		(print "executing key")
		(print (f value)))

       Any loop	can be terminated early	by placing an &until clause at the end
       of the bindings:

	      (local out [])
	      (each [_ value (pairs tbl) &until	(< max-len (length out))]
		(table.insert out value))

       Note: prior to fennel version 1.2.0, :until was used instead of &until;
       the old syntax is still supported for backwards compatibility.

       Most  iterators	return two values, but each will bind any number.  See
       Programming in Lua <https://www.lua.org/pil/7.1.html> for details about
       how iterators work.

   for numeric loop
       Counts a	number from a start to stop point (inclusive), evaluating  the
       body once for each value.  Accepts an optional step.  Returns nil.

       Example:

	      (for [i 1	10 2]
		(log-number i)
		(print i))

       This example will print all odd numbers under ten.

       Like  each, loops using for can also be terminated early	with an	&until
       clause.	The clause is checked before each iteration of the body; if it
       is true at the beginning	then the body will not run at all.

	      (var x 0)
	      (for [i 1	128 &until (maxed-out? x)]
		(set x (+ x i)))

   while good old while	loop
       Loops over a body until a condition is met.  Uses a  native  Lua	 while
       loop.  Returns nil.

       Example:

	      (var done? false)
	      (while (not done?)
		(print :not-done)
		(when (< 0.95 (math.random))
		  (set done? true)))

   do evaluate multiple	forms returning	last value
       Accepts any number of forms and evaluates all of	them in	order, return-
       ing  the	 last  value.	This is	used for inserting side-effects	into a
       form which accepts only a single	value, such as in a body of an if when
       multiple	clauses	make it	so you can't use when.	Some lisps  call  this
       begin or	progn.

	      (if launch-missiles?
		  (do
		    (power-on)
		    (open-doors)
		    (fire))
		  false-alarm?
		  (promote lt-petrov))

       Some other forms	like fn	and let	have an	implicit do.

DATA
   operators
        and, or, not: boolean

        +, -, *, /, //, %, ^: arithmetic

        >, <, >=, <=, =, not=:	comparison

        lshift, rshift, band, bor, bxor, bnot:	bitwise	operations

       These  all  work	 as you	would expect, with a few caveats.  The bitwise
       operators  are  only  available	in  Lua	 5.3+,	unless	you  use   the
       --use-bit-lib  flag  or	the useBitLib flag in the options table, which
       lets them be used in LuaJIT.  The integer  division  operator  (//)  is
       only available in Lua 5.3+.

       They  all take any number of arguments, as long as that number is fixed
       at compile-time.	 For instance, (= 2 2 (table.unpack [2 5])) will eval-
       uate to true because the	compile-time number of values  being  compared
       is 3.  Multiple values at runtime will not be taken into	account.

       Note  that  these  are  all  special  forms  which  cannot  be  used as
       higher-order functions.

   .. string concatenation
       Concatenates its	arguments into one string.  Will coerce	 numbers  into
       strings,	but not	other types.

       Example:

	      (.. "Hello" " " "world" 7	"!!!") ; => "Hello world7!!!"

       String  concatenation  is subject to the	same compile-time limit	as the
       operators above;	it is not aware	of multiple values at runtime.

   length string or table length
       Returns the length of a string or table.	 Note that the length of a ta-
       ble with	gaps (nils) in it is undefined;	it can return a	number	corre-
       sponding	 to  any  of  the table's "boundary" positions between nil and
       non-nil values.	If a table has nils and	you want to know the last con-
       secutive	numeric	index starting at 1, you must  calculate  it  yourself
       with  ipairs;  if  you  want to know the	maximum	numeric	key in a table
       with nils, you can use table.maxn on Lua	<= 5.2.

       Example:

	      (+ (length [1 2 3	nil 8])	(length	"abc"))	; => 6 or 8

   . table lookup
       Looks up	a given	key in	a  table.   Multiple  arguments	 will  perform
       nested lookup.

       Example:

	      (. mytbl myfield)

       Example:

	      (let [t {:a [2 3 4]}] (. t :a 2))	; => 3

       Note  that  if  the  field  name	is a string known at compile time, you
       don't need this and can just use	mytbl.field.

   Nil-safe ?. table lookup
       Looks up	a given	key in	a  table.   Multiple  arguments	 will  perform
       nested  lookup.	 If  any  of  subsequent  keys	is  not	 present, will
       short-circuit to	nil.

       Example:

	      (?. mytbl	myfield)

       Example:

	      (let [t {:a [2 3 4]}] (?.	t :a 4 :b)) ; => nil
	      (let [t {:a [2 3 4 {:b 42}]}] (?.	t :a 4 :b)) ; => 42

   icollect, collect table comprehension macros
       The icollect macro takes	a "iterator binding table" in the format  that
       each  takes,  and  returns a sequential table containing	all the	values
       produced	by each	iteration of the macro's body.	This is	similar	to how
       map works in several other languages, but it is a macro,	 not  a	 func-
       tion.

       If  the	value  is  nil,	 it is omitted from the	return table.  This is
       analogous to filter in other languages.

	      (icollect	[_ v (ipairs [1	2 3 4 5	6])]
		(if (< 2 v) (* v v)))
	      ;; -> [9 16 25 36]

	      ;; equivalent to:
	      (let [tbl	[]]
		(each [_ v (ipairs [1 2	3 4 5 6])]
		  (tset	tbl (+ (length tbl) 1) (if (< 2	v) (* v	v))))
		tbl)

       The collect macro is almost identical, except that the body should  re-
       turn two	things:	a key and a value.

	      (collect [k v (pairs {:apple "red" :orange "orange" :lemon "yellow"})]
		(if (not= v "yellow")
		    (values (..	"color-" v) k)))
	      ;; -> {:color-orange "orange" :color-red "apple"}

	      ;; equivalent to:
	      (let [tbl	{}]
		(each [k v (pairs {:apple "red"	:orange	"orange"})]
		  (if (not= v "yellow")
		    (match [(..	"color-" v) k]
		      [key value] (tset	tbl key	value))))
		tbl)

       If  the key and value are given directly	in the body of collect and not
       nested in an outer form,	then the values	can be omitted for brevity:

	      (collect [k v (pairs {:a 85 :b 52	:c 621 :d 44})]
		k (* v 5))

       Like each and for, the table comprehensions support  an	&until	clause
       for early termination.

       Both  icollect  and  collect  take an &into clause which	allows you put
       your results into an existing table instead of starting with  an	 empty
       one:

	      (icollect	[_ x (ipairs [2	3]) &into [9]]
		(* x 11))
	      ;; -> [9 22 33]

       Note:  Prior  to	fennel version 1.2.0, :into was	used instead of	&into;
       the old syntax is still supported for backwards compatibility.

   accumulate iterator accumulation
       Runs through an iterator	and performs accumulation, similar to fold and
       reduce commonly used in functional programming languages.  Like collect
       and icollect, it	takes an iterator binding table	and an	expression  as
       its  arguments.	 The  difference  is that in accumulate, the first two
       items in	the binding table are used as an  "accumulator"	 variable  and
       its initial value.  For each iteration step, it evaluates the given ex-
       pression	 and its value becomes the next	accumulator variable.  accumu-
       late returns the	final value of the accumulator variable.

       Example:

	      (accumulate [sum 0
			   i n (ipairs [10 20 30 40])]
		  (+ sum n)) ; -> 100

       The &until clause is also supported here	for early termination.

   faccumulate range accumulation
       (Since 1.3.0)

       Identical to accumulate,	but instead of taking an iterator and the same
       bindings	as each, it accepts the	same bindings as for and will  iterate
       the numerical range.  Accepts &until just like for and accumulate.

       Example:

	      (faccumulate [n 0	i 1 5] (+ n i))	; => 15

   fcollect range comprehension	macro
       (Since 1.1.1)

       Similarly to icollect, fcollect provides	a way of building a sequential
       table.	Unlike	icollect, instead of an	iterator it traverses a	range,
       as accepted by the for special.	The &into and &until clauses work  the
       same as in icollect.

       Example:

	      (fcollect	[i 0 10	2]
		(if (> i 2) (* i i)))
	      ;; -> [16	36 64 100]

	      ;; equivalent to:
	      (let [tbl	{}]
		(for [i	0 10 2]
		  (if (> i 2)
		      (table.insert tbl	(* i i))))
		tbl)

   values multi-valued return
       Returns	multiple values	from a function.  Usually used to signal fail-
       ure by returning	nil followed by	a message.

       Example:

	      (fn [filename]
		(if (valid-file-name? filename)
		    (open-file filename)
		    (values nil	(.. "Invalid filename: " filename))))

OTHER
   : method call
       Looks up	a function in a	table and calls	it with	the table as its first
       argument.  This is a common idiom in  many  Lua	APIs,  including  some
       built-in	ones.

       Just like Lua, you can perform a	method call by calling a function name
       where : separates the table variable and	method name.

       Example:

	      (let [f (assert (io.open "hello" "w"))]
		(f:write "world")
		(f:close))

       In the example above, f:write is	a single multisym.  If the name	of the
       method  or  the table containing	it isn't fixed,	you can	use : followed
       by the table and	then the method's name to allow	it  to	be  a  dynamic
       string instead:

       Example:

	      (let [f (assert (io.open "hello" "w"))
		    method1 :write
		    method2 :close]
		(: f method1 "world")
		(: f method2))

       Both of these examples are equivalent to	the following:

	      (let [f (assert (io.open "hello" "w"))]
		(f.write f "world")
		(f.close f))

       Unlike  Lua,  there's nothing special about defining functions that get
       called this way;	typically it is	given an extra	argument  called  self
       but this	is just	a convention; you can name it anything.

	      (local t {})

	      (fn t.enable [self]
		(set self.enabled? true))

	      (t:enable)

   ->, ->>, -?>	and -?>> threading macros
       The  -> macro takes its first value and splices it into the second form
       as the first argument.  The result of evaluating	the second  form  gets
       spliced into the	first argument of the third form, and so on.

       Example:

	      (-> 52
		  (+ 91	2) ; (+	52 91 2)
		  (- 8)	   ; (-	(+ 52 91 2) 8)
		  (print "is the answer")) ; (print (- (+ 52 91	2) 8) "is the answer")

       The  ->>	macro works the	same, except it	splices	it into	the last posi-
       tion of each form instead of the	first.

       -?> and -?>>, the thread	maybe macros, are similar to ->	& ->> but they
       also do checking	after the evaluation of	each threaded  form.   If  the
       result  is  false or nil	then the threading stops and the result	is re-
       turned.	-?> splices the	threaded value as the first argument, like ->,
       and -?>>	splices	it into	the last position, like	->>.

       This example shows how to use them to avoid accidentally	indexing a nil
       value:

	      (-?> {:a {:b {:c 42}}}
		   (. :a)
		   (. :missing)
		   (. :c)) ; ->	nil
	      (-?>> :a
		    (. {:a :b})
		    (. {:b :missing})
		    (. {:c 42})) ; -> nil

       While ->	and ->>	pass multiple values thru  without  any	 trouble,  the
       checks  in  -?>	and -?>> prevent the same from happening there without
       performance overhead, so	these pipelines	are limited to a single	value.

	      Note that	these have nothing to do with "threads"	used for  con-
	      currency;	 they  are  named  after  the  thread which is used in
	      sewing.  This is similar to the way that |> works	in  OCaml  and
	      Elixir.

   doto
       Similarly,  the	doto  macro  splices  the  first value into subsequent
       forms.  However,	it keeps the same value	and  continually  splices  the
       same  thing  in	rather than using the value from the previous form for
       the next	form.

	      (doto (io.open "/tmp/err.log")
		(: :write contents)
		(: :close))

	      ;; equivalent to:
	      (let [x (io.open "/tmp/err.log")]
		(: x :write contents)
		(: x :close)
		x)

       The first form becomes the return value for the whole  expression,  and
       subsequent forms	are evaluated solely for side-effects.

   tail!
       Tail  calls  will  be optimized automatically.  However,	the tail! form
       asserts that its	argument is called in a	tail position.	 You  can  use
       this  when  the code depends on tail call optimization; that way	if the
       code is changed so that the recursive call is no	longer in the tail po-
       sition, it will cause a compile error instead of	overflowing the	 stack
       later on	large data sets.

	      (fn process-all [data i]
		(case (process (. data i))
		  :done	(print "Process	completed.")
		  :next	(process-all data (+ i 1))
		  :skip	(do (tail! (process-all	data (+	i 2)))
	      ;;	     ^^^^^ Compile error: Must be in tail position
			    (print "Skipped" (+	i 1)))))

   include
	      (include :my.embedded.module)

       Loads  Fennel/Lua module	code at	compile	time and embeds	it in the com-
       piled output.  The module name must resolve to a	string literal	during
       compilation.  The bundled code will be wrapped in a function invocation
       in the emitted Lua and set on package.preload[modulename]; a normal re-
       quire  is then emitted where include was	used to	load it	on demand as a
       normal module.

       In most cases it's better to use	require	in your	code and use  the  re-
       quireAsInclude option in	the API	documentation and the --require-as-in-
       clude CLI flag (fennel --help) to accomplish this.

       The  require  function  is not part of Fennel; it comes from Lua.  How-
       ever, it	works to load Fennel code.  See	the Modules and	multiple files
       section	  in	the    tutorial	    and	    Programming	    in	   Lua
       <https://www.lua.org/pil/8.1.html> for details about require.

       The  include  macro and hence --require-as-include support semi-dynamic
       compile-time resolution of module  paths	 similarly  to	import-macros.
       See the relative	require	section	in the tutorial	for more information.

   assert-repl
       (Since 1.4.0)

       Sometimes it's helpful for debugging purposes to	drop a repl right into
       the middle of your code to see what's really going on.  You can use the
       assert-repl macro to do this:

	      (let [input (get-input)
		    value []]
		(fn helper [x]
		  (table.insert	value (calculate x)))
		(assert-repl (transform	helper value) "could not transform"))

       This  works  as a drop-in replacement for the built-in assert function,
       but when	the condition is false or nil, instead of an error,  it	 drops
       into  a	repl which has access to all the locals	that are in scope (in-
       put, value, and helper in the example above).

       Note that this is meant for use in development and will not  work  with
       ahead-of-time  compilation  unless your build also includes Fennel as a
       library.

       If you use the --assert-as-repl flag when running Fennel, calls to  as-
       sert will be replaced with assert-repl automatically.

       Note:  In  Fennel 1.4.0,	assert-repl accepted an	options	table for fen-
       nel.repl	as an optional third argument.	This was removed as a  bug  in
       1.4.1, as it broke compatibility	with assert.

       The  REPL  spawned  by  assert-repl applies the same default options as
       fennel.repl, which as of	Fennel 1.4.1 can be configured from  the  API.
       See the Fennel API reference for	details.

   Recovering from failed assertions
       You  can	,return	EXPRESSION from	the repl to replace the	original fail-
       ing condition with a different arbitrary	value.	Returning false	or nil
       will trigger a regular assert failure.

       Note: Currently,	only a single value can	be returned from the REPL this
       way.  While ,return can be used to make a failed	assertion recover,  if
       the  calling  code  expects  multiple return values, it may cause unex-
       pected behavior.

MACROS
       All forms which introduce macros	do so inside the current scope.	  This
       is usually the top level	for a given file, but you can introduce	macros
       into  nested  scopes as well.  Note that	macros are a compile-time con-
       struct; they do not exist at runtime.  As such  macros  cannot  be  ex-
       ported at the bottom of a module	like functions and other values.

   import-macros load macros from a separate module
       Loads a module at compile-time and binds	its functions as local macros.

       A macro module exports any number of functions which take code forms as
       arguments  at  compile  time and	emit lists which are fed back into the
       compiler	as code.  Macro	modules	are searched for in  filenames	ending
       in .fnl or .fnlm.  The module calling import-macros gets	whatever func-
       tions  have  been  exported  to use as macros.  For instance, here is a
       macro module which implements when2 in terms of if and do:

	      (fn when2	[condition body1 & rest-body]
		(assert	body1 "expected	body")
		`(if ,condition
		   (do ,body1 ,(unpack rest-body))))

	      {:when2 when2}

       For a full explanation of how this works	 see  the  macro  guide.   All
       forms in	Fennel are normal tables you can use table.insert, ipairs, de-
       structuring, etc	on.  The backtick on the third line creates a template
       list  for  the  code emitted by the macro, and the comma	serves as "un-
       quote" which splices values into	the template.

       Assuming	the code above is in the file "my-macros.fnl"  then  it	 turns
       this input:

	      (import-macros {:	when2} :my-macros)

	      (when2 (=	3 (+ 2 a))
		(print "yes")
		(finish-calculation))

       and  transforms it into this code at compile time by splicing the argu-
       ments into the backtick template:

	      (if (= 3 (+ 2 a))
		(do
		  (print "yes")
		  (finish-calculation)))

       The import-macros macro can  take  any  number  of  binding/module-name
       pairs.	It  can	 also  bind  the  entire macro module to a single name
       rather than destructuring it.  In this case you can use a dot  to  call
       the individual macros inside the	module:

	      (import-macros mine :my-macros)

	      (mine.when2 (= 3 (+ 2 a))
		(print "yes")
		(finish-calculation))

       Note  that  all	macro  code runs at compile time, which	happens	before
       runtime.	 Locals	which are in scope at runtime are not  visible	during
       compile-time.  So this code will	not work:

	      (local (module-name file-name) ...)
	      (import-macros mymacros (.. module-name ".macros"))

       However,	this code will work, provided the module in question exists:

	      (import-macros mymacros (.. ... ".macros"))

       See "Compiler API" below	for details about additional functions visible
       inside compiler scope which macros run in.

   Macro module	searching
       By  default,  Fennel  will search for macro modules similarly to	how it
       searches	for normal runtime modules: by walking thru  entries  on  fen-
       nel.macro-path  and  checking  the filesystem for matches.  However, in
       some cases this might not be suitable, for instance if your Fennel pro-
       gram is packaged	in some	kind of	archive	file and the  modules  do  not
       exist as	distinct files on disk.

       To support this case you	can add	your own searcher function to the fen-
       nel.macro-searchers  table.  For	example, assuming find-in-archive is a
       function	which can look up strings from the archive given a path:

	      (local fennel (require :fennel))

	      (fn my-searcher [module-name]
		(let [filename (.. "src/" module-name ".fnl")]
		  (match (find-in-archive filename)
		    code (values (partial fennel.eval code {:env :_COMPILER})
				 filename))))

	      (table.insert fennel.macro-searchers my-searcher)

       The searcher function should take a module name as a string and	return
       two  values  if	it  can	find the macro module: a loader	function which
       will return the macro table when	called,	and an optional	filename.  The
       loader function will receive the	module name and	the filename as	 argu-
       ments.

   macros define several macros
       Defines a table of macros.  Note	that inside the	macro definitions, you
       cannot  access  variables  and bindings from the	surrounding code.  The
       macros are essentially compiled	in  their  own	compiler  environment.
       Again,  see the "Compiler API" section for more details about the func-
       tions available here.

	      (macros {:my-max (fn [x y]
				 `(let [x# ,x y# ,y]
				    (if	(< x# y#) y# x#)))})

	      (print (my-max 10	20))
	      (print (my-max 20	10))
	      (print (my-max 20	20))

   macro define	a single macro
	      (macro my-max [x y]
		`(let [x# ,x y#	,y]
		   (if (< x# y#) y# x#)))

       If you are only defining	a single macro,	this is	equivalent to the pre-
       vious example.  The syntax mimics fn.

   macrodebug print the	expansion of a macro
	      (macrodebug (-> abc
			      (+ 99)
			      (< 0)
			      (when (os.exit))))
	      ;	-> (if (< (+ abc 99) 0)	(do (os.exit)))

       Call the	macrodebug macro with a	form and  it  will  repeatedly	expand
       top-level  macros  in that form and print out the resulting form.  Note
       that the	resulting form will usually not	be sensibly indented,  so  you
       might need to copy it and reformat it into something more readable.

       Note that this prints at	compile-time since macrodebug is a macro.

   Macro gotchas
       It's  easy  to  make macros which accidentally evaluate their arguments
       more than once.	This is	fine if	they are passed	literal	values,	but if
       they are	passed a form which has	side-effects, the result will be unex-
       pected:

	      (var v 1)
	      (macros {:my-max (fn [x y]
				 `(if (< ,x ,y)	,y ,x))})

	      (fn f [] (set v (+ v 1)) v)

	      (print (my-max (f) 2)) ; -> 3 since (f) is called	twice in the macro body	above

       In     order	to     prevent	   accidental	   symbol      capture
       <https://gist.github.com/nimaai/2f98cc421c9a51930e16#variable-capture>,
       you may not bind	a bare symbol inside a backtick	as an identifier.  Ap-
       pending	a  #  on the end of the	identifier name	as above invokes "auto
       gensym" which guarantees	the local name is unique.

	      (macros {:my-max (fn [x y]
				 `(let [x2 ,x y2 ,y]
				    (if	(< x2 y2) y2 x2)))})

	      (print (my-max 10	20))
	      ;	Compile	error in 'x2' unknown:?: macro tried to	bind x2	without	gensym;	try x2#	instead

       macros is useful	for one-off, quick macros, or even some	 more  compli-
       cated  macros,  but be careful.	It may be tempting to try and use some
       function	you have previously defined, but if you	need such  functional-
       ity, you	should probably	use import-macros.

       For  example,  this will	not compile in strict mode!  Even when it does
       allow the macro to be called, it	will fail  trying  to  call  a	global
       my-fn when the code is run:

	      (fn my-fn	[] (print "hi!"))

	      (macros {:my-max (fn [x y]
				 (my-fn)
				 `(let [x# ,x y# ,y]
				    (if	(< x# y#) y# x#)))})
	      ;	Compile	error in 'my-max': attempt to call global '__fnl_global__my_2dfn' (a nil value)

       See  the	 macro guide <https://fennel-lang.org/macros> for more details
       about writing macros.

   eval-compiler
       Evaluate	a block	of code	during compile-time with  access  to  compiler
       scope.	This  gives  you  a  superset of the features you can get with
       macros, but you should use macros if you	can.

       Example:

	      (eval-compiler
		(each [name (pairs _G)]
		  (print name)))

       This prints all the functions available in compiler scope.

   Compiler Environment
       Inside eval-compiler, macros, or	macro blocks, as well as import-macros
       modules,	the functions listed below are visible to your code.

        list -	return a list, which is	a special kind of table	used for code.

        sym - turn a string into a symbol.

        gensym	- generates a unique symbol for	use in macros, accepts an  op-
	 tional	prefix string.

        list? - is the	argument a list?  Returns the argument or false.

        sym? -	is the argument	a symbol?  Returns the argument	or false.

        table?	 -  is the argument a non-list table?  Returns the argument or
	 false.

        sequence? - is	the argument a non-list	sequential table (created with
	 [], as	opposed	to {})?	 Returns the argument or false.

        varg? - is this a ... symbol which indicates  var  args?   Returns  a
	 special table describing the type or false.

        multi-sym? - a	multi-sym is a dotted symbol which refers to a table's
	 field.	 Returns a table containing each separate symbol, or false.

        comment?  -  is  the  argument	a comment?  Comments are only included
	 when opts.comments is truthy.

        view -	fennel.view table serializer.

        assert-compile	- works	like assert but	takes  a  list/symbol  as  its
	 third argument	in order to provide pinpointed error messages.

       The  following  functions  standardize  Lua globals that	change between
       5.1-5.4.	 To limit common Lua-compatibility boilerplate such as	(local
       unpack  (or  _G.unpack  table.unpack))  from  macro code, the following
       helpers are present in the macro	environment:

        unpack	- _G.unpack in Lua 5.1/LuaJit, table.unpack in Lua >= 5.2

        pack -	Equivalent to table.pack available in Lua 5.2 and  up.	 (pack
	 :a  nil :c nil	nil) ->	{1 :a 3	:c :n 5}.  Useful for reliably storing
	 and correctly reproducing multi-values	that contain nil.

       These functions can be used from	 within	 macros	 only,	not  from  any
       eval-compiler call:

        in-scope?  - does the symbol refer to an in-scope local?  Returns the
	 symbol	or nil.

        macroexpand - performs	macroexpansion on its argument	form;  returns
	 an AST.

   Note: Compile-time List implementation
       Note  that lists	are compile-time concepts that don't exist at runtime;
       they are	implemented as tables which have a special metatable  to  dis-
       tinguish	 them  from regular tables defined with	square or curly	brack-
       ets.  Similarly symbols are tables with a string	entry for  their  name
       and a marker metatable.	You can	use tostring to	get the	name of	a sym-
       bol.

   Sandboxing
       Inside macros or	eval-compiler, by default there	are only two ways that
       code can	interact with "the outside world"; you can call	print in order
       to  debug,  and	you  can call io.open in read mode on files inside the
       current directory or its	subdirectories.	 The rest of the io table  and
       the entire os table is not accessible.

       You  can	loosen these restrictions by passing {:compiler-env _G}	in the
       options	table  when  using  the	 compiler  API	or  setting  --no-com-
       piler-sandbox on	the command line to get	full access.

       Please note that	the sandbox is not suitable to be used as a robust se-
       curity  mechanism.   It	has  not been audited and should not be	relied
       upon to protect you from	running	untrusted code.

       Note that other internals of the	compiler exposed in compiler scope but
       not listed above	are subject to change.

lua ESCAPE HATCH
       There are some cases when you need to emit Lua output  from  Fennel  in
       ways  that  don't  match	 Fennel's semantics.  For instance, if you are
       porting an algorithm from Lua that uses early returns, you may want  to
       do  the	port  as literally as possible first, and then come back to it
       later to	make it	idiomatic.  You	can use	the lua	special	form to	accom-
       plish this:

	      (fn find [tbl pred]
		(each [key val (pairs tbl)]
		  (when	(pred val)
		    (lua "return key"))))

       Lua code	inside the string can refer to locals which are	in scope; how-
       ever note that it must refer to the names after mangling	has been done,
       because the identifiers must be valid Lua.  The	Fennel	compiler  will
       change  foo-bar	to  foo_bar  in	 the  Lua output in order for it to be
       valid, as well as other transformations.	 When in  doubt,  inspect  the
       compiler	 output	 to see	what it	looks like.  For example the following
       Fennel code:

	      (local foo-bar 3)
	      (let [foo-bar :hello]
		(lua "print(foo_bar0 ..	\" world\")"))

       will produce this Lua code:

	      local foo_bar = 3
	      local foo_bar0 = "hello"
	      print(foo_bar0 ..	" world")
	      return nil

       Normally	in these cases you would want to emit a	 statement,  in	 which
       case  you  would	 pass a	string of Lua code as the first	argument.  But
       you can also use	it to emit an expression if you	pass in	 a  string  as
       the second argument.

       Note  that this should only be used in exceptional or temporary circum-
       stances,	and if you are able to avoid it, you should.

DEPRECATED FORMS
       The # form is a deprecated alias	for length, and	 ~=  is	 a  deprecated
       alias for not=, kept for	backwards compatibility.

   require-macros load macros with less	flexibility
       (Deprecated in 0.4.0)

       The  require-macros  form  is like import-macros, except	it imports all
       macros without making it	clear what new identifiers  are	 brought  into
       scope.  It is strongly recommended to use import-macros instead.

   pick-args create a function of fixed	arity
       (Deprecated 0.10.0)

       Like pick-values, but takes an integer n	and a function/operator	f, and
       creates a new function that applies exactly n arguments to f.

   global set global variable
       (Deprecated in 1.1.0)

       Sets  a global variable to a new	value.	Note that there	is no distinc-
       tion between introducing	a new global and changing the value of an  ex-
       isting one.  This supports destructuring.

       Example:

	      (global prettyprint (fn [x] (print (fennel.view x))))

       Using  global  adds  the	 identifier in question	to the list of allowed
       globals so that referring to it later on	will not cause a compiler  er-
       ror.   However, globals are also	available in the _G table, and access-
       ing them	that way instead is recommended	for clarity.

   Rest	destructuring metamethod
       (Deprecated in 1.4.1, will be removed in	future versions)

       If a table implements __fennelrest metamethod it	is used	to capture the
       remainder of the	table.	It can be used with custom data	structures im-
       plemented in terms of tables, which wish	to  provide  custom  rest  de-
       structuring.   The metamethod receives the table	as the first argument,
       and the amount of values	it needs to drop from the beginning of the ta-
       ble, much like table.unpack

       Example:

	      (local t [1 2 3 4	5 6])
	      (setmetatable
	       t
	       {:__fennelrest (fn [t k]
				(let [res {}]
				  (for [i k (length t)]
				    (tset res (tostring	(. t i)) (. t i)))
				res))})
	      (let [[a b & c] t]
		c) ;; => {:3 3 :4 4 :5 5 :6 6}

AUTHORS
       Fennel Maintainers.

fennel 1.6.1			  2025-12-30		   fennel-reference(5)

Want to link to this manual page? Use this URL:
<https://man.freebsd.org/cgi/man.cgi?query=fennel-reference&sektion=5&manpath=FreeBSD+Ports+15.1.quarterly>

home | help