1fennel-tutorial(7) Getting Started with Fennel fennel-tutorial(7)
2
3
4
6 fennel-tutorial - Getting Started with Fennel
7
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
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
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
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
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
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
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
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
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
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
678 Fennel Maintainers.
679
680
681
682fennel 1.3.1 2023-07-05 fennel-tutorial(7)