True leaders
are hardly known to their followers.
Next after them are the leaders
the people know and admire;
after them, those they fear;
after them, those they despise.To give no trust
is to get no trust.When the work's done right,
with no fuss or boasting,
ordinary people say,
Oh, we did it.
- Tao Te Ching chapter 17, translated by Ursula K. Le Guin
The fennel module is the fundamental entry point; it provides the entire
public API for Fennel when it's used inside another program. All other modules
except fennel.view are considered compiler internals and do not have a
guaranteed stable API.
src/fennel.fnl: returned when Fennel is embedded programmaticallysrc/launcher.fnl: handles being launched from the command linesrc/fennel/repl.fnl: provides interactive development contextThe core modules implement the text->AST->Lua pipeline. The AST used by the compiler is the exact same AST that is exposed to macros.
src/fennel/parser.fnl: turns text of code into an ASTsrc/fennel/compiler.fnl: turns AST into Lua outputsrc/fennel/specials.fnl: built-in fundamental language constructssrc/fennel/macros.fnl: built-in language constructs that use fundamentalssrc/fennel/match.fnl: pattern matching macro implementationssrc/fennel/utils.fnl: definitions of core AST types and helper functionsFinally there are a few miscellaneous modules:
src/fennel/friend.fnl: emits friendly messages from compiler/parser errorssrc/fennel/binary.fnl: produces binary standalone executablessrc/fennel/view.fnl: turn Fennel data structures into printable stringsFennel is written in Fennel. In order to get around the chicken-and-egg problem, we include an older version of the compiler (written in Lua) that's used to compile the new version (written in Fennel).
bootstrap/fennel.lua: version 0.4.x of the compiler librarybootstrap/aot.lua: short shim which wraps the library to do AOTNot all changes need to be backported to the bootstrap compiler, but new macros generally should be.
The file src/fennel/macros.fnl where the built-in macros are defined
is evaluated by the compiler in src/, not by the bootstrap compiler.
This means that you cannot use any macros here; for instance it's
necessary to use if even in cases where when would make more sense.
The file src/fennel/match.fnl contains the pattern matching macros;
because of their complexity they are broken out so that they can use the rest
of the macros in their implementation.
Before considering making a change to Fennel, please familiarize yourself with the Values of Fennel.
Fennel has made incompatible changes in the past, but at this point in its evolution we are committed to backwards compatibility. A change which breaks existing programs will only be considered if it fixes a security vulnerability.
Fennel follows Lua's lead in being a language with a very small conceptual footprint. Being built on Lua, Fennel is necessarily larger than Lua, but not by a lot. We have a high bar for adding new features to the language. Once you have identified a problem and have sketched out a potential solution there are four main questions to consider:
Let's look at some examples.
The match macro is quite large, both in terms of its implementation and its
meaning; it is by far the biggest addition to the semantics of Fennel for
which a comparative construct does not exist in Lua. Pattern matching in
general can be thought of as a composition of conditions and destructuring,
so its addition is not as big in Fennel (where conditions and destructuring
both already exist a la carte) as it would be in a language which did not
already have destructuring. But weighing this cost against the benefits we note
that match is applicable to a multitude of situations and that rewriting
the code to avoid it results in ugly code.
Adding icollect was thoroughly merited in that it is needed very frequently,
and the alternative is tedious. When considering the conceptual footprint, we
note that icollect parallels the existing each construct closely; the
only difference being that the body of the macro is used to construct a
sequential table instead of being discarded. So the cost/benefit ratio is
great. The collect macro, on the other hand, is used much more
infrequently. But it's also an even smaller change; given that icollect
exists, it's fairly obvious how a parallel key/value-based variant would
work.
Note that the above only describes the process for language-level features. There are other changes which affect (say) the compiler or the repl but do not affect the language itself; the dynamic for making those changes is different and the bar (other than that of backwards-compatibility) is not quite so high. An addition to the language is a cost that everyone reading and writing Fennel code from here on out will have to pay; an addition to the API is not.
If you want to contribute code to the project, please send patches to the
mailing list. Note that you do not need to subscribe to the mailing list
in order to post to it. When sending patches to the mailing list, it's usually
nicer to squash everything down to a single commit so that it will be sent as
a single email rather than a series of messages, unless there really are two
relatively unrelated changes. If you like to use git send-email you can,
but since its usability is not very good, you can also just attach your patch
to your message if you prefer. Running git format-patch HEAD~ will write
your most recent change to a .patch file you can attach.
We also accept code contributions on the GitHub mirror if you prefer not to use email. For smaller changes that are unlikely to require back-and-forth discussion, you can also push your changes to a branch on a public git remote hosted anywhere you like and ask someone on IRC/Matrix or the mailing list to take a look.
Please note that it is ethically unacceptable to submit patches (to this project or any other) which you did not author yourself without giving clear attribution to the original author. Note that this includes submitting changes generated by most so-called "artificial intelligence" language models as these systems make it impossible to even identify (much less credit) the original author.
In order to get CI to automatically run your patches, they will need to have
[PATCH fennel] in the subject. You can configure git to do this automatically:
git config format.subjectPrefix 'PATCH fennel'
For large changes, please discuss it first either on the mailing list, IRC/Matrix channel, or in the issue tracker before sinking time and effort into something that may not be able to get merged.
main branch. The contents of this branch should be
the same on Sourcehut as they are on the Github mirror. But make
sure that main on your copy of the repo matches upstream.make test.
Fennel's tests use the faith
library; see the docs there and follow the conventions in existing tests.make test) and rely on the CI suite to run the rest, but for larger
changes please make sure that your changes will work on Lua versions 5.1, 5.2,
5.3, 5.4, and LuaJIT. Making fennel require LuaJIT or 5.2+ specific
features is not going to fly. In general, this means target Lua
5.1, but provide shims for where functionality is different in newer Lua
versions. Running make testall will test against all supported versions,
assuming they're installed.changelog.md. Changes that affect the compiler API should update api.md
while changes to the built-in forms will usually need to update
reference.md to reflect the new behavior.