
Fennel – Lisp in Lua - tosh
https://fennel-lang.org
======
sillysaurusx
Lumen – Lisp in Lua and JavaScript:
[https://github.com/sctb/lumen](https://github.com/sctb/lumen)

Scott Bell wrote a lot of that. I was fascinated by the self-hosting
technique. The whole repo is so small that you can almost dismiss it as some
kind of academic toy, if you don't realize it's a full-featured production-
grade lisp.

(Not to take anything away from Fennel! The more lisp, the better.)

~~~
tgbugs
I knew about Fennel, but did not know about Lumen. Thanks for the pointer!

edit: Looking at the git history is very educational for how to bootstrap a
self hosting system like this. Fascinating.

------
rudolfwinestock
A Hacker News regular, technomancy (the same guy behind the Atreus keyboard),
has contributed a great deal to the Fennel language.
[https://builds.sr.ht/~technomancy/fennel-
lang.org](https://builds.sr.ht/~technomancy/fennel-lang.org)

I'd like to see him visit this thread.

[https://news.ycombinator.com/user?id=technomancy](https://news.ycombinator.com/user?id=technomancy)

~~~
reitzensteinm
Not to mention lein for Clojure. He only posts here a few times a year which
might be how he manages to be so prolific.

~~~
O_H_E
Ahh, that's whats probably holding me back.

\s .... but not really. I need to figure out a way to tone down my mind
wandering off.

------
aasasd
I really wish Lua were popular for small-scale utility scripting instead of
Python and JS. Because Lua is way snappier: even with language translation,
plenty of Fennel scripts will run under Python's startup time. Likewise, Lua
seems like a great fit for small mobile apps and automation (dunno about
threading though, but I seem to have read that Lua can do threads with a
_library_ ).

My primary woe with Lua is the absence of ‘null’. You can't preserve structure
of deserialized tables if there are nil values in them, so you can't really
build API middleware and such, without extra effort for that (so you'd also
not be able to use functional libraries like Penlight in your middleware).

~~~
ebg13
Lua will never be more popular without a proper standard library.

Leaving everything up to the user so that you have to manually implement (or
hunt the web for) even the most basic features is mega annoying. How many
millions of people need to rewrite string.startswith?

The whole "sequence iteration stops at the first internal hole and therefore
the # operator doesn't do what you think it should" gotcha is extremely user
hostile.

Metatables are cool and all, but holy crap are they confusing for Lua newbies.
Having an integrated class system would go a long way.

~~~
pansa2
> _Lua will never be more popular without a proper standard library._

How would you add a large standard library without compromising Lua’s main
purpose - to be a lightweight, embeddable language?

~~~
ebg13
It would still be lightweight and embeddable with common sense pure Lua
functionality like classes and major utility methods like
string.startswith/string.endswith/string.trim/string.split/table.deepcopy. You
don't have to integrate a multiprocessing web server for different operating
systems.

Instead you're left with bullshit community essays like [http://lua-
users.org/wiki/SplitJoin](http://lua-users.org/wiki/SplitJoin) which shows
pages and pages of different user attempts for one extremely common function,
many of which explicitly do not work!

Lua has its strengths, but faffing about refusing to just standardize the
right way to do things is super user-hostile.

~~~
pull_my_finger
Lua, by design, favors "mechanisms not policy"[1]. It's not a failing it meant
to empower users. There are plenty of mature utility libraries that serve for
batteries if you like that.

[1] - [http://lua-users.org/wiki/MechanismNotPolicy](http://lua-
users.org/wiki/MechanismNotPolicy)

~~~
ebg13
> _It 's not a failing it meant to empower users._

Freedom to implement bespoke solutions is great if what you want to spend your
time on is implementing bespoke solutions. Freedom from needing to implement
bespoke solutions is better for everyone.

Even non-programmers can understand what common simple operations are for and
when they might be useful. Saying that one needs to know how to implement or
find them as well, especially when a bunch of extremely learned participants
in lua-users can repeatedly fail to implement working versions, is literally
the opposite of empowering.

~~~
pull_my_finger
The examples you're listing, I don't use those very often at all. If I did
it's much easier to just grab a lib, then to carry all my tools with me for
every script. Even the libraries that _are_ included can be cast aside if you
want a slim build. Not every language does or _should_ strive to cater to
those looking for quick hacking or rapid prototyping. I like that Lua is slim
and loads fast. I understand your perspective though.

------
rcarmo
It should be noted that 0.6.0 came out 3 days ago, with a few improvements:

[https://fennel-lang.org/v0.6.0/](https://fennel-lang.org/v0.6.0/)

------
mumblemumble
A nice summary of what Fennel's about here: [http://jakob.space/blog/thoughts-
on-lisps.html#org4d7f824](http://jakob.space/blog/thoughts-on-
lisps.html#org4d7f824)

~~~
masklinn
It's kinda weird that the only mention of Clojure is

> Clojure's license makes it a complete non starter for me, sorry. Live free
> or die.

when at first glance fennel looks a lot like clojure e.g. `fn`, square
brackets, {} for maps / table literals.

~~~
SahAssar
> when at first glance fennel looks a lot like clojure e.g. `fn`, square
> brackets, {} for maps / table literals.

Aren't those mostly inherited from lua and present in a lot of different
languages invented before and after clojure?

~~~
masklinn
Lua defines functions with the `function` keywords, and as most other C-style
languages uses parenthesis to wrap the function parameters, not brackets.

It does use `{}` for table literals but requires the `=` and `,` separators,
where fennel (and clojure) uses no internal separator at all: a map literal is
a pair of braces around a _plist_
([https://www.gnu.org/software/emacs/manual/html_node/elisp/Pr...](https://www.gnu.org/software/emacs/manual/html_node/elisp/Property-
Lists.html#Property-Lists\)\[0\]).

Historically, lisp have used something around "define" to define functions
(e.g. define, defun), parenthesis as essentially the only grouping, and would
use macros or special-forms rather than complex literals.

Clojure is the first lisp I know of (so I may well have missed a predecessor
which inspired it) which largely diverged from that.

[0] clojure allows commas but ignores them entirely, I don't know whether
fennel allows them as well

~~~
lispm
If we think Clojure is a 'diverging Lisp', then there are other diverging
Lisps with different syntax like MDL, Logo, Dylan, even JavaScript. Some
Scheme code uses angular brackets.

Historically core Lisp has left other grouping character or syntax to
sublanguages or new data types. There are many examples for that. This is
usually done via reader macros, which open up the s-expression reader to the
programmer.

This is a Joshua rule:

    
    
        (defrule grandfather-determiner (:backward)
          ;; there are 3 ways to find your grandfather
          IF [or [is-genetic-grandfather-of ?gramps ?kid]
          [is-grandfather-thru-step-parent ?gramps ?kid]
          [is-step-grandfather-of ?gramps ?kid]]
          THEN [is-grandfather-of ?gramps ?kid])
    

Many internal or external domain specific languages for Lisp use special
syntax and defining forms, since their authors either believed that it's
easier to use in general or that it was necessary for 'end users' of their
systems. Early examples were maths systems like Macsyma, which were
programmable, but addressed mathematicians as audience. Logo, which was Lisp
for kids. One of the first theorem provers had the first ML as the programming
language interface for its users.

Historically Lisp 2 was supposed to get rid of simple s-expressions on the
surface. There was a different syntax defined for it.

~~~
masklinn
I was mostly talking about diverging in the way Fennel had.

> Historically core Lisp has left other grouping character or syntax to
> sublanguages or new data types.

Sure, but Clojure uses other grouping characters for the main dialect itself,
and Fennel uses very similar deviations from the "Lisp baseline", hence my
wondering about the inspiration.

~~~
lispm
Square brackets have been used in some Scheme for a long time. There are also
Scheme books which use them in code.

~~~
masklinn
Used them to define the formal parameters of a function? Because that's what
I'm referring to. Fennel uses

    
    
        (fn [param*] expr*)
    

for function definition, which is also how it is spelled in Clojure.

~~~
lispm
One can use them everywhere instead of parentheses.

~~~
blandflakes
The difference for Clojure would largely be that other delimiters are sugar
for the various data types, so you make maps with {}, vectors with [], sets
with #{}, etc.

~~~
lispm
it then mixes syntax and data type issues. For example arguments are then
vectors, probably to have [] as syntax. Though in Lisp one would keep argument
lists: there is no technical need to make them vectors.

~~~
blandflakes
I wasn't making a value judgement in the statement, just nothing that in
Clojure the other delimiters aren't just alternative ways to enclose forms.

------
j_m_b
What are some projects that people are using that use Lua? Why use it over
something like python for scripting?

~~~
masklinn
Lua is way more common for game scripting than Python. One of the few major
games which used Python I can think of was Civ IV, and Civ V switched to Lua.

Lua is smaller, easier to embed interface / interact with (e.g. it's way less
ceremony to expose a native function to Lua than to Python), and tends to be
faster, especially when jumping back and forth between the engine and the
scripting. Embedding / scripting (within a larger program) is the original
use-case of Lua, not so Python.

I believe it's also much easier to secure / sandbox (remove bits you don't
want script writers to have access to) as well, the stdlib is smaller and I
think it has less interactions between modules.

Python embedding / scripting tends to be more common for software where the
securing / restriction aspect is smaller but flexibility & larger embeds are
necessary e.g. 3D, CAD and other "production pipeline" software tends to be
scripted with Python.

~~~
pansa2
> _I believe [Lua is] also much easier to secure / sandbox [than Python]_

Yes. C code that embeds the Lua VM has total control over which functions are
exposed to the Lua code. It's possible to create a VM that, for example, has
no access to the filesystem.

AFAIK it's not possible to do that if you embed Python. Dangerous functions
like `open` are always available and sandboxing facilities have to try and
prevent untrusted code from getting a reference to them. Unfortunately, there
are numerous ways to work around these restrictions and obtain access to
dangerous functions, e.g. via `__builtins__`.

Another reason running untrusted Lua code is considered fairly safe is that
the VM is well-engineered and has a history of very few bugs [0]. However, the
latest 5.4.0 release seems to have more bugs than older releases.

It might still possible for untrusted Lua code to use 100% CPU and hang a
program, or to read data from the embedding process using a Spectre-style
attack (although even that's unlikely because the Lua VM is an interpreter
rather than a JIT compiler). However, it's quite possible to secure an
embedded Lua VM to prevent it from doing things like accessing arbitrary
files.

[0] [https://www.lua.org/bugs.html](https://www.lua.org/bugs.html)

~~~
philsnow
> the Lua VM is an interpreter rather than a JIT compiler

There's also luajit, but I don't know how common it is vs "just Lua". My
impression was that luajit was used pretty often though.

~~~
pjmlp
LuaJIT is stuck in version 2.1.0, while Lua is at version 5.4.

~~~
masklinn
These versions have nothing to do with one another. LuaJIT 2.1.0 implements
Lua 5.1

------
minxomat
Here's an example web app hello-world in Fennel, parsing requests and
responding with HTML rendered from the S expressions:
[https://gist.github.com/turbo/4388c9ad19028560053951a25b3b45...](https://gist.github.com/turbo/4388c9ad19028560053951a25b3b45d1)

~~~
yawn
Thanks for posting this. I took a look at Fennel a few months ago and although
they list 2 web frameworks on the front page, there's little documentation
(that may have changed since then) about how to interface with other Lua
projects. I kept running into things I couldn't figure out when working with
the existing Lua ecosystem and projects.

~~~
erikcw
I’ve been using it quite successfully in a large OpenResty project.

While you’re right that there aren’t any docs on working with any of the 3rd
party projects listed in the homepage - I found that it was pretty straight
forward to use as a drop in Lua replacement by examining the compiled Lua from
the online REPL. I was surprised how clean the Lua output is. There is really
no magic here. I believe one of the project goals is to stay as close to Lua
semantics as possible. Basically just lisp flavored Lua.

In my project I just use the compiled .fnl files as I would any of other .lua
file.

Is something specific you’re getting stuck with? I’d be happy to try to point
you in the right direction.

It’s really nice! I encourage you to give it another try.

------
vsurabhi
This and a recent feature addition to neovim (passing lua closures to
vimscript functions [1]) should make a good substitute for elisp to configure
neovim.

[1]
[https://github.com/neovim/neovim/pull/12507](https://github.com/neovim/neovim/pull/12507)

~~~
adz5a
I don't see it mentioned but there is a neovim plugin called conjure [1] that
is written in fennel. This is a general purpose REPL client that works for
Clojure (I use it daily for Clojure and ClojureScript) but also for fennel
itself. So you can have a really nice neovim plugin dev experience using this
tool. Conjure works with fennel out of the box, but if you want to go deeper
you may be interested in another plugin called aniseed [2] which provides
interesting facilities for the fennel language and that is used internally by
conjure.

Disclaimer: I am not the author of those tools, just a random Clojure dev.

[1] [https://github.com/Olical/conjure](https://github.com/Olical/conjure)

[2] [https://github.com/Olical/aniseed](https://github.com/Olical/aniseed)

------
merricksb
If curious, see previous discussion from 2018

[https://news.ycombinator.com/item?id=18016168](https://news.ycombinator.com/item?id=18016168)

------
miguendes
Interesting project. I had never heard of a lisp flavour for lua before. Looks
very elegant.

It reminds me of hy, for python. [1] [1]
[https://github.com/hylang/hy](https://github.com/hylang/hy)

~~~
mumblemumble
I haven't spent any time with Fennel, but it seems more promising to me.

I couldn't get comfortable with Hy. It drops so many lispy things, such as let
statements and persistent data structures, in order to stay close to the
Python substrate. (You can get both from libraries, but at the cost of easy
interop with Python packages, which kind of defeats the whole point of being
on the Python platform in the first place.) It ultimately came out feeling
less like I was working with a lisp, and more like I was working with an
s-expression syntax for Python.

OTOH, if you think that Python, while otherwise great, is really suffering for
lack of defmacro, Hy absolutely succeeds in getting you Python with defmacro.

~~~
rcarmo
I ended up dropping Hy very soon after they dropped let. Had an entire blog
engine written in it:
[https://github.com/rcarmo/sushy](https://github.com/rcarmo/sushy)

...but I wanted Hy to be LISP, not a thin veneer over Python.

------
slumos
I wrote my simple Hammerspoon config in Fennel and am really happy with it.

[https://github.com/slumos/dot.hammerspoon](https://github.com/slumos/dot.hammerspoon)

------
molloy
A lot of the questions in this thread are answered here: [https://fennel-
lang.org/rationale](https://fennel-lang.org/rationale)

------
fortran77
The reason I used Lua was its "accessibility" \-- so end users can script --
and not necessarily for the beauty and elegance of the language. It was also
very easy to integrate into a C++ project.

------
galfarragem
AFAIK, for his author, Fennel is legacy. Nowadays he seems to contribute only
to Janet ([https://janet-lang.org](https://janet-lang.org)).

~~~
molloy
It's still under very active development by technomancy and others, with a
friendly community and lively IRC channel.

------
remix2000
In the realm of Lua Lisps, there is also Urn: [https://urn-
lang.com/](https://urn-lang.com/)

------
manexploitsman
Why do you need all of those brackets?

