Hacker News new | past | comments | ask | show | jobs | submit login
Fennel: A Practical Lisp (mattroelle.com)
261 points by mattroelle on April 14, 2022 | hide | past | favorite | 85 comments



Calvin rose, the author of fennel is a really great programmer and an even better language designer. Personally, i dont use or like lua, but I'm a big fan of one of his other projects, janet[0]

[0] https://github.com/janet-lang/janet


> i dont use or like lua, but I'm a big fan of one of his other projects, janet

As a big fan of both Lua _and_ Janet, I'll just point out that a lot of what makes Janet nice and interesting are in fact inspired by Lua. Tables, fibers (coroutines), prototypes (metatables/metamethods), custom module loaders, a lot of the API, the peg parser (LPEG), etc etc. I understand no language is for everyone, but the influence can be felt throughout Janet if you're familiar with both.


I’ll also add that Fennel fixes some of things about Lua most people dislike, like the global variables by default thing. IMO it’s worth checking out even if you didn’t like Lua at first


I didn't realize the author of Janet was the author of Fennel, thanks for making the connection.


Yup, same author! Although to my knowledge Calvin has mostly stepped away from the Fennel project at this point. It is primarily maintained by the same dev that made Clojure's Leiningen, technomancy.


I got nothing but respect for technomancy.


I love that it comes with build instructions for Haiku—and that they're listed before Windows!


I've been considering rewriting a bunch of my stuff in Fennel since it has two other advantages the article doesn't point out: both Lua and Fennel are _very_ stable and unlikely to go through major changes over, say, the next five years or so.

That kind of thing also has a lot of value when you only want to write something once.


Agreed, stability is a big plus. I recently decided to take the plunge and start learning Common Lisp, and now it's my favorite language despite some warts.

Being an ANSI standard with multiple cooperative implementations, it's unlikely to change any time soon.


That's a very good point. To add: stability and simplicity are choices. Compare Lua to JS for example which started out similarly but Lua was kept simple and stable, while JS introduced massive churn and tooling complexity, much of it being unnecessary in my eyes.

I don't use Lua anymore but it has a special place in my heart. I started learning it to use Love2d and then got my first job as a developer out of sheer luck (someone I knew saw a demo of the game I was working on).

Back then, I didn't understand why it is important that Lua is how it is. I just thought it was to make it more accessible, and while that is certainly a consideration, I think the bigger value proposition is trust and the ability to keep the whole language in one's head with very minimal effort.

Other languages that optimize for stability and simplicity are Clojure and Go. Their respective ideas of simplicity are different but have some overlap. Both languages are very attractive and deeply relaxing.


> Compare Lua to JS for example which started out similarly but Lua was kept simple and stable, while JS introduced massive churn and tooling complexity, much of it being unnecessary in my eyes.

Anything you wrote in JS twenty years ago probably still works. Tooling has changed, but none of that's an official part of the JS language. The language has only some added features that you can opt to not use.

If you're comparing the whole JS ecosystem to the Lua and Fennel languages, well that's not really a fair comparison.


For a solo developer, you can opt not to use any of the new features of JS.

But if you are working in industry and do some front-end programming… You are going to work with the features that other people around you have decided makes their life easier.

It is possible to work with like-minded souls that want to keep JS simple, but it certainly isn’t a given.


You’re right, the comparison is a bit unfair.

And there are examples in the JS ecosystem that prove me wrong to some extent like htmx, which is a small, simple library written in a straight forward style.

But it’s not entirely unfair either. Reading code is an important part of programming. Integration with libraries often too. You can’t just isolate yourself from churn that easily when everyone else doesn’t. Breaking changes, versioning issues, deprecation etc. happen regardless.


> To add: stability and simplicity are choices.

I thought Lua constantly broke compatibility between releases. Several posts about this on HN


Thank you for pointing this out. I haven’t touched Lua since a decade and assumed it stayed simple and stable as GP was implying. But the general point and the other examples are still valid IMO.


> Other languages that optimize for stability and simplicity are Clojure and Go.

If you have not tried Elixir I would say it also has these qualities. (In its own way.) It is very pleasant to use.


Fennel is really cool, and the fact that it builds on Lua—an already-popular language—is a huge win; Fennel gets access to the entire Lua ecosystem.

Importantly, Lua is pretty easy to embed in native applications. I haven’t found a Lisp or Scheme as easily-embeddable as Lua(+Fennel).

It’s also a good drop-in replacement for Lua. I write my Hammerspoon config in Fennel, and hopefully my Neovim config soon as well.


Chicken Scheme is embedded in C programs with one line.

https://wiki.call-cc.org/embedding

