1fennel-tutorial(7)        Getting Started with Fennel       fennel-tutorial(7)
2
3
4

NAME

6       fennel-tutorial - Getting Started with Fennel
7

DESCRIPTION

9       A  programming language is made up of syntax and semantics.  The seman‐
10       tics of Fennel vary only in small ways from Lua (all noted below).  The
11       syntax  of  Fennel comes from the lisp family of languages.  Lisps have
12       syntax which is very uniform and predictable, which makes it easier  to
13       write code that operates on code (https://stopa.io/post/265) as well as
14       structured editing  (http://danmidwood.com/content/2014/11/21/animated-
15       paredit.html).
16
17       If  you  know Lua and a lisp already, you'll feel right at home in Fen‐
18       nel.  Even if not, Lua is one of the simplest programming languages  in
19       existence, so if you've programmed before you should be able to pick it
20       up without too much trouble, especially if you've used another  dynamic
21       imperative   language   with   closures.    The  Lua  reference  manual
22       (https://www.lua.org/manual/5.1/) is a fine place to look for  details,
23       but  Fennel's  own  Lua  Primer (https://fennel-lang.org/lua-primer) is
24       shorter and covers the highlights.
25
26       If you've already got some Lua example code and you just  want  to  see
27       how it would look in Fennel, you can learn a lot from putting it in an‐
28       tifennel (https://fennel-lang.org/see).
29

OK, SO HOW DO YOU DO THINGS?

31   Functions and lambdas
32       Use fn to make functions.  If you provide an optional name,  the  func‐
33       tion  will be bound to that name in local scope; otherwise it is simply
34       an anonymous value.
35
36              A brief note on naming: identifiers are typically lowercase sep‐
37              arated  by  dashes  (aka "kebab-case").  They may contain digits
38              too, as long as they're not at the start.  You can also use  the
39              question  mark  (typically  for  functions that return a true or
40              false, ex., at-max-velocity?).  Underscores (_) are  often  used
41              to name a variable that we don't plan on using.
42
43       The  argument  list is provided in square brackets.  The final value in
44       the body is returned.
45
46       (If you've never used a lisp before, the main thing to note is that the
47       function or macro being called goes inside the parens, not outside.)
48
49              (fn print-and-add [a b c]
50                (print a)
51                (+ b c))
52
53       Functions  can  take an optional docstring in the form of a string that
54       immediately follows the arglist.  Under normal compilation, this is re‐
55       moved from the emitted Lua, but during development in the REPL the doc‐
56       string and function usage can be viewed with the ,doc command:
57
58              (fn print-sep [sep ...]
59                "Prints args as a string, delimited by sep"
60                (print (table.concat [...] sep)))
61              ,doc print-sep ; -> outputs:
62              ;; (print-sep sep ...)
63              ;;   Prints args as a string, delimited by sep
64
65       Like other lisps, Fennel uses semicolons for comments.
66
67       Functions defined with fn are fast; they have no runtime overhead  com‐
68       pared  to  Lua.   However, they also have no arity checking.  (That is,
69       calling a function with the wrong number of arguments does not cause an
70       error.)   For  safer code you can use lambda which ensures you will get
71       at least as many arguments as you define, unless you signify  that  one
72       may be omitted by beginning its name with a ?:
73
74              (lambda print-calculation [x ?y z]
75                (print (- x (* (or ?y 1) z))))
76
77              (print-calculation 5) ; -> error: Missing argument z
78
79       Note that the second argument ?y is allowed to be nil, but z is not:
80
81              (print-calculation 5 nil 3) ; -> 2
82
83       Like fn, lambdas accept an optional docstring after the arglist.
84
85   Locals and variables
86       Locals  are introduced using let with the names and values wrapped in a
87       single set of square brackets:
88
89              (let [x (+ 89 5.2)
90                    f (fn [abc] (print (* 2 abc)))]
91                (f x))
92
93       Here x is bound to the result of adding 89 and 5.2, while f is bound to
94       a  function  that  prints  twice its argument.  These bindings are only
95       valid inside the body of the let call.
96
97       You can also introduce locals with local, which is nice when they'll be
98       used  across  the  whole  file, but in general let is preferred because
99       it's clearer at a glance where the value is used:
100
101              (local tau-approx 6.28318)
102
103       Locals set this way cannot be given new values, but you  can  introduce
104       new locals that shadow the outer names:
105
106              (let [x 19]
107                ;; (set x 88) <- not allowed!
108                (let [x 88]
109                  (print (+ x 2))) ; -> 90
110                (print x)) ; -> 19
111
112       If you need to change the value of a local, you can use var which works
113       like local except it allows set to work on it.  There is no nested let-
114       like equivalent of var.
115
116              (var x 19)
117              (set x (+ x 8))
118              (print x) ; -> 27
119
120   Numbers and strings
121       Of  course,  all  our standard arithmetic operators like +, -, *, and /
122       work here in prefix  form.   Note  that  numbers  are  double-precision
123       floats  in  all  Lua versions prior to 5.3, which optionally introduced
124       integers.  On 5.3 and up, integer division uses // and  bitwise  opera‐
125       tions  use  lshift, rshift, bor, band, bnot and xor.  Bitwise operators
126       and integer division will not work if the host Lua environment is older
127       than version 5.3.
128
129       You may also use underscores to separate sections of long numbers.  The
130       underscores have no effect on the output.
131
132              (let [x (+ 1 99)
133                    y (- x 12)
134                    z 100_000]
135                (+ z (/ y 10)))
136
137       Strings are essentially immutable byte arrays.  UTF-8 support  is  pro‐
138       vided   in  the  utf8  table  in  Lua  5.3+  (https://www.lua.org/manu
139       al/5.3/manual.html#6.5)     or     from     a     3rd-party     library
140       (https://github.com/Stepets/utf8.lua) in earlier versions.  Strings are
141       concatenated with ..:
142
143              (.. "hello" " world")
144
145   Tables
146       In Lua (and thus in Fennel), tables are the only data  structure.   The
147       main syntax for tables uses curly braces with key/value pairs in them:
148
149              {"key" value
150               "number" 531
151               "f" (fn [x] (+ x 2))}
152
153       You can use . to get values out of tables:
154
155              (let [tbl (function-which-returns-a-table)
156                    key "a certain key"]
157                (. tbl key))
158
159       And tset to put them in:
160
161              (let [tbl {}
162                    key1 "a long string"
163                    key2 12]
164                (tset tbl key1 "the first value")
165                (tset tbl key2 "the second one")
166                tbl) ; -> {"a long string" "the first value" 12 "the second one"}
167
168   Sequential Tables
169       Some  tables  are used to store data that's used sequentially; the keys
170       in this case are just numbers starting with 1  and  going  up.   Fennel
171       provides alternate syntax for these tables with square brackets:
172
173              ["abc" "def" "xyz"] ; equivalent to {1 "abc" 2 "def" 3 "xyz"}
174
175       Lua's  built-in  table.insert function is meant to be used with sequen‐
176       tial tables; all values after the inserted value are shifted up by  one
177       index:  If you don't provide an index to table.insert it will append to
178       the end of the table.
179
180       The table.remove function works similarly; it takes a table and an  in‐
181       dex  (which  defaults to the end of the table) and removes the value at
182       that index, returning it.
183
184              (local ltrs ["a" "b" "c" "d"])
185
186              (table.remove ltrs)       ; Removes "d"
187              (table.remove ltrs 1)     ; Removes "a"
188              (table.insert ltrs "d")   ; Appends "d"
189              (table.insert ltrs 1 "a") ; Prepends "a"
190
191              (. ltrs 2)                ; -> "b"
192              ;; ltrs is back to its original value ["a" "b" "c" "d"]
193
194       The length form returns the length of sequential tables and strings:
195
196              (let [tbl ["abc" "def" "xyz"]]
197                (+ (length tbl)
198                   (length (. tbl 1)))) ; -> 6
199
200       Note that the length of a table with gaps in it is  undefined;  it  can
201       return  a  number  corresponding to any of the table's "boundary" posi‐
202       tions between nil and non-nil values.
203
204       Lua's standard library is very small, and thus  several  functions  you
205       might  expect  to be included, such map, reduce, and filter are absent.
206       In Fennel macros are used for this instead; see icollect, collect,  and
207       accumulate.
208
209   Iteration
210       Looping  over  table  elements  is  done with each and an iterator like
211       pairs (used for general tables) or ipairs (for sequential tables):
212
213              (each [key value (pairs {"key1" 52 "key2" 99})]
214                (print key value))
215
216              (each [index value (ipairs ["abc" "def" "xyz"])]
217                (print index value))
218
219       Note that whether a table is sequential or not is not an inherent prop‐
220       erty  of  the table but depends on which iterator is used with it.  You
221       can call ipairs on any table, and it will  only  iterate  over  numeric
222       keys starting with 1 until it hits a nil.
223
224       You  can  use  any Lua iterator (https://www.lua.org/pil/7.1.html) with
225       each, but these are the most common.   Here's  an  example  that  walks
226       through   matches  in  a  string  (https://www.lua.org/manual/5.1/manu
227       al.html#pdf-string.gmatch):
228
229              (var sum 0)
230              (each [digits (string.gmatch "244 127 163" "%d+")]
231                (set sum (+ sum (tonumber digits))))
232
233       If you want to get a table back, try icollect to get a sequential table
234       or collect to get a key/value one.  A body which returns nil will cause
235       that to be omitted from the resulting table.
236
237              (icollect [_ s (ipairs [:greetings :my :darling])]
238                (if (not= :my s)
239                    (s:upper)))
240              ;; -> ["GREETINGS" "DARLING"]
241
242              (collect [_ s (ipairs [:greetings :my :darling])]
243                s (length s))
244              ;; -> {:darling 7 :greetings 9 :my 2}
245
246       A lower-level iteration construct is  for  which  iterates  numerically
247       from the provided start value to the inclusive finish value:
248
249              (for [i 1 10]
250                (print i))
251
252       You  can  specify an optional step value; this loop will only print odd
253       numbers under ten:
254
255              (for [i 1 10 2]
256                (print i))
257
258   Looping
259       If you need to loop but don't know how many times, you can use while:
260
261              (while (keep-looping?)
262                (do-something))
263
264   Conditionals
265       Finally we have conditionals.  The if form in Fennel can  be  used  the
266       same  way  as  in other lisp languages, but it can also be used as cond
267       for multiple conditions compiling into elseif branches:
268
269              (let [x (math.random 64)]
270                (if (= 0 (% x 2))
271                    "even"
272                    (= 0 (% x 9))
273                    "multiple of nine"
274                    "I dunno, something else"))
275
276       With an odd number of arguments, the final  clause  is  interpreted  as
277       "else".
278
279       Being a lisp, Fennel has no statements, so if returns a value as an ex‐
280       pression.  Lua programmers will be glad to know there  is  no  need  to
281       construct precarious chains of and/or just to get a value!
282
283       The other conditional is when, which is used for an arbitrary number of
284       side-effects and has no else clause:
285
286              (when (currently-raining?)
287                (wear "boots")
288                (deploy-umbrella))
289

BACK TO TABLES JUST FOR A BIT

291       Strings that don't have spaces or reserved characters in them  can  use
292       the :shorthand syntax instead, which is often used for table keys:
293
294              {:key value :number 531}
295
296       If  a  table  has  string keys like this, you can pull values out of it
297       easily with a dot if the keys are known up front:
298
299              (let [tbl {:x 52 :y 91}]
300                (+ tbl.x tbl.y)) ; -> 143
301
302       You can also use this syntax with set:
303
304              (let [tbl {}]
305                (set tbl.one 1)
306                (set tbl.two 2)
307                tbl) ; -> {:one 1 :two 2}
308
309       If a table key has the same name as the variable you're setting it  to,
310       you can omit the key name and use : instead:
311
312              (let [one 1 two 2
313                    tbl {: one : two}]
314                tbl) ; -> {:one 1 :two 2}
315
316       Finally, let can destructure a table into multiple locals.
317
318       There is positional destructuring:
319
320              (let [data [1 2 3]
321                    [fst snd thrd] data]
322                (print fst snd thrd)) ; -> 1       2       3
323
324       And destructuring of tables via key:
325
326              (let [pos {:x 23 :y 42}
327                    {:x x-pos :y y-pos} pos]
328                (print x-pos y-pos)) ; -> 23      42
329
330       As  above,  if a table key has the same name as the variable you're de‐
331       structuring it to, you can omit the key name and use : instead:
332
333              (let [pos {:x 23 :y 42}
334                    {: x : y} pos]
335                (print x y)) ; -> 23      42
336
337       This can nest and mix and match:
338
339              (let [f (fn [] ["abc" "def" {:x "xyz" :y "abc"}])
340                    [a d {:x x : y}] (f)]
341                (print a d)
342                (print x y))
343
344       If the size of the table doesn't match the number  of  binding  locals,
345       missing  values  are  filled  with  nil and extra values are discarded.
346       Note that unlike many languages, nil in Lua actually represents the ab‐
347       sence  of  a value, and thus tables cannot contain nil.  It is an error
348       to try to use nil as a key, and using nil as a value  removes  whatever
349       entry was at that key before.
350

ERROR HANDLING

352       Errors  in  Lua have two forms they can take.  Functions in Lua can re‐
353       turn any number of values, and most functions which can fail will indi‐
354       cate failure by using two return values: nil followed by a failure mes‐
355       sage string.  You can interact with this style of function in Fennel by
356       destructuring with parens instead of square brackets:
357
358              (match (io.open "file")
359                ;; when io.open succeeds, it will return a file, but if it fails it will
360                ;; return nil and an err-msg string describing why
361                f (do (use-file-contents (f:read :*all))
362                      (f:close))
363                (nil err-msg) (print "Could not open file:" err-msg))
364
365       You can write your own function which returns multiple values with val‐
366       ues.
367
368              (fn use-file [filename]
369                (if (valid-file-name? filename)
370                    (open-file filename)
371                    (values nil (.. "Invalid filename: " filename))))
372
373       Note: while errors are the most common reason to return multiple values
374       from  a  function,  it can be used in other cases as well.  This is the
375       most complex thing about Lua, and a full discussion is out of scope for
376       this   tutorial,   but   it's  covered  well  elsewhere  (https://bena
377       iah.me/posts/everything-you-didnt-want-to-know-about-lua-multivals/).
378
379       The problem with this type of error is that it does not  compose  well;
380       the  error  status  must be propagated all the way along the call chain
381       from inner to outer.  To address this, you can use  error.   This  will
382       terminate  the whole process unless it's within a protected call, simi‐
383       lar to the way in other languages where throwing an exception will stop
384       the  program unless it is within a try/catch.  You can make a protected
385       call with pcall:
386
387              (let [(ok? val-or-msg) (pcall potentially-disastrous-call filename)]
388                (if ok?
389                    (print "Got value" val-or-msg)
390                    (print "Could not get value:" val-or-msg)))
391
392       The pcall invocation there means you  are  running  (potentially-disas‐
393       trous-call  filename) in protected mode.  pcall takes an arbitrary num‐
394       ber of arguments which are passed on to the function.  You can see that
395       pcall returns a boolean (ok? here) to let you know if the call succeed‐
396       ed or not, and a second value (val-or-msg) which is the actual value if
397       it succeeded or an error message if it didn't.
398
399       The  assert function takes a value and an error message; it calls error
400       if the value is nil and returns it otherwise.  This can be used to turn
401       multiple-value failures into errors (kind of the inverse of pcall which
402       turns errors into multiple-value failures):
403
404              (let [f (assert (io.open filename))
405                    contents (f.read f "*all")]
406                (f.close f)
407                contents)
408
409       In this example because io.open returns nil and an error  message  upon
410       failure, a failure will trigger an error and halt execution.
411

VARIADIC FUNCTIONS

413       Fennel supports variadic functions like many languages.  The syntax for
414       taking a variable number of arguments to a function is the ...  symbol,
415       which  must be the last parameter to a function.  This syntax is inher‐
416       ited from Lua rather than Lisp.
417
418       The ... form is not a list or first class value, it expands to multiple
419       values  inline.   To  access individual elements of the vararg, you can
420       destructure with parentheses, or first  wrap  it  in  a  table  literal
421       ([...])  and index like a normal table, or use the select function from
422       Lua's core library.  Often, the vararg can be passed directly to anoth‐
423       er function such as print without needing to bind it.
424
425              (fn print-each [...]
426                (each [i v (ipairs [...])]
427                  (print (.. "Argument " i " is " v))))
428
429              (print-each :a :b :c)
430
431              (fn myprint [prefix ...]
432                (io.write prefix)
433                (io.write (.. (select "#" ...) " arguments given: "))
434                (print ...))
435
436              (myprint ":D " :d :e :f)
437
438       Varargs  are scoped differently than other variables as well - they are
439       only accessible to the function in which they are created.  This  means
440       that  the  following  code  will  NOT work, as the varargs in the inner
441       function are out of scope.
442
443              (fn badcode [...]
444                (fn []
445                  (print ...)))
446
447       You can read more detailed coverage of some of the  problems  with  ...
448       and   multiple  values  (https://benaiah.me/posts/everything-you-didnt-
449       want-to-know-about-lua-multivals/) here.
450

STRICT GLOBAL CHECKING

452       If you get an error that says unknown global in strict  mode  it  means
453       that  you're  trying  compile  code that uses a global which the Fennel
454       compiler doesn't know about.  Most of the time, this is due to a coding
455       mistake.   However, in some cases you may get this error with a legiti‐
456       mate global reference.  If this happens, it may be due to  an  inherent
457       limitation  of  Fennel's strategy.  You can use _G.myglobal to refer to
458       it in a way that works around this check and  calls  attention  to  the
459       fact that this is in fact a global.
460
461       Another  possible  cause for this error is a modified function environ‐
462       ment (https://www.lua.org/pil/14.3.html).  The solution depends on  how
463       you're using Fennel:
464
465       • Embedded  Fennel can have its searcher modified to ignore certain (or
466         all) globals via the  allowedGlobals  parameter.   See  the  Lua  API
467         (https://fennel-lang.org/api) page for instructions.
468
469       • Fennel's CLI has the --globals parameter, which accepts a comma-sepa‐
470         rated list of globals to ignore.  For example, to disable strict mode
471         for globals x, y, and z:
472
473                fennel --globals x,y,z yourfennelscript.fnl
474

GOTCHAS

476       There  are  a  few surprises that might bite seasoned lispers.  Most of
477       these result necessarily from Fennel's insistence  upon  imposing  zero
478       runtime overhead over Lua.
479
480       • The arithmetic, comparison, and boolean operators are not first-class
481         functions.  They can behave in surprising ways with  multiple-return-
482         valued  functions,  because  the  number of arguments to them must be
483         known at compile-time.
484
485       • There is no apply function; instead use table.unpack  or  unpack  de‐
486         pending on your Lua version: (f 1 3 (table.unpack [4 9])).
487
488       • Tables  are  compared  for  identity, not based on the value of their
489         contents, as per  Baker  (https://p.hagelb.org/equal-rights-for-func
490         tional-objects.html).
491
492       • Return values in the repl will get pretty-printed, but calling (print
493         tbl) will emit output like table: 0x55a3a8749ef0.  If you  don't  al‐
494         ready  have  one,  it's recommended for debugging to define a printer
495         function which calls fennel.view on its argument before printing  it:
496         (local  fennel  (require  :fennel)) (fn _G.pp [x] (print (fennel.view
497         x)))
498
499       • Lua programmers should note Fennel functions cannot do early returns.
500

OTHER STUFF JUST WORKS

502       Note   that   built-in   functions   in    Lua's    standard    library
503       (https://www.lua.org/manual/5.1/manual.html#5)  like  math.random above
504       can be called with no fuss and no overhead.
505
506       This includes features like coroutines, which are often implemented us‐
507       ing special syntax in other languages.  Coroutines let you express non-
508       blocking operations without callbacks  (https://leafo.net/posts/itchio-
509       and-coroutines.html).
510
511       Tables    in   Lua   may   seem   a   bit   limited,   but   metatables
512       (https://www.lua.org/pil/13.html) allow a great deal more  flexibility.
513       All the features of metatables are accessible from Fennel code just the
514       same as they would be from Lua.
515

MODULES AND MULTIPLE FILES

517       You can use the require function to load code from other files.
518
519              (let [lume (require :lume)
520                    tbl [52 99 412 654]
521                    plus (fn [x y] (+ x y))]
522                (lume.map tbl (partial plus 2))) ; -> [54 101 414 656]
523
524       Modules in Fennel and Lua are simply tables which contain functions and
525       other values.  The last value in a Fennel file will be used as the val‐
526       ue of the whole module.  Technically this can be any value, not just  a
527       table, but using a table is most common for good reason.
528
529       To  require  a module that's in a subdirectory, take the file name, re‐
530       place the slashes with dots, and remove the extension, then  pass  that
531       to  require.  For instance, a file called lib/ui/menu.lua would be read
532       when loading the module lib.ui.menu.
533
534       When you run your program with the fennel command, you can call require
535       to  load Fennel or Lua modules.  But in other contexts (such as compil‐
536       ing to Lua and then using the lua command, or in  programs  that  embed
537       Lua)  it  will  not know about Fennel modules.  You need to install the
538       searcher that knows how to find .fnl files:
539
540              require("fennel").install()
541              local mylib = require("mylib") -- will compile and load code in mylib.fnl
542
543       Once you add this, require will work on Fennel files just like it  does
544       with   Lua;   for   instance   (require  :mylib.parser)  will  look  in
545       "mylib/parser.fnl" on Fennel's search path (stored in fennel.path which
546       is distinct from package.path used to find Lua modules).  The path usu‐
547       ally includes an entry to let you load things relative to  the  current
548       directory by default.
549

RELATIVE REQUIRE

551       There  are  several ways to write a library which uses modules.  One of
552       these is to rely on something like LuaRocks, to manage library  instal‐
553       lation  and  availability of it and its modules.  Another way is to use
554       the relative require style for loading nested modules.   With  relative
555       require, libraries don't depend on the root directory name or its loca‐
556       tion when resolving inner module paths.
557
558       For example, here's a small example library, which contains an init.fnl
559       file, and a module at the root directory:
560
561              ;; file example/init.fnl:
562              (local a (require :example.module-a))
563
564              {:hello-a a.hello}
565
566       Here,  the  main  module  requires  additional example.module-a module,
567       which holds the implementation:
568
569              ;; file example/module-a.fnl
570              (fn hello [] (print "hello from a"))
571              {:hello hello}
572
573       The main issue here is that the path to the library must be exactly ex‐
574       ample,  e.g.   library must be required as (require :example) for it to
575       work, which can't be enforced on the library user.  For example, if the
576       library were moved into libs directory of the project to avoid clutter‐
577       ing, and required as (require :libs.example), there will be  a  runtime
578       error.   This happens because library itself will try to require :exam‐
579       ple.module-a and not :libs.example.module-a, which is now  the  correct
580       module path:
581
582              runtime error: module 'example.module-a' not found:
583                      no field package.preload['example.module-a']
584                      ...
585                      no file './example/module-a.lua'
586                      ...
587              stack traceback:
588                [C]: in function 'require'
589                ./libs/example/init.fnl:2: in main chunk
590
591       LuaRocks  addresses  this  problem by enforcing both the directory name
592       and installation path, populating the LUA_PATH environment variable  to
593       make  the  library available.  This, of course, can be done manually by
594       setting LUA_PATH per project in the build pipeline, pointing it to  the
595       right  directory.  But this is not very transparent, and when requiring
596       a project local library it's better to see the full path, that directly
597       maps  to the project's file structure, rather than looking up where the
598       LUA_PATH is modified.
599
600       In the Fennel ecosystem we encourage a simpler way of managing  project
601       dependencies.   Simply  dropping  a library into your project's tree or
602       using git submodule is usually enough, and the require paths should  be
603       handled by the library itself.
604
605       Here's  how  a relative require path can be specified in the libs/exam‐
606       ple/init.fnl to make it name/path agnostic, assuming that  we've  moved
607       our example library there:
608
609              ;; file libs/example/init.fnl:
610              (local a (require (.. ... :.module-a)))
611
612              {:hello-a a.hello}
613
614       Now, it doesn't matter how library is named or where we put it - we can
615       require it from anywhere.  It works because when requiring the  library
616       with  (require  :lib.example),  the  first  value  in ... will hold the
617       "lib.example" string.  This string is then concatenated with the ".mod‐
618       ule-a",  and  require  will properly find and load the nested module at
619       runtime under the "lib.example.module-a" path.  It's a Lua feature, and
620       not  something  Fennel specific, and it will work the same when the li‐
621       brary is AOT compiled to Lua.
622
623   Compile-time relative include
624       Since Fennel v0.10.0 this also works at compile-time,  when  using  the
625       include  special  or the --require-as-include flag, with the constraint
626       that the expression can be computed at compile time.  This  means  that
627       the expression must be self-contained, i.e.  doesn't refer to locals or
628       globals, but embeds all values directly.  In other words, the following
629       code  will  only work at runtime, but not with include or --require-as-
630       include because current-module is not known at compile time:
631
632              (local current-module ...)
633              (require (.. current-module :.other-module))
634
635       This, on the other hand, will work both at runtime and at compile time:
636
637              (require (.. ... :.other-module))
638
639       The ... module args are propagated during compilation, so when the  ap‐
640       plication which uses this library is compiled, all library code is cor‐
641       rectly included into the self-contained Lua file.
642
643       Compiling a project that uses this example library  with  --require-as-
644       include will include the following section in the resulting Lua code:
645
646              package.preload["libs.example.module-a"] = package.preload["libs.example.module-a"] or function(...)
647                local function hello()
648                  return print("hello from a")
649                end
650                return {hello = hello}
651              end
652
653       Note  that  the  package.preload  entry contains a fully qualified path
654       "libs.example.module-a", which was resolved at compile time.
655
656   Requiring modules from modules other than init.fnl
657       To require a module from a module other than init module, we must  keep
658       the path up to the current module, but remove the module name.  For ex‐
659       ample, let's add a greet module  in  libs/example/utils/greet.fnl,  and
660       require it from libs/example/module-a.fnl:
661
662              ;; file libs/example/utils/greet.fnl:
663              (fn greet [who] (print (.. "hello " who)))
664
665       This module can be required as follows:
666
667              ;; file libs/example/module-a.fnl
668              (local greet (require (.. (: ... :match "(.+)%.[^.]+") :.utils.greet)))
669
670              (fn hello [] (print "hello from a"))
671
672              {:hello hello :greet greet}
673
674       The  parent  module  name is determined via calling the match method on
675       the current module name string (...).
676

AUTHORS

678       Fennel Maintainers.
679
680
681
682fennel 1.3.1                      2023-07-05                fennel-tutorial(7)
Impressum