Fennel is a lispy language that compiles to, maintains the semantics of, and seamlessly interacts with Lua! It’s a fun little language to mess around in, and does a good job showing off some of Lua’s Scheme heritage, while also using some compile-time features to smooth out lua’s rough edges (checking use of undefined variables, macros, local variables by default…)
Check it out and celebrate 1.0 with us!
Fennel lead developer here.
I’ve posted about Fennel a few times in the past (https://lobste.rs/s/11p7mn/fennel_0_2_0_released https://lobste.rs/s/6bphbw/fennel_programming_language_rationale https://lobste.rs/s/hppjix/making_fennel_compiler_self_hosting_with) on this site for previous milestones and have had some great discussion ensue; feel free to ask me anything!
Congratulations on the milestone! I’ve only shipped plain-old-lua so far, but I’ve been smiling as I watched Fennel get better for a while now. Nicely done. And I look forward to the game jam where using Fennel turns out to be a good idea for me :)
Thanks! Of course you can use Fennel for any game jam, but the big ones are the Lisp Game Jams held every April and October on itch.io: https://itch.io/jam/autumn-lisp-game-jam-2021 / https://itch.io/jam/spring-lisp-game-jam-2021
I was just talking to a friend of mine who is a game dev and used to use Lua a lot when he worked in cryengine. He complained about global variables in Lua and how Lua can return null values when a value isn’t present in a collection. I had brought up Fennel, but I wasn’t sure if it tackled either of those issues. I know that Fennel is semantically very similar to Lua. Does it provide any protections against either of those types of bugs?
On global variables: in fennel, only names bound with
(global ...)produce global variables, all other binding mechanisms (pattern matching,
local) all produce local variables. Very hard to accidentally make a global variable :P
nilin tables, not directly. You’ll still get
nilindexing into a table where there’s no value, but there is a macro like this:
(?. tbl :foo :bar :baz)that will
nil-check each index and short-circuit so you don’t accidentally index into
nil, producing an error. You still have to
nil-check individual indexing yourself though.
Glad to hear that variables don’t default to globals!
This is honestly kind of an odd complaint since it’s also true of nearly every other dynamic language out there. (Racket and Erlang being the main exceptions I know of.) Lua’s behavior (and thus Fennel’s too) is sublty different in that nil is much more consistently used to represent the absence of data, rather than other languages where you can have absurd situations like “I put nothing in this list and now the list is longer than it used to be”. In Fennel/Lua there is no difference between
[nil]. This takes some adjusting to if you are used to the other behavior but once you learn it, it’s a lot less error-prone.
Fennel has a few advantages over Lua that make it easier to deal with nils when they do occur. The other comment mentions the nil-safe deep accessor macro
?.but there is also automatic nil-checks for function arguments with
lambdaas well as the fact that the pattern matching system defaults to including nil-checks in every pattern.
They’re primarily working in statically typed languages (C++, C#) so it’s not totally a dig against Lua. Just normal workflow mismatch vs dynamically typed paradigm. Also, it was in the context of video game designers using scripting languages (e.g., Blueprint), getting out of their depth, and losing a couple days on bugs.
Python also throws when you try to access a value that isn’t present in a dictionary. Always reminds me of this blog post:
That is interesting! I didn’t know it did that.
Thanks for taking the time to respond! I don’t really work in the video game space, but fennel seems like an interesting choice for video games. Piggy backing off of Lua for embedding in game engines and using macros to create DSLs seems like an interesting niche.
E.g., I could imagine game designers editing rules in a minikanren DSL to affect changes in the game world.
Haha; yeah can’t believe I forgot about Python. I listed Erlang and Racket there because they specifically do not even have a notion of nil/null in the entire language, whereas Python somehow manages to include null in their semantics, but not actually use it for this.
On the other hand I’m actually somewhat sympathetic to this because nils are the most common type error in my experience by at least an order of magnitude, so I would characterize it differently. To me it’s more like you ask Ruby (or Clojure, or JS, or whatever) to go to the store to get some milk, it comes back, walks over to you, says “here’s what I got you”, holds out its empty hand, and drops nothing into your hand.
If a human did that it would be considered extremely disrespectful or overly sarcastic! Throwing an exception is indeed an overreaction, but there needs to be some middle ground where you don’t just pretend that the operation was successful, and most dynamic languages simply don’t have the ability to convey this. https://blog.janestreet.com/making-something-out-of-nothing-or-why-none-is-better-than-nan-and-null/
edit: the reason this works better in Erlang than other dynamic languages is that pattern matching is baked into the core of the language so it’s easy to represent success as a tuple of
[ok, Value]which encourages you to match every time you have an operation which might in other languages return nil. Fennel has a pattern matching system which was in many ways inspired by Erlang.
That’s great to hear! I haven’t used Erlang, but I’ve used a bit of Elixir and I really love that design. Jose Valim described assertive code in an article a while back and I appreciate that perspective. That’s another reason why I like python burning down the store so to speak. Having the option to say “hey, I really mean it this time” is always useful.
I like how Fennel is a love letter to Lua, highlights Lua’s semantics and wisdom, while bringing the homoiconic clarity of Lisp. ♥
Nice, seems that I need to update my NeoVim config to match newly released version.
I really <3 this project.
I just took a look at this finally, I’ve seen the posts a few times and knew it was a good project but I am still more impressed than I expected to be! There is no doubt that I will use this in the future, maybe I’ll even slip back into neovim if I like it a lot xD