I've even embedded it in Objective-C programs running in iOS (could make OpenGLES calls in the repl and everything).

Full API spec:

https://wiki.call-cc.org/man/5/Embedding

FFI example:

https://wiki.call-cc.org/An%20extended%20FFI%20example

https://www.more-magic.net/posts/scheme-c-integration.html


Thanks for sharing this, I've bookmarked it to check out more later, but from the links you provided, it looks like Chicken Scheme is pretty friendly to use.


I used Chicken for several years in the past and looking at it again recently. Worked great under FreeBSD, Linux, has a large set of well-maintained extensions and has (mostly) excellent documentation. And IME Chicken developers were always willing to answer questions, easily reached on ICQ/mailing lists. That's probably still the case.


Chibi scheme is easily embeddable. Guile is a different beast to manage, but then you get a lot more power and speed. Not luaJIT speed, of course, but a lot faster than vanilla lua, and with proper threads as a bonus.

The others I haven't tried embedding, but I hear racket is really no fun to embed,but things might have changed with the chez transition.


Chibi Scheme, interesting. Also beautiful. Thank you.

Racket CS is also quite fast, comparable to Lua or faster (for numeric ops). It also uses rational numbers which makes it useful for exact numeric operations.


As a fellow hammerspoon user who is not smitten with Lua’s syntax, I would really appreciate seeing your config :-)


It’s pretty minimal right now as I’m in the process of porting over a larger config. Here’s what I have, although you might find it might be a bit underwhelming:

https://github.com/jonpalmisc/dotfiles/blob/master/hammerspo...


s7 Scheme is as easy to embed as Lua, you might like it. I used it for Scheme for Max and Scheme for Pd and it's been wonderful - it was the easiest to embed of a bunch of lisps I tried. It's not well known because it came out of the computer music world, but it has also been used in games and other media tools.

Janet is surprisingly similar to s7 in many ways. I've been keeping my eyes on it as well, I sure hope it continues! Schemes are so flexible that moving from one to another is just not that hard.


I haven't heard of S7 before—it looks like a great project as well. Thanks!


There's also Spacehammer[1] which is all written in Fennel. Having to write Lua would have kept me from Hammerspoon.

[1]: https://github.com/agzam/spacehammer


Side question for all of you who are saying “the Lua language is very stable”: do you mean you're staying on Lua 5.1/5.2? If so, is this driven by LuaJIT essentially freezing everything in place by grabbing so many users and libraries off the PUC-Rio implementation? If not, what counts as stable to you? Lua 5.3 and 5.4 both have dialect incompatibilities with prior versions.


I consider Lua 5.1/5.3/5.4 to be roughly their own language. Somewhat akin to a Python2 vs Python3. Completely manageable transition if you want, but best to stick to an implementation if you can. Within a platform, there are seemingly only ever bug fixes.

NeoVim[0] had a few words on this topic.

0: https://github.com/neovim/neovim/wiki/FAQ#why-lua-51-instead...


For anyone who doesn't want to follow the link, the practical gist in that case is indeed that LuaJIT wins and they don't care about later Luas. (Edited somewhat for wording post facto.)


See also:

Fennel – Lisp in Lua (2020, 112 comments) https://news.ycombinator.com/item?id=24390904

Fennel – Lisp in Lua (2018, 62 comments) https://news.ycombinator.com/item?id=18016168

Neovim Configuration and Plugins in Fennel Lisp (2019, 19 comments) https://news.ycombinator.com/item?id=21676606

Raymarching with Fennel and LÖVE (2019, 3 comments) https://news.ycombinator.com/item?id=24835766


I tried Moonscript (another lang that compiles to Lua). Unlike JS, it didn't seem like the Lua VM supported source-mapping, so if there was a runtime error in my code, it appeared as a gibberish Lua error and not a MoonScript error. Is the same true for Fennel?


Not sure exactly what you're looking for but it at least seems to do a good job of telling me the line in my .fnl file that has the error (though it also says "in function ?" despite being in a named function). Also, that's running through the Fennel interpreter rather than transpiling to Lua and then running it with Lua.


Fortunately the generated Lua code is pretty readable. I can't speak for a large project, but I'm usually able to find bugs by inspecting the generated Lua code, and I'm usually able to find and fix them in the Fennel source without difficulty.


I'm concerned about the bus factor of LuaJIT. If the advanced cyborg known as Mike Pall decides to quit (as has happened once already) then what happens? Vanilla Lua performance isn't *bad* by any means, but hardly remarkable compared to LuaJIT.

Fennel looks like a gorgeous lisp. I will probably play with it at some point.


