hckrnws
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.
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.
You might be able to repurpose CSharp.lua[1] to compile F#
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
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 find most niche / less popular languages lack the developer tools ecosystem in ways that simply hinders adoption by more devs.
Niche languages often serve specific purposes that don't require broad tooling. They also sometimes can leverage tooling from more popular languages. Less popular doesn't necessarily mean less capable - some niche languages have excellent tooling in their domain. Not to mention that not all developers need the same type of tooling or prioritize certain set of tools equally, like for example, for me personally - homoiconicity and REPL-connectivity in a language are far more important than LSP or some other things.
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.
Link to the macros: https://github.com/Grazfather/dotfiles/blob/master/nvim%2Ffn...
If only there was an editor which could act as an interpreter for Lisp directly ...
I've been using EMACS since it was TECO on TOPS-10. Not a day goes by that I don't spend at least an hour with it. But it is long in the tooth and the abstractions it exposes are as advanced as they can be without breaking reverse compatibility. It is one of the few packages I think would be improved by a fully-supported package manager. E-Lisp is a warren of inefficiency, notational paucity and half-implemented tools. If someone wanted to build an emacs-like text editor in Lua or Fennel, I would absolutely sign up as a beta tester (assuming the key bindings were mostly the same.)
I understand it's improved, but Emacs was always too slow for me.
Additionally, in my workflow I open my editor across many many terminals, the startup time alone prevented this. With emacsclient it was faster, but I specifically DON'T want to have a window that shows all my other open files.
> I specifically DON'T want to have a window that shows all my other open files.
There are multiple different ways of separating project/workspace/etc. contexts in Emacs - tab-bar mode, persp.el, etc.
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 know you're being down voted, but I've struggled with my Emacs config breaking quite frequently over the years. Nearly all of the breakages were due to plugins. I assume these breakages would be far less common if I was only using old plugins like Helm. But I like fancy new plugins that bring new functionality and smooth UX. They just seem to make breaking changes a lot more often. As far as performance goes, Emacs is roughly equal to VSCode IMO. It sometimes chokes on large files, but in general works well enough for me. To be fair, I've had trouble with Neovim + plugins becoming sluggish as well.
I am no longer impressed by the gray color treatment of my comments when it happens (don't get the impression that it's the norm; it's fairly rare). HN has showed me that it's much more about group-think and cargo culting than objective and calm discussion. Obviously those elements still kinda sorta prevail a good part of the time but for the most part the random HN user fell victim to the usual big herd logic fallacies. So let them press their arrows, that's not changing anything. And I didn't even claim something is objectively / universally bad. I said that the priorities of Emacs are not my priorities, in not-the-perfect diplomatic way that's apparently expected. Some people couldn't take that and pressed the arrow.
It's their right. ¯\_(ツ)_/¯
With that being said, I also struggled with Emacs. Again, too much freedom. I don't want to care where am I supposed to put `.el` files. I want a plugin manager and to be able to tell it: install / update / delete. Neovim's Lazy and Mason do that and that's why I love them. Just earlier today some configuration of a plugin broke; it used git submodules and something got moved and the new revision used another mechanism. I literally fixed it in 5 seconds: deleted the plugin, installed it again, and configuration persisted and I didn't have to set it up again. Reopen a file (just input `:e` in the command bar), everything worked right away. Is this too much to ask of Emacs?
> They just seem to make breaking changes a lot more often.
That was my experience as well. Always some warning in the command bar or a full-blown stack trace. No thanks. I don't associate with amateur work.
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.
Now you're living life large with jetbrains and making the big bucks !
Nope. Neovim. And even there some of the "distros" got on my nerves. I guess at one point I'll just learn Neovim's API and make my own blend like everyone else.
NOT looking forward to it. But I suppose there's no other way.
I still view Neovim as a huge improvement over Emacs though.
> I still view Neovim as a huge improvement
Neovim unlike Emacs is an editor. Emacs is not a mere code editor, not an IDE, text-processor, or web-browser. Emacs first and foremost is a Lisp REPL, with a built-in text-editor. Without deeply understanding that aspect one can never truly appreciate the incredible power it grants you.
Do you use your editor to read and annotate pdfs? Or watch videos? Or manage the library of your ebooks? Or track your expenses? Or control project management like Jira? Or keep your knowledge base and note-taking? Or interact with LLMs? Or explore APIs like Postman? Or keep your spaced repetition flash cards like Anki? Or use it for chat over platforms like Telegram and Slack? Or find and read RFCs and manpages? Or to perform web-search, search through your browser history, Wikipedia. Do you have etymology lookup, thesaurus, dictionaries, translation? Or to order pizza? Or measure distances between coordinates on a map? Automate things based on solar calendar or moon phases? Manage all your configs, aka dotfiles? OCR images with text? List, browse and code review Pull Requests, etc., etc.
In what sense exactly is Neovim/VSCode/IntelliJ/whatever is a "huge improvement", please tell us?
Well you kind of answered yourself. After limping with Emacs for 19-ish years, during most of which I never made the serious effort you alluded to, I finally admitted I just don't have and don't want to have the mindset that's deemed necessary to make full use of Emacs.
All of my professional experience, which is at this point substantial (though of course I make no claims that quality stems from quantity!), has showed me that smaller specialized tools always perform better -- in every meaning of the word.
To me Neovim wins because it's extremely snappy, it has no visual noise, and doesn't show me a warning from a random plugin down there in the command bar almost every minute (something which Emacs apparently will always do). And is configurable in a way I find intuitive. I never cared about all the directories where I am supposed to install my own Elisp files, and I still don't. I want a plugin manager and I want to issue commands to it: install, update, delete. Neovim's Lazy and Mason do exactly what I expect.
I was never, in almost two decades, able to look at how Emacs does things and think to myself "oh but of course it will work like that".
So no, I haven't used Emacs for anything except coding. And I don't intend to use Neovim for anything else as well (with the possible exception of lazygit integration, that one works great). Though I am also working towards having everything except my web browsers be in the terminal.
Again, many will say "skill issue" or "it's just not for you" (the smarter ones). Which, again again, I never denied. But I don't plan to mince words and I am tired of the (to me) unjustified praise for Emacs. It absolutely is not, not just for everyone, but not for most even, IMO.
If you have tamed it and find it intuitive, I am sure it empowers you. I never got to that point and I regret trying to fit in for such an extremely long time. But oh well, we live and learn. We live for sure. :)
The point you're trying to make is meaningless. You're trying to compare two things of distinct categories. It's as if I said - "I like Zoom, we sometime use it for screen recordings", and someone replied: "I've moved away from Zoom for my screen recordings - OBS is such a huge improvement over it..."
I don't care what editor you use or like or moved on to. I use Neovim myself. But like I said, Emacs is not just an editor. Come back when you find a better replacement for a "Lisp REPL with a built-in editor", maybe then, the conversation start making sense.
Emacs was sold to me as an editor however, and technically that wasn't a lie.
The distinction you're making is too only technically correct. Emacs is still (also) an editor. I judged its editor abilities and found them lacking. Finally I woke up and understood it's not for me and moved on.
You can stop arguing now.
[flagged]
[flagged]
Hey... could you write about one or two of the worst offenses? I consider myself an emacs/elisp power user, yet somehow (M)ELPA seems pretty doinked out-of-the-box on a stock Debian nox-emacs install. I just thought it was me.
My apologies, but I no longer remember. I only remember my huge annoyance that almost anything I did, even successful, led to some warnings or unnecessary notifications in the command bar. Always some noise there. Not to mention the occasional actual failure in a plugin leading to huge stacktraces and leaving me with a partially working editor that kind of still works but not quite.
My brain mercifully deleted all details. At one point I really had enough (and waiting for 19 years for an editor to get better is IMO having an angelic patience that Emacs did not deserve) and just moved on and forgot all about it.
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...
Janet is very small, apparently smaller than Lua, and is also intended to be embedded into native-code programs. Hence, I suppose, the decisions that make it "too" simple: it apparently is not intended for writing large amounts of code.
I prefer Janet, but Fennel is great in places Lua is already supported, like in Löve2D.
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 <calsrose@gmail.com>
Date: Sun Aug 7 18:50:34 2016
First commit.
commit b62a24853e24662f76932a2a81bb77cc25704491
Author: Calvin Rose <calsrose@gmail.com>
Date: Sun Aug 7 19:05:40 2016
Add some examples.
commit 5e3e0fe11e4f56007b3523e54d81d55280ef2204
Author: Calvin Rose <calsrose@gmail.com>
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
Comment was deleted :(
>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.
Great book—as an entry point for designing languages.
I thought almost all programmers created a language or two in the course of their careers or hobbies.
You can make a simple language very easily if you design the syntax carefully and restrict its capabilities. It all depends on what you need it for.
In my case I needed a way to create reports from a Turbo Pascal program (TP3 for DOS I think) without having to edit the program and ship a new version. So I made a simple report generating language. It had simple for loops, all variables were global, tokens were all separated by white-space, no user defined sub-routines or functions, a set of predefined procedures specifically designed for the report generating function, arithmetic expressions that were only allowed in assignment statements, interpreted not compiled.
It was actually quite easy to do but of course was not a general purpose language. These days it might be simpler to embed Lua.
Apart from Forth, Lisp and Lua, Tcl also deserves mention as a small embeddable language. Not sure of its state today; I think by v8 it had started to get more complex, but its original incarnation was very simple. I remember getting Ousterhout’s book and being so enamored with the elegant design that I felt compelled to just implement it myself from scratch, which took just two days.
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.
It does sound interesting, and I'll look into Lisp, can you give me some advice on what's the best way to learn it?
On your other point, I've programmed in many languages in many years, and mostly I did some in an environment with an IDE, or powerful language-specific tooling (not tree-sitter) that had a proper good understanding of the syntax and semantics of the language used.
If you learn better by video than by reading, the Structure and Interpretation of Computer Programs lectures by Abelson and Sussman are spectacular. I have watched the entire course multiple times. The SICP book also receives a lot of praise, but I have yet to read it myself. They specifically use Scheme, but most of the knowledge can be translated to other Lisp dialects as well. The biggest difference between the different Lisp dialects are the macro systems and the standard libraries, so getting started and learning it doesn't really matter which one you choose. GNU Guile or Racket would be easy to use to follow along with SICP though.
Also... HtDP (How to Design Programs) is a good follow-on to SICP.
Oh... and I think we can't mention SICP without referencing this (relatively recent) video about why MIT moved from Scheme to Python for intro classes: https://youtu.be/OgRFOjVzvm0
HtDP was written to address difficulties with learning from SICP https://cs.brown.edu/~sk/Publications/Papers/Published/fffk-...
I have gotten much farther (and accordingly learned more from HtDP). It is accurate to think of it as an on ramp for SICP.
A good text to make you aware of the power of Lisp is "The Anatomy of Lisp" by John Allen (MIT). It's an old text but they don't write books like that anymore.
> what's the best way to learn it?
What's the best way to learn programming in general? For me, is to try to build something. Find a problem, pick a Lisp, start building.
Just make sure to have two things: structural editing and the REPL. Without these two, Lisp may feel awkward. But when you have the ability to quickly move any expression around, transpose them, etc., - writing programs becomes like composing haikus or something. You basically will be moving some "lego-pieces" around. With the connected REPL, you will be able to eval any expression in-place, from where you're writing your code.
I started without these and indeed, that was challenging. Having to balance the parentheses by hand, counting them, omg, I was so obtuse, but I'm glad I didn't give up. You don't have to go too crazy - in the beginning, something that automatically balances out the parens, or highlights them when they are not, and lets you grab an expression and paste it somewhere else would be good enough.
And the REPL. Shit, I didn't know any better, I thought I was suppose to be copypasting or typing things into it. That is not the way! Find a way to eval expressions in-place. Some editors even show you the result of the computations right where the cursor is.
I have done years of programming prior to discovering Lisp, and I don't really understand how I was okay without it. I wish someone has insisted to try it out. Today I don't even understand how can anyone identify as a programmer and "hate" Lisp, just because they have stared at some obscure Lisp code for like two minutes at some point.
also, for what it's worth, I've moved on to mexp in my old age. Just easier to figure out where things begin and end without all those parens. And they more-or-less mechanically convert to sexpr when you need them to.
And back in my day you couldn't get a CS degree without a class on parsing and interpreting structured data. Usually it was the compilers class. Now we don't require kids to take a compilers class so an entire generation doesn't understand why regexes don't work in all cases. When kids in my organization try to "parse" HTML or XML with regexes, I hand them a copy of the O'Reilly Lex and Yacc book. And when they come back saying they can't understand it, I hand them the Dragon book. I guess I just think we should all feel that particular pain. Sorry for the digression, but I was triggered by "regex" and "parser" used in the same sentence.
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..
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.
>> as it mainly applies to Lisp-like languages that uses parentheses heavily.
This is so wrong. Lisp does not use parentheses heavily. It doesent even use more parens than any C like language. I just dont understand the fixation with parentheses. The power of lisps comes from the fact that everything is an expression. Someone can correct me if I am wrong, since I only have experience with functional lisp Clojure, but I believe other lisps are more or less similar.
So if everything can be evaluated, then you can have a really great REPL experience. You can be inside your favorite editor and have the tightest feedback loop possible. So the absence of statements is actually a great feature of the language which improves ergonomics.
The anti parentheses argument is usually just a straw that people grasp, who have no experience with writing code in lispy languages. A quick superficial jab at something they do not know well, so that they can go on with their day, without having to deal with learning a new thing, that might change their whole view of programming.
Plus, don't forget the secret sauce of Lisp syntax: same number of parentheses, with none of the commas. Nor the semicolons. Nor the brackets. Nor the braces.
[dead]
I just don't understand the fixation with REPL. How do you use it? It sounds like you write code as black box then run it in REPL to see what it does because you don't understand it by yourself.
> I just don't understand the fixation with REPL
Perhaps because maybe you just haven't used one? I'm not talking about "a REPL" in langs like Python, where you typically have to type stuff into it. Lisp REPLs allow evaluating code directly in the source buffer, by sending it into the REPL. That REPL can be remote. Like seriously remote - NASA once did it on a spacecraft 150 million miles away from Earth. We run ours in a kubernetes cluster. Lisp REPL allows you to evaluate specific expressions, functions, or regions directly from where you are. Without saving, linting, linking, compiling, etc.
> because you don't understand it by yourself.
REPL is not about "understanding your code", it's about interactive development and exploration - it allows you to test your ideas and assumptions in real-time. Imagine being able to send http request, pull some good chunk of data, and then sort it, group it, slice it, dice it, filter it, visualize it - do whatever you want to do with it, directly from your editor. It's incredibly empowering and extremely liberating.
The only downsides - after getting used to that, using non-lispy languages sucks all the joy out of programming and you also have to explain to people what it is like.
It's like moving to an island where people have never discovered salt, pepper, and other spices, and though their cuisine looks beautiful - you just can't describe to them what they all are missing. Some even may say - "Hey, I've tried pepper once - but I don't understand your fixation with it" and you'd be like: "have you ever tried putting it into your food?"
You check assumptions about dynamic types this way? To see what the function even returns in the first place. Browser console has a similar function: you type a function invocation, run it, then the returned value is presented as a tree for inspection.
You can check any assumptions about the code. But it's not exactly like using browser's dev tools console. First — you don't have to type things "somewhere else" — you're doing it right where you're writing code. And it can be a file or a scratch buffer — you don't even have to save those experimental bits. Second — because you have things neatly wrapped into symbolic expressions (those pesky parens that non-lispers find so confusing), you don't need any ceremony for setting up the stage.
take this Javascript example:
function addFive(x) { return x + 5; }
or const addFive = (x) => x + 5;
And its counterpart in Clojurescript: (defn add-five [x] (+ x 5))
In javascript REPL you can eval the entire thing, but try doing it piecemeal - it makes little sense, i.e., what is (x) or => from js perspective, semantically? While Clojure variant already is a list - a native data-structure, the argument is just a vector - another native thing, etc.So that infamous code-is-data & data-is-code mantra may not seem like a big deal in a trivial example like this, in practice, it's very nice, it allows you to do things otherwise difficult to achieve, watch this video https://www.youtube.com/watch?v=nEt06LLQaBY and give it a thought, how does one build something like that and how using a Lispy language helps there, while using more traditional PL would make things much more difficult.
> It sounds like you write code as black box then run it in REPL to see what it does because you don't understand it by yourself.
That doesn't even make any remote sense. "I don't get the fixation with the JVM. It sounds like you write code as a black box, then compile and run it to see what it does because you don't understand it by yourself."
>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).As someone who likes lisps, visual separation is helpful. I tend to find complicated Lisp code just blends together into syntax soup
How would you write this in Lisp without introducing a bunch of parens? `if (0 <= x + m && x + m <= n) {…}`
I’m not a Lisp hater, but there’s a reason people make this criticism. I think most people find the infix version easier to read.
(if (<= 0 (+ x m) n) ...)
There is one additional set of parentheses due to the simple addition, which I already mentioned.I unwittingly chose an example that allowed you to write fewer s-expressions in Lisp… touché.
I've been working on this for TXR Lisp. The next, release 300, will support infix.
The core expression in your example expression will parse as you have written it. Except we have to add ifx:
1> (ifx (if (0 <= x + m && x + m <= n) (prinl 'do-this)))
** expr-1:1: warning: unbound variable x
** expr-1:1: warning: unbound variable m
** expr-1:1: warning: unbound variable x
** expr-1:1: warning: unbound variable m
** expr-1:1: warning: unbound variable n
** expr-1:1: unbound variable x
Though it doesn't work since it's not a complete example with defined variables, we can quote it and expand it to see what the expansion looks like, which answers your question of how you write it one underlying Lisp: 1> (expand '(ifx (if (0 <= x + m && x + m <= n) (prinl 'do-this))))
(if (and (<= 0 (+ x m))
(<= (+ x m) n))
(prinl 'do-this))
The ifx macro deosn't have to be used for every expression. Inside ifx, infix expressions are detected at any nesting depth. You can put it around an entire file: (ifx
(defstruct user ()
id name)
(defun add (x y) (x + y))
...)
Autodetection of infix add some complexity and overhead to the code walking process that expands macros (not to mention that it's newly introduced) so we wouldn't want to have it globally enabled everywhere, all the time.It's a compromise; infix syntax in the context of Lisp is just something we can support for a specific benefit in specific use case scenarios. It's mainly for our colleagues who find it a barrier not to be able to use infix.
In this particular infix implementation, a full infix expression not contained inside another infix expression is still parenthesized (because it is a compound form, represented as a list). You can see from the following large exmaple that it still looks like LIsp; it is a compromise.
I took an example FFT routine from the book Numerical Recipes in C and transliterated it, to get a feel for what it's like to write a realistic numerical routine that contains imperative programming "warts" like using assignments to initialize variables and such:
(defun fft (data nn isign)
(ifx
(let (n nmax m j istep i
wtemp wpr wpi wr wi theta
tempr tempi)
(n := nn << 1)
(j := 1)
(for ((i 1)) ((i < n)) ((i += 2))
(when (j > i)
(swap (data[j]) (data[i]))
(swap (data[j + 1]) (data[i + 1])))
(m := nn)
(while (m >= 2 && j > m)
(j -= m)
(m >>= 1))
(j += m))
(nmax := 2)
(while (n > nmax)
(istep := nmax << 1)
(theta := isign * ((2 * %pi%) / nmax))
(wtemp := sin 0.5 * theta)
(wpr := - 2.0 * wtemp * wtemp)
(wpi := sin theta)
(wr := 1.0)
(wi := 0.0)
(for ((m 1)) ((m < nmax)) ((m += 2))
(for ((i m)) ((i <= n)) ((i += istep))
(j := i + nmax)
(tempr := wr * data[j] - wi * data[j + 1])
(tempi := wr * data[j + 1] + wi * data[j])
(data[j] := data[i] - tempr)
(data[j + 1] := data[i + 1] - tempi)
(data[i] += tempr)
(data[i + 1] += tempi))
(wr := (wtemp := wr) * wpr - wi * wpi + wr)
(wi := wi * wpr + wtemp * wpi + wi))
(nmax := istep)))))
It was very easy to transliterate the C code into the above, because of the infix. The remaining outer parentheses are trivial.A smaller example is a quadratic roots calculation, from the test suite. This one has the ifx outside the defun:
(ifx
(defun quadratic-roots (a b c)
(let ((d (sqrt b * b - 4 * a * c)))
(list ((- b + d) / 2 * a)
((- b - d) / 2 * a)))))
sqrt is a function, but treated as a prefix operator with a low precedence so parentheses are not required.It is only function calling that needs (,); in C and () in Lisp. Which is half as many characters, but much much much noisy since:
- it is used everywhere
- carries very little information
Look at the example on the Janet page transcribed to Python syntax [0]. Several differences: - in Janet nearly every line starts and ends with ( and ), which is just noise
- in Janet there are several ))))
- in Janet there is no special syntax for: function definition, variable definition, collections, statements
- while Python is the opposite, it reads like a mix of English and Mathematics. Also it has special syntax for the ~5 things that you can do with the language, so you can just look at the Python code from far, don't read it, and you'll have a clue what's going on. It is also helpful when you search for something with your eyes. Also nested parentheses are different shaped parentheses, so you know which ones match.
Also in theory you could manipulate Python AST the same way you do in Lisps both in your editor and both at program-level. In practice you can't do that.Ok, let's compare looping
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
vs (loop for i from 0 below 5
do (format t "~A~%" i))
C has the same number of parentheses and also has curly brackets. C additionally has a bunch of semicolons and a comma not present in the Lisp version. The C version is also omitting the required function encapsulation to actually run this, while the Lisp version can be run as a top level expression.The comparison really isn't even close. If you really want, you can always put the trailing )) on new lines like people do with the trailing }}}} for their nested if in a for in a method in a class in C-like languages. The Lisp community has just decided that putting single characters on new lines like that is needlessly noisy and emphasizes characters that are easily ignored by humans and instead manipulated using structural editing.
> The comparison really isn't even close.
IMO it's close. Lisp isn't much worse than other languages. Tho it needs special syntax for some common constructs. It helps the human.
Regarding the for loop, if you only loop one statement, you do
for (int i = 0; i < 5; i++) printf("%d\n", i);
If you loop several statements, you do (loop for i from 0 below 5
do (progn
(format t "~A~%" i)
(format t "~A~%" i)))
again, lisp lacks the syntax to group several statements together. In C it ends with );} , indicating a function call inside a control flow or a block. In lisp ))) can be anything from function definition to assigning variables or arrays/collections or anything.[dead]
My understanding is that Lisp and most functional languages map very well to PL theory and how compilers and interpreters manipulate programming languages (functions as graphs of code, types as contracts etc.), while procedural languages map well to how CPUs execute programs, and the abstractions operating systems provide (functions as mappable pieces of memory, types as memory or register layouts etc. )
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...
> One "statement" per line is bog standard
This isn't really true. Most non-Lisp languages I work in, like JS or Ruby or Python, have things like long_expression =\n long_other_expression, or long_expression\n.another_long_expression\n.another_long_expression.
And if most of your code looks like that you are making a mistake.
"Sometimes I need multiple lines" is fine, exceptions happen.
But again I ask, visually are those lines super different?
Ditto for things like for loops which have multiple statements in a line.
When writing code you are transforming it all the time. Having only expressions (mostly) comes in very handily. It is called structured editing of code.
<3
Are n4ture and torginus the same person?
Why the exact repeat of the earlier post under a different name or some bots at play here?
It's not an exact repeat.
Stop karma whoring!!
If it is not suspicious then why has the comment been deleted, causing this comment to rise to the top level?
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.
Try defining data in C. Try extracting data from that data you've defined in C.
If you can understand the appeal of having JSON in JavaScript, you can understand some of the appeal of Lisp.
I’m having trouble understanding what you mean, if you could provide or link to an example I’d appreciate it.
Sure. Here's a comment with more of an explanation:
I feel like I must be missing something, because I don’t understand why representing data in an s-expression is better than representing it as nested arrays (lists of lists) and hashtables/dictionaries. I also don’t see why representing data in a language’s data structure is inherently better than representing it in a language-agnostic format like JSON, and having libraries to parse and convert data from that format into a language data structure, or a library that defines a JSON type, or storing the JSON in a string and using a library that operates on strings that contains valid JSON. I’ve worked with data in this way (JSON on disk, converted to nested arrays/tables in code) and haven’t felt it to be painful.
I can see how one might have a taste preference for it, but I’m struggling to understand what the tangible benefits are.
How does it know "1970-01-01" is a date?
It wouldn't. The consumer would need to know to expect a date as a string and parse it.
If you parse things with structure, and the structure is unknown in advance. In C(++) you have to generate all dynamically, and is very complex.
In other systems, you can just read the object, and the structure comes with it.
Javascript does that without lisp syntax. Not sure how helpful it is though, unknown structure is equivalent to no structure, an attempt to communicate without a protocol.
I personally find lisp-y syntax to be pleasant to write, and to generally be straightforward and easy to read. It's interesting to hear you have the opposite opinion, though.
> 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)
Most Lisp-y language have multiple parsers. The frontend may be that one, or it might be another. Racket has hundreds of frontends [2], Scheme has Wisp [0], and so on.
The ideal part of it comes down to the language being able to manipulate itself. Make the tokens an array, that you can shift, inject and/or mould into what you need.
That being said, that power isn't isolated to just Lisp-y. A few stack languages have it, like Forth, or to plug myself [1]. However, stack languages are a bit harder to optimise.
It isn't that they don't want a complicated parser. It's that you want to be able to easily modify that parser as its running, without hitting TeX levels of performance slowdowns.
[0] https://srfi.schemers.org/srfi-119/srfi-119.html
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.
It's called https://en.m.wikipedia.org/wiki/Homoiconicity
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
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.
Are you interested in learning the appeal?
Comment was deleted :(
The first thing that comes to mind is macros.
I tried fennel for a game jam and honestly was pretty disappointed. The way lisp languages are pitched here, I thought I was in for a mind opening experience, but instead the end experience was pretty much identical to lua in ever meaningful way, the only differences felt surface level (I.e. using closures, and parentheses).
I'm forever indebt to lisp for giving JS it's saving graces (closures and FN as first class citizens), but I think we some honestly on what the end experience really is.
Did you program against a REPL connected to your game, with state maintaining hot reload? Does your editor support working with s-expressions (eg. slurping and barfing)?
Batch processing style programming (write to file, save, run, stop, repeat) in Lisp removes some 3/4 of what the language(s) offer. It's especially so if one is navigating code by text rather than structure, eg. jump-to-next-word instead of jump-to-next-expression.
What is magical about Lisps, esp. the ones that are fully featured in this sense (Common Lisp, Clojure), is that you're programming a running program without losing state when you re-evaluate code.
That is magic. Instead of doing the save-compile-run-setup-conditions-wait-for-result-repeat hundreds of times per session, you run and write the code for your given problem when it arises during gameplay.
On top of that you throw on things like paredit, condition systems and you're and order of magnitude less frustrated and dare I say it, productive, than when you constantly have to churn through constant transitions between the code-in-file and game-in-memory disparity.
For games especially, things like test driven development make no sense because state is so insanely tangled. So you either hope for the best, or you program against a live game. I prefer the second option.
> The way lisp languages are pitched here, I thought I was in for a mind opening experience
Have you used a Lisp with a connected REPL? Not the one that you have to type instructions into - the one that allows you to send any expression at point, with virtually zero ceremony to it, basically letting you evaluate any part of the program on the fly? And that REPL can be even remote - at work we use one running in a kubernetes cluster, we can change our APIs and experiment without not only redeploying things, we don't even have to save our changes. Can you imagine being able to try your code without saving, linting, linking, compiling, deploying - all that on the fly? It is truly mind-opening experience. It's so fucking nice, it's like playing a video game. I do understand why it's appealing to build actual video games that way.
Using a Lisp without structural editing tools and without a REPL is like having a Ferrari without a working engine - you'd have to pedal it to move around, it's ridiculous.
The way to appreciate a Lisp is to use an actual Lisp, not a syntax transpiler for a non-Lispy language.
Common Lisp, Racket or Scheme with a good REPL and editor integration are light years ahead than Fennel which is little more Lua-with-parens.
> Fennel is little more Lua-with-parens.
Totally. And Clojure is Java-with-parens; Janet is C-with-parens; LFE - Erlang-with-parens; Elisp is a Stallman's erotic fantasy, neatly wrapped in parens. Only Common Lisp is an "actual Lisp for immortal souls" - the rest is for peasants.
Fennel isn't a "real" Lisp. It's more of an aesthetic layer on top of Lua than anything else. Try learning Scheme.
Give me a break. What's not "real" about it? It's homoiconic, it supports connecting to a REPL, it has macros, first-class functions and proper lexical scoping. What else do you want, Guy Steele personally sending you an emoji whenever you install it?
Comment was deleted :(
And yet people write a ton of XML, JSON or YAML by hand.
Comment was deleted :(
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.
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've been working on something along those lines in eli:
https://github.com/codr7/eli?tab=readme-ov-file#type-checkin...
Nice looking language! What’s the status? Is it stable?
That would be a question of definitions :)
It's mostly addition by now, rare that something disappears or changes significantly.
No prod use by anyone afaik.
I did find this, though it seems runime only: https://github.com/dokutan/typed-fennel
Maybe a static system can be built upon it.
Semi-related for those looking for other languages built on top of Lua:
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 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
I'd guess a major reason is that Java is statically typed, and Lua/Fennel are dynamic; making it easier to reuse any existing libraries.
I get the impression that Fennel is intended as a different/better interface for Lua.
In contrast, Clojure is intended as the language Rich Hickey wanted for writing the sort of applications he wrote, and the JVM happened to be a powerful (and already existing) platform that was suitable for doing that.
JVM has no notion of a function, only of a method! You don't have something to piggy-back on. Java stdlib from 15 years ago (to say nothing of 25) is a realm of heavy OOP and mutability everywhere, something you may not want to adapt your Lisp code to.
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.
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…
I don’t love fennel, it usually dominates the whole taste of a dish for me
But in the spirit of answering the headline's question, it's because nothing else tastes quite like it!
Comment was deleted :(
I like the popularity of Fennel so that I can greedily keep Hylang and LFE all to myself!
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.
Is it a theorem that languages named after trendy (such as spices) or shiny things die fast? Ruby, Crystal, Carbon, now Fennel.
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.
Why not Fennel?
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.
Crafted by Rajat
Source Code