The Fennel Programming Language
fennel-lang.org
fennel-lang.org
Lua is beloved and I have a ton of respect for that project in itself, it's definitely one of the seven wonders of the programming world. But the (well-considered) compromises for its intended use as an embedded scripting language make it pretty rough for large complex projects.
Fennel is just an extremely focused aid to some of the worst lua warts. Pattern matching alone is incredible for the overloaded tables lua uses. The macro system is excellent, and since a lot of the time what you're doing with lua is defining a DSL anyway it gives you more powerful tools for that.
I initially recoiled but I now think it was a genius decision to do it with just a few special forms over the lua semantics. Other than the lisp syntax there's very little new to learn to use it. And the best part is that it hooks into the lua module loader system so you can freely mix tables and functions between the two, a life changer in legacy codebases.
Can't say enough good things about fennel. There are a lot of languages that I like but it's one of the only ones I think is actually good. I rarely write normal lua anymore it's just so flexible.
Not sure if the market for LUA has changed since then...
Still, it's a great language for scripting.
also, minor observation: https://lua.org/about.html#name
True, however it was later adapted and ported to a number of small architectures. NodeMCU is just the most popular one of them as the EluaProject started years before even the ESP8266 was available. https://eluaproject.net/
Why Fennel? - https://news.ycombinator.com/item?id=37497131 - Sept 2023 (102 comments)
Language Showcase: Fennel - https://news.ycombinator.com/item?id=32349491 - Aug 2022 (2 comments)
Fennel: A Practical Lisp - https://news.ycombinator.com/item?id=31029478 - April 2022 (85 comments)
Fennel: A Practical Lisp - https://news.ycombinator.com/item?id=30963628 - April 2022 (2 comments)
Fennel 1.0.0 Released - https://news.ycombinator.com/item?id=29221245 - Nov 2021 (1 comment)
Fennel 0.8.0 Released - https://news.ycombinator.com/item?id=25842797 - Jan 2021 (1 comment)
Raymarching with Fennel and LÖVE - https://news.ycombinator.com/item?id=24835766 - Oct 2020 (3 comments)
Fennel – Lisp in Lua - https://news.ycombinator.com/item?id=24390904 - Sept 2020 (112 comments)
Neovim Configuration and Plugins in Fennel Lisp - https://news.ycombinator.com/item?id=21676606 - Dec 2019 (19 comments)
Fennel – Lisp in Lua - https://news.ycombinator.com/item?id=18016168 - Sept 2018 (62 comments)
https://wiki.fennel-lang.org/Fennel-in-LuaTeX
but no activity for this on tex.stackexchange (yet?) --- given the difficulties in doing recursion (see: https://tex.stackexchange.com/questions/723897/breaking-out-... ) using something like to Lisp could potentially be very helpful.
It sits at 99% forever.
This is presumably https://github.com/fengari-lua/fengari/issues/147. You can workaround it by substituting "%.f" for "%.0f" in https://fennel-lang.org/fennel/fennel.lua.
However Chrome won't let me override the contents of this URL for some reason. And the first Firefox response override extension I tried ended up confusing the page. So you can alternatively override the contents https://fennel-lang.org/fengari-web.js as follows:
`"string" == typeof o.response ? a.f = yt(o.response.replace("%.f","%.0f")) : a.f = new Uint8Array(o.response.replace("%.f","%.0f"));`
(Just before `var l = Ee(a);`)
Opened a bug for it: https://github.com/bakpakin/Fennel/issues/485
> I've turned the web repl back to 1.5.0 for now while we investigate the issue.[1]
[1] https://github.com/bakpakin/Fennel/issues/485#issuecomment-2...
If you want to experiment with something like Dwarf Fortress, you don't need 2d graphics. You can just start with ASCII-graphics like Dwarf Fortress did.
If you're set on Python there is PyGame. I haven't touched it for a number of years but back then it seemed less polished and waaay less performant than Löve.
Didn't try it myself though.
I’ve found the neovim ecosystem to churn way more in recent years than it did when I initially started using it 8 years ago. It really reminds me of the JS ecosystem of the past decade: full featured plugin that works great decides to strip things back so that it’s functionality is all plugable and you suddenly have to wade through 2 major release’s migration docs to get things kind of working like they were before. I digress to point out why it might be hard to have yet another layer to translate through. Though if you don’t have kids, I’d say “go for it!”
Fennel solves some Lua kirks, so I think that is a good use case, since Fennel has some cool features that can help to maintain the code. Right now I am moving the plugins that I maintain to Fennel.
If interested take a look on the https://github.com/Olical/nfnl and https://github.com/gpanders/fennel-repl.nvim plugins.
It’s a convenient way to access the immense power of SQLite, with the succinctness of a lisp-like syntax. Throw in tests that execute at 350 tests per second and it’s a fast, stable development platform.
Right now we’re building a wrapper to display graphics sufficient for simple puzzle games so we can generate the layouts and handle interactions also in Fennel (by mapping to native iOS and Android views). It also does hot reloading in the iOS simulator. Very much a WIP though. We haven’t shipped this part yet.
Fun times. Stable. Small. Uncomplicated. Great community.
The performant APIs that the Python wrappers call are powerful but it might be a little cumbersome to use. A library I used a lot in university was the OpenCV through the native C++ APIs, the Java API and the Python API; the Python version felt a lot easier to work with.
For internal use, you give the game designers a higher-level and easier-to-use language with quick feedback (no recompiling your game just to change some logic). For modders, it lets you sandbox their contributions to a specific API, but let them implement arbitrary logic inside of it. This lets users install mods from untrusted sources with confidence that it's not going to crash their computer, delete their data or install a keylogger.
* You'd have to remember to free heap allocations in C. Lua does it for you.
* You might want to always access a particular object's field through a function rather than directly. If you programmed in C it's your responsibility to not mess this up, you won't get any warnings or errors if you forget. A scripting language could automatically call the function when you try to access the field.
* You might want to ensure that any switch case over a enum always considers all possible values of the enum. C wouldn't help you here, but Fennel's infrastructure for pattern matching would.
What's the reason for (/ num1 num2) rather than just num1/num2 for division for example?
In C++, there's 17 levels of precedence across 60 operators, with varying associativity.
In many languages, due to precedence, 1 + 2 * 3 = 7, but in others, like Smalltalk, it equals 9. In those languages, its purely left to right, with no precedence. But at a casual glance, they look identical. And for someone bouncing back and forth, they better ensure they have the right hat on when they start writing out equations.
In prefix languages, this is not an issue. Everything looks like a function.
+, -, sqrt, sin, draw.
You also can have niceties like (+ 1 2 3 4), which simply adds up all of the arguments, in contrast to (+ 1 (+ 2 (+ 3 4))). But, that's not different grammatically from (sin (sqrt (abs x))), (f1 (f2 (f3 x))), which appears like sin(sqrt(abs(x))) in infix languages, not much different really at all.
Clearly there are benefits to infix presentation, and for basic (or, even BASIC!) use its very approachable.
But underlying it, there's some definite complexity that you need to grok when the equations start to get complicated.
For example many lisps implement SRFI 105: Curly Infix https://srfi.schemers.org/srfi-105/ which lets you write {a * b + c / d} instead of (+ (* a b) (/ c d)).
Afaik Fennel doesn't have this, but that's fine because it has macros!
(macro infix [expr]
(let [stack []
ops {
"+" (fn [a b] (+ a b))
"-" (fn [a b] (- a b))
"*" (fn [a b] (* a b))
"/" (fn [a b] (/ a b))}]
(fn [item]
(if (number? item)
(table.insert stack item)
(let [op (get ops item)
b (table.remove stack -1)
a (table.remove stack -1)]
(table.insert stack (op a b)))))
(each [_ v (ipairs expr)]
(infix v))
(first stack)))
And now you can do: (infix [1 + 2 * 3 - 4 / 5])
; => 5.2 1 + (2 * 3) - (4 / 5) = 6.2 (PEMDAS)
1 + (2 * (3 - (4 / 5))) = 5.4 (greedy)
(((1+2) * 3) - 4) / 5 = 0.4 (non-greedy)
Was it a typo or is it using some other grouping I don't understand?Unfortunately, no such thing seems to exist.
But that said, my random forays into Guile have been met with frustration. I can’t say anything specific, but for whatever reason, whenever I tried to use it, I’d get no momentum on my little project du jour, and I’d shelve it once again, moving on to something else.
Perhaps next time it’ll click.
__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
Plus, the LGPL license will ensure that it can't be shipped on a multitude of platforms (iOS I believe, game consoles, etc.)Just for my understanding, is this based on the fact that these platforms don't allow the user to modify or upgrade Guile to another version in the future?
I don't think there's any restrictions on LGPL code for the App Store, but the restrictions I mentioned earlier are probably tricky to conform to[1].
[1]: https://stackoverflow.com/questions/35068054/does-app-store-...
Hundreds of software projects today contain the mysterious line
__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
as a lasting memory of this glibc debacle. I just added it to my code as well."""
Also Guile (and many other embeddable scripting languages) want to initialize global state which I'd prefer not to.
Fennel has something a bit more robust (assert-repl) and it can restart, but can only return a single value from the REPL, which often creates more errors downstream.
I thought I remembered being able to manipulate the env as a dynamic which I think could get you what you need. But reading the source now I guess not? I'm pretty out of practice reading janet.
The only real issue with Fennel is luarocks—the experience of adding new libraries (or general dependency management) to a (Lua) project is still gnarly.
It's R7RS scheme, which is a very small, well designed core for lisp. 13k LOCs of C. Very easy to embed, and a nice C FFI. I absolutely love it.
Geiser supports Chibi scheme out of the box, and I wrote this simple nREPL for it that I use: https://github.com/Risto-Stevcev/chibi-repl-server/
I use delimited continuations for conditions and restarts: https://gist.github.com/Risto-Stevcev/e6fd8417e34ef74a74adfa...
Most recent release was May of this year. I don’t know if it is as portable as Lua, but it should basically run anywhere you can call into C.
You’d have to import a third-party hygienic CL macro system, though. But in return you get a full-fledged Lisp.