You can continue to use the LuaJIT that has been released? The Lua language has been very stable and has little need to develop for developments sake.

There are a few things that might be nice to have, but few of them are worth more platform churn.


One RCE bug in a codebase only one person actually knows really well… recipe for disaster.

It could happen. I’ve been coding more than three decades and I’ve read some real horror stories over the years.

If I were a luajit user, I’d be interested in trying to raise funding to get some more eyes on the source. It’s an engineering marvel, that’s incontestable, but it needs more coverage in case something happens. Even just to make room whenever Mike decides to retire.

I thought cloudflare and Kong would kick back to the community more, but that seems not to be the case.


I love Lua and the Lua community, but as a native Windows user, it's historically been a nightmare to use. To this day, I have never gotten LuaRocks to work reliably on Windows.

This is very different than Node + NPM, which you can get set up and going in <5 minutes.


I've had issues with LuaRocks on pretty much all platforms. Some libs work fine (at least those without native deps) but the Luarocks native builds mostly lean on handcrafted makefiles. They usually come with hardcoded paths for libraries which is extremely fragile if you are using some other flavor of *nix than the maintainer.

If you need a solid library ecosystem Lua just isn't the best choice.


So, another advantage, then?


I'm not sure I follow; it's an advantage that it doesn't work well on Windows?


Might help encourage you to move away from Windows.


I have plenty of reasons to move away from Windows, and also plenty of reasons to stay. :) But I think it's overall a net-negative that there is no simple way to use LuaRocks on Windows. I don't believe in gatekeeping, especially when so many programmers still begin their journey on Windows.


It’s not gatekeeping. Maintaining for Windows is a huge cost to damn near everyone involved.


Perhaps, but the majority of PC users are on Windows, so it's a bit cheeky to imply not having Windows support is an advantage.


Thanks I will give this a go. I had a long fling with Lua a while back. Wrote everything in Lua -- it is a great language. I would have used it for data science if only it had more well maintained packages. The main API for Torch was in Lua for a while before they abandoned it for Python. Had that carried on we could be using Torch with Fennel right now.


Not sure when you used Lua but there is a package manager called LuaRocks now which has some pretty nice packages in it for many purposes. I don't think it'd be too hard to get Lua running inside of a Python env either :)

https://luarocks.org/


It's really pretty rough compared to everything else I've used. I really don't feel like luarocks solves very many of my problems. A lot of gaps in there, or libraries that only target 5.1, or only luajit. Or are completely unmaintained, or have poorly documented dependencies on C libraries. etc etc.

I use lua a lot and like it fine but the build/dependency/tooling situation is a fucking mess. It exposes you to a lot of the inconvenience of C in the end.


Yeah, my experience is limited mostly to web dev, via OpenResty, and game dev, via love2d.

The Lua libraries in these areas are very well maintained.

That said 90% of the time libraries ship as a single lua file and it’s often better to copy and paste it into your project. I have a modified version of lume I like to use floating around :)

This spartan approach to libraries is a strength of Lua.


Furthermore, you are likely to encounter three different libraries that partially implement your desired feature set.


If you're interested in discussing further, there is a growing community that hangs out on #fennel on the Libera Chat IRC server. You can connect via matrix chat using https://matrix.to/#/!rnpLWzzTijEUDhhtjW:matrix.org I'll be hanging out there all day today and I'd love to talk about Fennel. There is also a Fennel User Group meeting this Saturday at 10am, open to all. More info at https://fennel-lang.org/events


The thing I hate the most about Fennel is when you go back to Clojure and type `print` rather than `println`.


  (ns foo.bar
    (:refer-clojure :exclude [print] :rename {print println}))

  (print "Should have a newline")


;; How to infuriate your co-workers


Workaround:

    print C-x a - println
    M-x abbrev-mode


Not all clojurians use emacs ;)


Well, configuring their editor of choice to provide the same functionality is left as an exercise to the reader... :-)


    inoreabbrev print println


Fennel also would benefit from something like the Clojure "discard form", in my opinion.


I have an Objective-C/Cocoa project that uses Javascript as it’s embedded language. That’s because the bridge is right there and makes interacting with Objective-C objects trivial.

That said I dislike JS as a language and the environment is restrictive. The only way to support debugging is console messages or through Safari web inspector. I’d love to do better.

How would something like Fennel work in this context as a replacement for JS & the bridge?


You could have a look at Hammerspoon as an example of a Cocoa app that embeds/bridges to Lua.


Useful suggestion, thank you.


Lisps are great because of their radical simplicity which saves you a ton time learning syntax.

You're going to need that time to invest in switching to Emacs.


Not anymore! If you use Neovim, you can use Conjure: https://conjure.fun/


