(评论)
(comments)

原始链接: https://news.ycombinator.com/item?id=43673551

这个Hacker News帖子讨论了Fennel,一种编译成Lua的Lisp语法语言。帖子里辩论了Lisp类语言的吸引力,一些人认为其过多的括号语法很繁琐,而另一些人则重视操作S-表达式的便捷性和宏的强大功能。一位评论者甚至开玩笑地反驳了这一论点,来为Lisp类语言辩护。 几位用户分享了他们使用Fennel的积极经验,尤其是在Neovim配置和游戏开发(Love2D、TIC-80、PICO-8)方面,强调了其函数式特性,例如模式匹配。一些人谨慎地指出,对于简单的配置,Fennel可能显得大材小用,并且其工具落后于Lua。人们也担忧其缺乏静态类型支持以及在沙盒Lua环境中可能出现的问题。一些用户建议使用其他类似Lisp的语言,例如Janet、LunarML、Hylang和LFE。总的来说,讨论反映了人们对Fennel优势的热情以及对其可用性和生态系统的务实考虑。

相关文章
  • 为什么选择茴香? 2025-04-14
  • (评论) 2025-04-11
  • 我为什么用 Lisp 编程 2025-04-11
  • Lisp:糖霜还是蛋糕? 2024-06-03
  • (评论) 2024-04-22

  • 原文
    Hacker News new | past | comments | ask | show | jobs | submit login
    Why Fennel? (fennel-lang.org)
    192 points by behnamoh 10 hours ago | hide | past | favorite | 79 comments










    I do not understand the appeal of LISPy languages. I get that the parser is simple and elegant, but I believe the developer (of the compiler in this case) should serve the convenience of the user, not the other way around.

    Writing code like this is cumbersome and unnecessarily symbol heavy, and reading it isn't really nice as well.

    I'd rather have the language add that extra complexity into the parser than have me stare down these endless paretheses. Parsing something C-like is not that, hard, trust me, I've done it



    Focusing on the runtime's parser is a red herring and I think a common error in lisp advocacy.

    Even if I didn't the full power of a lisp macro system, it is an absolute joy to manipulate programs written in s-expressions. Being able to cut/copy/paste/jump-[forward/back] by sexpr is really convenient, and often done nowhere near as well in other languages. I think this is because until the invention of tree-sitter and LSPs (and the former isn't yet widely adopted in editor tech), most editors had regex-based syntax highlighting and some kind of ad-hoc "parser" for a language. This makes them less aware of the language the developer is editing, but was probably a pragmatic design decision by editor implementers: it's easier than writing a full parser and means the editor can still assist even if a program is syntactically ill-formed.



    I do not understand the appeal of non-LISPy languages. I get that most people are used to reading it and that they are efficent, but I believe the developer (of the compiler in this case) should serve the convenience of the user, not the other way around.

    Writing code like this is combersome and unnecessarily symbol heavy, and reading it isn't really nice as well.

    I'd rather have the language add those extra parens into the parser than have me stare down these endless semi-colon, linebreaks or indentation. Parsing something Lisp-like is not that, hard, trust me, I've done it.



    This doesn't really resonate with people though, as most people are more familiar with C-style notation. Also:

    >Writing code like this is combersome and unnecessarily symbol heavy

    Does not make sense in this context, as it mainly applies to Lisp-like languages that uses parentheses heavily.



    Yeah was going to change that part too for something like "Writing code like this is verbose and spans too many lines", but then I just thought it'd be better if it sounded more like the parent comment.

    And I understand it doesn't resonate with most, I just wanted to highlight how the initial parent comment was very subjective and not very substantive, some people didn't take the joke so well, I guess it could've sounded a bit passive aggressive. I personally enjoy both C-like and Lisp-like syntaxes and languages, I do have a sweet spot for Forth tho.

    But back on topic, Fennel is a great language, working with Love and Fennel is really nice. And if the parentheses can seem off-putting for some, I'd highly encourage to give it a shot as you can quickly get past it and see how comfy it feels to have your statements properly demarkated.

    S-expr shine the most when working with XML-like structure. Spinneret[1] was the most fun I ever had working with HTML, every other templating engine feels subpar now that I have tasted that sweet nectar..

    [1] https://github.com/ruricolist/spinneret



    It's your comment that seems to add the least, because the majority agree with the OP. The point is that ergonomics is not as good, of course there are contrary opinions. The existence of a contrary and minority opinion doesn't detract from the point.


    >Does not make sense in this context, as it mainly applies to Lisp-like languages that uses parentheses heavily.

    I had read, some years back, that someone did an actual calculation / demonstration that showed that the number of symbol / punctuation characters in Lisp is actually less than in C-based languages, for a block of code with equal functionality in both languages.

    I don't have the reference handy. Someone here may know of it, and post it.



    I never got people’s objections to the parentheses when the bottom of most js/ts files is a soup of three different flavors of closing bracket. Even more “fun” in languages like PHP that make you sprinkle ceremonial semicolons into some parts to satisfy the compiler. Hunting for the right brace is tedious, whereas in a lisp I just bounce on close paren til the open paren highlight is where I want it.


    It's obvious to anyone who is familiar with both.

        (f x y)
    
    vs

        f(x, y);
    
    Note the extra comma and semicolon. The only place this breaks down is for simple arithmetic expressions like (+ a b) vs a + b, which is trivial enough to ignore (and also goes back in favor of Lisp when you start having more operands).


    Let's be real in most situations it doesn't matter. One "statement" per line is bog standard. Whether that statement is surrounded by parens or ended in a semicolon isn't impactful for reading.

    LISP is only better when you add source code transformation (which is way easier with its source code looking like the transformed code). But then you introduce "everyone can write their own syntax" which is good and bad given the history of DSLs...



    <3


    The appeal can be seen with paredit-style [1] editor plugins. They give you the power of working on trees rather than text. When you master the paredit way of editing you’ll wish you could do that with every language.

    [1] https://paredit.org/



    > I’d rather have the language …

    check out Lean 4 then. Its syntax system is based on Racket but —instead of parens— implements stuff like [JSX syntax](https://github.com/leanprover-community/ProofWidgets4/blob/d...) and a [maze](https://github.com/dwrensha/lean4-maze)



    Consider the following LISP function that performs a transformation of an MxN matrix:

    (defun transpose (matrix) (apply #'mapcar #'list matrix))

    Based on my own experience I think I can say that It isn't until one has acquired a reasonable amount of experience with the language that they can fully appreciate its power.



    Now try it with BQN


    The unmatched beauty of the Lisp is the elegance of writing code generators (macros).

    Code is list and main structure is list. This is genius.





    Are you interested in learning the appeal?


    i have never used a lisp, but i’d assume due to its focus on macros, you are alternately the developer of a compiler and the user of that compiler. so making it easy on the compiler dev makes it easy on you.


    The first thing that comes to mind is macros.


    I love seeing new languages targeting the Lua runtime. I've been adding Lua scripting support to pretty much everything I make now. I recently made my SSE server programmable with Lua and it's extended the functionality far beyond what I would have had the patience and time to do myself. Highly recommend Lua with mlua-rs Rust bindings.

    [0] https://tinysse.com

    [1] https://github.com/benwilber/tinysse

    [2] https://github.com/mlua-rs/mlua



    I don't have any use cases in mind right now, but this looks cool. You should try posting another Show HN.


    I would love to see a stripped back ML-style language that targets lua, just something like ocaml's type system and exhaustive pattern match right on top would be all I need. There have been a few attempts but nothing I know of that got usably far and is maintained.

    There might be a way to get standard ML to output lua or something but I'm not that familiar with it. I think it would be an incredible fit for a third backend for gleam, but they say they aren't adding any more beyond erlang and js.



    I think that LunarML [1] could fit this niche quite well. It's a StandardML compiler that targets Lua. I can't tell much more as I've only played with it a little and don't have much experience with ML languages in general, but it looks promising.

    [1] https://github.com/minoki/LunarML



    You might be able to repurpose CSharp.lua[1] to compile F#

    [1] https://github.com/yanghuan/CSharp.lua



    Fennel is nice. I converted my neovim config[1] to fennel and haven't looked back.

    [1]: https://github.com/Grazfather/dotfiles/blob/master/nvim/fnl/...



    Fennel is indeed nice and I rewrote my config in it too, but looked back ~2 years later and rewrote it again in Lua. I think Fennel for configuration is not justified and just adds complexity. Also the tools are not there: two existing language servers[1][2] can't compete with Sumneko's Lua language server[3] and they are fennel-exclusive and clueless about Lua code. I still like Fennel for writing more complicated code (my plugins: [4][5]) because of neat features like pattern matching and structural decomposition, both are surprisingly robust.

    [1]: https://git.sr.ht/~xerool/fennel-ls/

    [2]: https://github.com/rydesun/fennel-language-server

    [3]: https://github.com/LuaLS/lua-language-server

    [4]: https://gitlab.com/repetitivesin/16cm.nvim/-/tree/main

    [5]: https://gitlab.com/repetitivesin/madol.nvim



    I find most niche / less popular languages lack the developer tools ecosystem in ways that simply hinders adoption by more devs.

    A good and comprehensive LSP for example. It really needs to be on par with other languages to increase adoption



    I’ve been dipping my toe in Lua and found some ways to achieve pattern matching of sorts and there’s a package called Tamale too but I’m not sure if that is used much.




    If only there was an editor which could act as an interpreter for Lisp directly ...


    Wake me up when its authors realize most of us don't want to "hack" our editors. They're still stuck in 1980. We actually have to pay bills and want to spend time with our families and go out in nature.

    I appreciate customization; that's why I moved to Neovim and not to a full-blown GUI IDE (and I'll never use those). But that editor you speak of practically requires me to fix the idiocies of plugin authors that can't be bothered to run 7 tests on their code.

    I get it, a lot of programmers are cult followers. I was as well. At one point though, pragmatism should prevail. The editor should ideally be non-laggy and not spit warnings after I move from one buffer to another. Just embarrassing.



    I haven't really touched my emacs config in years. I just set it up how I wanted and now I use it to pay my bills. It sounds like the flexibility was just too tempting for you to not hack on it when you should be doing other things.


    I have set it up 50 times and it crapped the bed every time.

    Before anyone yells "skill issue!", I'll say that if any plugin author can make the editor crap the bed, then the editor is not good.

    There are degrees to freedom. Complete freedom in such environments is a liability, not an asset.

    Say hi to the others in the ivory tower. And remind them not to do selective omissions in their supposed counter-arguments to "win" an argument, and that it's a cheap tactic.



    This is a pretty weird hill to die on, boss. Are you suggesting that all of the software written over the years by emacs users (eg gcc) is “ivory tower”?

    As a community of practitioners we should embrace the idea that not all tools have to be “ideal” for all users. Some people like hacking their editor, and some don’t. If software tools sink to the lowest common denominator, like the vast majority of commercial software, we’ll all be worse for it.



    1. I ain't willing to die on any hill. In fact I was 100% certain I'll regret commenting negatively on Emacs. Pretty ardent and devoted fans, it seems.

    2. The ivory tower thing is dedicated to the parent poster sounding a bit elitistic and trying to imply I am doing it wrong and he's doing it right -- which I did not deny by the way (which is the really funny part) as my central point was "too much freedom is not good".

    3. I completely agree with the notion that not all tools are ideal for all users. I used this sub-thread to express a strong opinion that Emacs allows the "too much freedom" thing that actually becomes much more of a hurdle for those of us that just want to get on with it. I was sure it's going to ruffle feathers which makes me commenting on it fairly stupid, come to think of it, because I was not looking to pick fights, but just to broadcast an apparently unpopular opinion and never engage with replies. Which I failed spectacularly. :D

    > If software tools sink to the lowest common denominator, like the vast majority of commercial software, we’ll all be worse for it.

    Here's the part where you and I will disagree. Your statement is correct on the outset but I take issue with it because I take it as a hint that Emacs > all other editors. Which cannot be stated as a fact, ever, not for any editor, not just Emacs.



    I have about 140 Emacs plugins running.

    I've never had to declare Emacs bankruptcy.

    10 years of good times with Emacs+evil and 35 years of vim.

    Fwiw.



    Sadly that's not worth anything. ¯\_(ツ)_/¯ Anecdotal evidence in both directions.

    I had a soft spot for Emacs for a _long_ time (almost two decades)... FWIW. ;)

    Key word: had.



    https://janet-lang.org

    Also by the same author.



    I like Janet a lot, and have been using it for small personal projects for about a year.

    But it does come with some design decisions that I'm a bit ambivalent about and for which I haven't found a good explanation:

    - No persistent data structures. I guess this has something to do with limitations of the GC?

    - unhygienic macros combined with lack of namespaces. XOR those two choices would be fine, but the combination is janky

    - Somewhat peculiar choices in syntax. It's neither Scheme, nor is it Clojure. # starts comments, ; is splice, @ marks literals as mutable...



    I prefer Janet, but Fennel is great in places Lua is already supported, like in Löve2D.

    https://git.sr.ht/~benthor/absolutely-minimal-love2d-fennel



    One thing I've found that's really nice with Fennel + Love2D is you can even do hot reloading with nvim+conjure[0](I assume emacs too). I assume there's a way to hot refresh with just straight Lua but it feels very natural to do with a lisp.

    0(example config): https://github.com/TheBlob42/love2d-fennel-neovim



    I believe Fennel was originated by Phil Hagelberg (technomancy)

    https://git.sr.ht/~technomancy/fennel-lang.org

    Janet looks like is by Calvin Rose (bakpakin) https://github.com/janet-lang/janet/graphs/contributors



    I’m pretty sure Calvin created Fennel and Phil took up the reins when Calvin moved on from the project.


    I stand corrected:

        % git remote -v
        origin https://git.sr.ht/~technomancy/fennel (fetch)
        origin https://git.sr.ht/~technomancy/fennel (push)
    
        % git log --reverse
        commit 9afe4338ed1816a759cb4b120f89e8f67159ce16
        Author: Calvin Rose 
        Date:   Sun Aug 7 18:50:34 2016
    
            First commit.
    
        commit b62a24853e24662f76932a2a81bb77cc25704491
        Author: Calvin Rose 
        Date:   Sun Aug 7 19:05:40 2016
    
            Add some examples.
    
        commit 5e3e0fe11e4f56007b3523e54d81d55280ef2204
        Author: Calvin Rose 
        Date:   Tue Aug 9 08:27:43 2016
    
            Update README.md


    Dammit, Janet! Ok, looks good. I'll need to look into it.


    Whomever downvoted you probably never saw the Rocky Horror Picture Show


    >by the same author

    What? People are just creating new languages these days as if they were Javascript libraries?

    Let's say I wanted to make my own programming language. What's the easiest way to prototype it in a way I can share it with the world? Are the programming language development toolkits that come with a tokenizer library and things like that? Should I write my own program to output machine code? Or maybe it's easier to just transpile to Javascript?



    The author, Calvin Rose, is a (I assume pretty great) compiler engineer at NVIDIA who has a personal affinity for LISPs and Lua: https://bakpakin.com/

    I don't think the average programming language enthusiast is maintaining multiple well-known languages.



    Interesting. I saw another link here of someone who insists on making C# run everywhere, now someone who insists on LISPs.

    I really want to try making a language that is imperative, like really imperative, where every line must start with a verb, just to see what it would look like.



    > I really want to try making a language that is imperative, like really imperative, where every line must start with a verb, just to see what it would look like.

    It would look like Tcl.



    A bit but I don't like "set foo 32"

    One way I think I can get rid of that is like this

        32 = foo;
    
    But why do we even need variables? I think the perfect language design would be if you could just do this:

        pow(x, 2);
        pow(y, 2);
        sqrt() = result;
    
    And maybe you could do this

        {
            pow(x, 2);
            pow(y, 2);
            sqrt();
        } + 1;
        pow(2) = result;
    
    Instead of result = pow(sqrt(pow(x, 2), pow(y, 2)) + 1, 2); that we have today.


    At that point you’re almost, but not quite into the realm of “forth”


    Crafting interpreters by Robert Nystrom could be a place to start - https://craftinginterpreters.com/

    The author posts on HN as ‘munificent’, I think.



    Ok I’m genuinely convinced I’d be happier using Fennel than using Lua in instances where I need to use Lua. I’m not currently using Lua for anything. Maybe if I write a Pico-8 app…


    Fennel is pretty nice.

    I wish it had gradual typing support though or at least allowed for type annotation for static tooling. Not that dynamic typing isn't a valid choice but with more and more languages getting gradual typing support it is hard to go back.

    I guess we could build something like Coalton but for Lua.



    I did find this, though it seems runime only: https://github.com/dokutan/typed-fennel

    Maybe a static system can be built upon it.



    I've been working on something along those lines in eli:

    https://github.com/codr7/eli?tab=readme-ov-file#type-checkin...



    Fennel's approach of compiling to Lua while maintaining meta-programming capabilities is elegant. The syntax reminds me of Clojure, but without the JVM overhead. For embedded systems or game development, having both functional idioms and Lua's tooling seems like a powerful combination.


    > Fennel's approach of compiling to Lua while maintaining meta-programming capabilities is elegant.

    Yeah, it is very nice to work with.

    The only tiny "complaint" I have is that it doesn't compile to pure Lua, but instead assumes you'll be running it together with Lua's libraries.

    I say this because, for me, the places where I'd like to use Fennel have a lot of overlap with the places where I'd like to use Lua without loading any of the provided libraries (e.g. embedding Lua into other software, instead of using it standalone).



    Wait what do you mean? If I understand you correctly I use fennel for this all the time, I just copy the compiled lua into the place the program expects to find lua scripts.


    Sandboxed lua can't assume it has access things like debug libraries or any of the file IO, among other things. Many setups pretty aggressively remove and/or patch libraries and even large parts of the default global table. Assuming a vanilla lua library environment can make a project like this unusable in many places.


    Oh, sure. I didn't realize fennel used those libs in the compiled lua output if you don't call to them. I've never run into this problem and again I've written a lot of fennel that is targeting locked down scripting environments. I probably don't use all of its features though so maybe just got lucky so far.


    I might be remembering wrong on where the problem was exactly, but it was either the generated code, or the code for the Fennel transpiler itself.

    I just know that I tried to use it without loading Lua's libraries (i.e. without calling the `luaL_openlibs` function or equivalent) and was unable to.

    > I just copy the compiled lua into the place the program expects to find lua scripts.

    Yeah most existing programs just load all Lua libraries by default, so that's generally not an issue.

    My post is more from the point of view of embedding a restricted Lua interpreter into a program (i.e. only Lua-the-language, not Lua-the-language-plus-its-libraries) while still supporting Fennel.

    ---

    EDIT: Just checked, the Fennel code `(lambda [foo bar] bar)` compiles to Lua code that calls `_G.assert`.



    Another spot it's great for is in legacy lua programs that you inherit from who knows where, which in my experience is a lot of the live lua out there. It hooks into the module loader system so you can just freely mix functions and tables between the two.


    Semi-related for those looking for other languages built on top of Lua:

    https://moonscript.org/



    I believe that people who complain about parens have not coded in Lisp (atleast not enough)! Once you get over the "parens", the homogeneity of the language shines through and you will appreciate why some people like me never get over Lisp.


    it's kinda funny that whole noise about parenthesis. For a experienced Lisper parenthesis are so meaningless that can be ignored by reading good indented code, however... for a newbie, the amount of parenthesis can be a real nightmare. All that can just be properly solved by using a decent editor that support good parenthesis edition... like emacs. Truly funny. I've been on this community for more than 10 years and always the same thing.


    The comparison with Closure is really interesting. They make the point that they do less reinvention of Lua than Closure does with Java - functions, standard library, tooling. I'd love to know why. Is it just that Lua has less problems than old-Java


    Clojure


    I'm not sure if this was the up front reasoning but a lot of lua code is run in situations where you don't have full control over the runtime or distribution method.

    So anything that requires C libs would automatically rule out fennel for a lot of projects that are essentially using someone's lua api as the target platform. Roblox, mud client scripting, openresty, that sort of thing.

    And these environments usually have so much added to them, pcre, stdlib extensions, class systems etc fennel works best not making any assumptions about any of that. It's just straight up the lua semantics, and so anywhere lua works it works. I've used it a lot and originally recoiled from this decision but now I think it is genius.



    I don’t love fennel, it usually dominates the whole taste of a dish for me


    Great for casual and fun game development. TIC-80 has direct support, and it’s pretty easy to go from fennel to PICO-8.


    I'm a bit puzzled by whether Fennel is trying to make "different things look different" as they claim, or not. On the one hand, Fennel separates Lua's multipurpose "for" into "for" (for numeric ranges) and "each" (for iterators). On the other hand... Don't macros and function calls, two rather different things with different powers, look identical in Fennel?

    On the "Values" page, they also mention a macro that alters the lexical scope somehow. This macro is now deprecated, but its mere existence implies that such a macro can be expressed in Fennel -- that is, that it's possible to write Fennel code that causes what looks like an ordinary function call to do surprising things to the program state.

    As they rightly mention (on either this page or the "Values of Fennel" page), the advantage of constraining a language is that you can more easily tell what a program is doing (and not doing) at a glance. But this seems to be undone by giving a pass to (unhygienic?) macros.



    Linking to this without the fennel-lang.org main page which states the following

    "Fennel is a programming language that brings together the simplicity, speed, and reach of Lua with the flexibility of a lisp syntax and macro system." is a bad idea. Not having this sentence on your justification is ill advised.

    Not to detract from the language or anything I have found many programming languages justification to just not have an elevator pitch and I have a hard time understanding why this is the case. Unfortunately people's attention spans are extremely short.



    > Not to detract from the language or anything I have found many programming languages justification to just not have an elevator pitch and I have a hard time understanding why this is the case.

    But they do have one, that you just copied?



    I'm pretty sure the parent comment is pointing out that the quoted sentence from the main page ought to be present in the rationale page that is linked.


    I like the popularity of Fennel so that I can greedily keep Hylang and LFE all to myself!


    If I'm writing an advanced Neovim config, I'd find YueScript[0] a lot more pertinent. Personally I can't deal with Lisp paren noise, I can hardly deal with semicolons.

    [0]https://yuescript.org/doc/







    Join us for AI Startup School this June 16-17 in San Francisco!


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



    Search:
    联系我们 contact @ memedata.com