Why Fennel?
fennel-lang.org
fennel-lang.org
The decision to strictly adhere to lua runtime semantics was initially repulsive to me, but I've come to appreciate the incredible wisdom of that approach. The practical experience of using lua is that because of the constraints of the language, every lua runtime is in some ways custom & unique. This decision of fennel's creator allows you to drop fennel onto any lua codebase, of any version, with any amount of custom freak shit grafted in, and just use it. Not only use it, but hook its compiler into the lua module loader system, and freely & transparently mix functions and tables between the two languages. A life raft in decrepit legacy lua codebases.
The restraint and technical focus of this language led to me checking out janet, another project of its creator. I've also come to really like that language, it makes some similarly minor-seeming but brilliant decisions like including PEGs in the core lang instead of regex.
Anyway try fennel, if you have to interact with a lot of lua pattern match alone will improve your life. Try janet too it's cool.
https://github.com/Grazfather/dotfiles/blob/master/nvim/fnl/...
https://github.com/kbd/setup/blob/master/HOME/.hammerspoon/i...
Thanks for sharing your setup, I’ll be stealing some ideas.
I always thought of emacs as that, i.e. an editor that is extensible and dev friendly (in contrast to vimscript). I don’t know much about emacs though, so this got me curious.
From the GNU Emacs homepage:
At its core is an interpreter for Emacs Lisp, a dialect of the Lisp programming language with extensions to support text editing.
It's not an editor with a lispy config language, it's a Lispy interpreter that comes with an editor that can configure it via said Lisp.
It's kinda like one of those Smalltalk VMs where the line is blurred between the code you're writing and the environment that it runs in.
Emacs can do basically anything you write an extension for it to do. It can be your calendar, your email client, your rss reader or even your git gui on top of being an editor. Emacs can do so much that it's daunting to start using.
Vim can do all of those things.
You can build a graphics editor in the browser, but it probably will never reach the level of Photoshop. Emacs can emulate Vim to a fairly good extent. However, I don't think Vim can ever surpass Emacs in terms of power and extensibility. It's simply the way Emacs is built. I love Vim, and I am a die-hard Vimmer. But I have to admit there are things that work in Emacs in a way that no other editor, whether it's Vim, VSCode, or anything else, does better.
A boon is a good thing. I think you meant a problem?
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 – Lisp in Lua - https://news.ycombinator.com/item?id=24390904 - Sept 2020 (112 comments)
Fennel – Lisp in Lua - https://news.ycombinator.com/item?id=18016168 - Sept 2018 (62 comments)
There was s little discussion here ~18 months ago³, but it will feel largely circular if you look at it as people are suggesting fennel there ;)
¹ https://github.com/pigpigyyy/Yuescript
² This probably only matters if you have tonnes of moonscript, not just a little neovim/mpv/awesomewm config or something.
I don't understand this; why is this a speed consideration?
Fennel does support arity checks but it comes with a runtime cost since it's implemented as a separate Lua call after being transpiled from Fennel.
(fn add-1 [x] (+ x 1))
(lambda add-2 [x] (+ x 2))
transpiles to the following lua: local function add_1(x)
return (x + 1)
end
local function add_2(x)
_G.assert((nil ~= x), "Missing argument x on /home/sullyj3/tmp/fn-vs-lambda/fnl/x.fnl:3")
return (x + 2)
end
return add_2Unfortunately, I can't find that blog post so take my words with a grain of salt.
If I remember correctly, Lua is a stack-based VM. What this means is that every piece of data has a corresponding location on a stack data structure in-memory. Function arguments are pushed into the stack as-needed and popped in the function context, and vice-versa for the function return values.
If you wanted arity-checking in this context, you'd have to confirm that you got exactly the right number of elements on the stack, meaning there would be an extra branch in every function call. This might reduce performance if the branch predictor gets it wrong. Plus there would need to be extra instructions to count and to check the count for each pop off the stack in the context of the function call.
This is from the perspective of calling C functions in Lua, so it's probably more complicated for the VM itself when it's running native Lua code.
This Fennel:
(fn foo [x y ...]
(print x y ...))
(lambda bar [x ?y ...]
(print x ?y ...))
...transpiles to this Lua: local function foo(x, y, ...)
return print(x, y, ...)
end
local function bar(x, _3fy, ...)
_G.assert((nil ~= x), "Missing argument x on test.fnl:3")
return print(x, _3fy, ...)
end
[1] Fennel's alternative `& xs` vargs format does create a runtime check in functions declared with lambda, but it always evaluates to true because it simply nil-checks the table (here `xs`) that it dumps Lua `...` vargs into.Maybe some other OF can confirm/contradict?
This was also used to implement variadic functions like printf before varargs, see https://retrocomputing.stackexchange.com/a/20517
int foo() {
return 0;
}
int main(int argc, char* argv[]) {
return foo("alpha", "beta", 1);
}
$ gcc-13 -Wall -Werror -o foo foo.c
# oh, sads
$ clang -o foo ./foo.c
./foo.c:5:34: warning: too many arguments in call to 'foo'
return foo("alpha", "beta", 1);
~~~ ^
./foo.c:5:15: warning: passing arguments to 'foo' without a prototype is deprecated in all versions of C and is not supported in C2x [-Wdeprecated-non-prototype]
return foo("alpha", "beta", 1);
^
2 warnings generated.From the blurb you quoted, it sounds like Lua doesn't check function argument arity natively, and fennel can. This means that fennel applies a runtime check to validate if a function was called with an expected arity, and if not, propagates an error (however Lua does that, I do not know), hence why arity checked functions are not as performant.
function M(...)
if select("#",...)>3 then
error("failed arity check on M()")
end
endThe Lua VM that they compile to already handles missing arguments with some attendant but unavoidable performance cost.
Fennel's own `lambda` form that they layer on top does check for missing arguments, but it must do so by generating extra Lua code to do those checks. That additional code has a runtime cost.
Using `fn` avoids that extra generated code and its cost.
If Fennel had its own runtime and VM, then the performance story for how missing arguments are handled would be different.
Still has a smaller community though and less appeal in the indie game scene
it's only a benefit for writing simplistic things (and configs for some complicated programs like a text editor or a terminal or some MMORPG is already not simple), otherwise you start appreciating that there is not just a single dictionary as a data type etc.
Considering the common role Lua serves, in most cases where I won't be even in a position to install anything besides the single source file I can distribute.
All I want is some quality of life improvements (that includes types). Write in that, then get a single Lua file as output. Nim does that with its JS-backend beautifully.
I forgot if Fennel can do that (seems that's what it means by Ahead-Of-Time compilation), but it's not typed so I passed on it. I might reconsider, as I gave up on the search last time.
Oh... not the plant...
[1]: https://www.foodrepublic.com/recipes/roasted-chicken-fennel/
not to detract attention from this cool project in the least.
The fact that indentation (whether significant to syntax or not)helps readability is something I hope I need not demonstrate since indenting is near universal in languages that use explicit delimiters.
So beginners learning a language with delimiters need to learn both delimiters and indentation, and then learn that while the latter helps them more, it's actually just the former that is required.
For beginners, mental load is death by a thousand cuts, so it may make more of a difference then an experienced programmer thinks.
[Edit]
My first reaction was just the benefits for beginners of indentation e syntax. I personally have a subjective feeling of advantages for non beginners to delimiter based syntax (I use common lisp regularly), but I have fairly low confidence in that, while I'm pretty sure of the advantages to beginners for indentation.
Some folks like parenthesis delimiters and functional programming. It structures and separates things out clearly in their mind. And when you introduce them to editing tricks like parinfer - they fall in love. The guy whom I introduced Clojure to knows it far better than me now.
Some reasonable ones: https://wiki.python.org/moin/Powerful%20Python%20One-Liners
Some less so: https://hostcloud.blog/is-it-possible-to-write-obfuscated-on...
That seems strange to me. A range is a range so why have different ways to iterate over ranges based on what type of range it is?
>This feature is intentionally listed last because while lisp programmers have historically made a big deal about how powerful it is, it is relatively rare to encounter situations where such a powerful construct is justified.
In other words, there are serious drawbacks to using macros that are usually outweigh any benefits they might give you.
Tools have their strengths. Use them for that. Don't try to drive a nail with a screwdriver.
lua and javascript are somewhat interchangeable aside from the fact that js has a much larger ecosystem.
sure there are situations where lua might be a bad choice but based on your remarks you make it sound like it sucks anywhere except inside a game engine.
Show me an echo server. Show me how you open a file and read data. Show me SOMETHING. Right on the landing page. How are we still doing this?
EDIT: I stand corrected - this is but a subpage. The actual landing page apparently DOES have code.