That looks pretty cool. When I want to fiddle with things, might even use that to replace my setup around vim-slime + other minor plugins https://gist.github.com/mhitza/a00d7900571e9f13bac2bbf4a203d... (example setup for Guile, but I use the same thing for Chicken). Hopefully it's not Neovim exclusive!


(Edited for correctness)

Conjure itself is definitely Neovim-exclusive and heavily uses Neovim Lua APIs.

However I think several of the REPL "server" implementations use various independent protocols. Clojure uses nREPL, Common Lisp uses SWANK, etc. So maybe some of that logic (or even the Lua code) can be reused and ported to other editors.

As much as I love Jupyter, the system of starting and connecting to kernels is kind of a pain, and I believe implementing a proper Jupyter client is nontrivial (and often involves calling out to the reference implementation in Python). It is an "N+1" tool that solves the "NxM problem" (like LSP, DAP, and Tree Sitter), but it doesn't have the same adoption as those other tools.


Fennel/Lua was the best option few years ago when I wanted to embed a lispy scripting language in another app. All of the alternatives failed on requirements like not having any global state, ability to replace memory allocators, proper MSVC support and so on.


What kind of possibilities are there for Lua on android and ios app development? Any experiences there?


Love2d, Pico-8, Solar2d (formerly Corona), Cocos2d, Raylib, Defold, Godot to name a few. Here's a few more with a break down[1]

[1]: https://gamefromscratch.com/lua-game-engines/


Curious how this compares to Guile.

Guile is designed for embedding. But it is vanilla Scheme, which might not be as modern as the Fennel flavor.

Maybe, the Fennel all gets lowered to Lua at build time, so you don't need a (maybe more expensive) Guile runtime? Or, Lua has a more comprehensive library ecosystem?


If I recall correctly, you can do both. You can compile your fennel code to lua, or you can use fennel as a library in your lua application so it will understand your fennel code.


Correct, it's all converted to Lua, Fennel has no runtime.


So, I don't exactly see how one uses this in an already-embedded Lua. For example, my son knows Lisp, and a bit of Lua because he uses PowderToy, which (to your point) has built-in Lua. So if he wanted to program his Lua extensions to PowerToy in Lisp (Fennel) instead of Lua, how would he go about it, exactly? Compile outside of PT from Lisp to Lua and then load the Lua code into PT, or, run the Fennel Lua code on PT's internal Lua? Either seems clunky. I skimmed the online doc, and there may be someplace that gives guidance on this, but I didn't see it right away. If you can point it out (or, if it doesn't exist, perhaps explain a bit) that would be great. Thanks!


Hi there! Once you have lua running somewhere you're 90% of the way there. You can do AOT compilation which will output lua code via.

fennel -c my-fennel-code.fnl

Or you can use the lua package api to automatically load .fnl files via require seamlessly:

local fennel = require("fennel") table.insert(package.loaders or package.searchers, fennel.searcher)

Enjoy!


The primary maintainer of Fennel, Phil Hagelberg, has put together some brief instructions on how to get Fennel running inside of Powder Toy for you :) https://p.hagelb.org/hello-powdertoy.fnl.html


It seems those are indeed the two options. This link gives details on how to perform either or the other.

https://fennel-lang.org/setup#embedding-fennel


What might be a good toy project to try with this? I've had a vague interest in lisp for a while, and fennel seems like a good entry point, I just need some direction on a small project that would be a good fit for the language.


The cookbook[0] has some good examples, I went with the state machine parser as a base for highlighting in mpv's console (mpv scripting has been the main focus of my fennel use, mostly rewriting existing code)

[0] https://github.com/bakpakin/Fennel/wiki/Cookbook


Thanks, Life w/TIC-80 seems like a good candidate. Simple enough that I can wrap my head around the whole thing and tweak the rules, even though it's (to me) an alien syntax and structure.


IRC bot! You just need to pull in a lua sockets library to get going.


I have been meaning to automate my spamming of random channels, doing it manually is a PITA. I like to keep things more innane and less troll/toxic though, so I might need to do some work with GPT-3 on the actual content. At least once I find a good corpus of low-effort shit posts that aren't also toxic.


Anyone tried Fennel -> Lua -> nelua compiler -> c/native?


You don't need to as `fennel` the compiler comes with it's own way to compile native binaries! Here's an example[0] from one of the maintainers. It even let's you require other Lua modules and will embed along with the executable.

[0]: https://git.sr.ht/~technomancy/fennel-http-example


Debugging seems like it would quickly become impossible, but that is a delightfully cursed idea.


Out of curiosity has anyone tried using Fennel in LuaLaTeX?




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: