HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
sctb
lumen
JavaScript2015–2024
sctb / lumen

lumen

A Lisp for Lua and JavaScript

14Distinct people
54Mentions
1Explicit recommenders
571GitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

A Lisp for Lua and JavaScript

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

Lots of Lisp interest today! Here are a few resources. On Lisp: https://www.lurklurk.org/onlisp/onlisp.pdf Lumen - a Lisp for JavaScript and Lua: https://github.com/sctb/lumen…

shawn used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:56:07 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year54 dated mentions

Observed mentions: 2015-10-16 03:04:02 UTC to 2024-03-25 08:25:16 UTC.

0510152015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 6 mentions; 0 submissions, 6 comments20152016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 6 mentions; 1 submissions, 5 comments2018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 17 mentions; 2 submissions, 15 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments20192020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 10 mentions; 1 submissions, 9 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 6 mentions; 2 submissions, 4 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments2024

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC606
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC101
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC615
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC17215
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC211
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC1019
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC624
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC202
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC202
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC211

What Hacker News says

54 sources
I agree! That’s actually not a jeer, it’s one of my main criticisms of lisp. You don’t need lists to have lisp. In many respects it works better without them; https://github.com/sctb/lumen proves it, since hash tables and arrays are the fundamental data structure. They have to be, because that’s the only way lumen can run in JS or Lua. Every time I can’t delete the first element of a list in lisp (I.e. del x[0] in the python sense) I get annoyed with racket. The reason I look past it is because the benefits are so good that they outweigh the annoyances. I wouldn’t trade it away.
In Gerbil Scheme – A Lisp for the 21st Century
Thank you! I didn’t realize tildes was open source. That’s really cool. One of my all time favorite examples of simple systems is Lumen, which was written by Scott Bell: https://github.com/sctb/lumen It took years of study to understand just how large the ratio was of functionality vs size.
In YC Library
Nice work. A few years ago, I made something similar out of lumen[0] by tweaking the reader to support clojure style literals. I used hyperapp[1] as a lightweight library for react/elm style applications with a hiccup-like syntax. The code ended up looking like (h 'div { class: 'main } (h 'div {} .... Where h is the raw function for hyperapp, not a macro. I'd intended to develop my own mini-lisp with the same syntax, but got sidetracked by other projects. Maybe someday I'll get back to it. (Currently, I'm deep in the weeds trying to learn how to write a dependent typed language that compiles to javascript.) [0]: https://github.com/sctb/lumen [1]: https://github.com/jorgebucaran/hyperapp
In Show HN: Dak – a Lisp-like language that transpiles to JavaScript
> What do you develop with Arc usually? I try to use Arc for as much as possible. We wrote our TPU monitoring software in it: http://tensorfork.com/tpus Eventually I became frustrated with Racket's FFI. So I eventually made my own arclike language called elflang: https://github.com/elflang/elf ... which itself is a fork of Lumen (https://github.com/sctb/lumen) by Scott Bell. The performance is good enough to run a minecraft-style game engine: https://i.imgur.com/iyr0YrB.png which was satisfying. Nowadays I've been trying to implement Bel, mostly for the challenge of it than for any practical reason. > I like how the "html" and "css" part was embedded in that "news.arc" file. Do you think that VIM script will highlight and lint the "css" part of an "arc" file? Nope. https://i.imgur.com/o9aUG6j.png But it has one very important feature: it
Read the rest
can properly highlight atstrings: https://i.imgur.com/wO4f742.png It's probably hard to tell, but the "@(hexrep border-color*)" would normally be highlighted as if it were a string. Arc has a feature called atstrings, where you can use @foo to reference the enclosing variable "foo". It can also call functions, e.g. "The value of 1 plus 2 is @(+ 1 2)" will become "The value of 1 plus 2 is 3".
In A project with a single 11,000-line code file
Not mine! That was all Scott Bell. It's forked from Lumen: https://github.com/sctb/lumen But, I did make an interactive tutorial here: https://docs.ycombinator.lol/ If you have any questions about it, I'd be happy to answer. This stuff is pure fun mixed with a shot of professionalism. For what it's worth, as someone with narcolepsy, I relate quite a lot to your chronic pain. (https://twitter.com/theshawwn/status/1392213804684038150) For me, it mostly translated into wandering aimlessly from job to job, since I thought no one would have me. I hope that you find your way -- there's nothing wrong at all with taking it slow and spending years on something that takes others a few months. Everyone is different, and it's all about the fun.
In How the Python import system works
Sorry if it sounded out of place. The context is that Dan's team does so much impactful work thanks to powerful software. But for various reasons, a lot of the required knowledge is esoteric. (Do you know how to write an emacs library to provide proper indentation for arc? If not, are you ready to dive in and learn, without extensive supervision?) It's also crucial for HN team members to be trustworthy and effective. I'm pretty sure that access to the core HN infrastructure is limited to very, very few people – it has to be. For example, pg used HN to keep notes on startups he was meeting with during office hours. And that's just one of dozens and dozens of sensitive areas. For obvious reasons, no one can have access to those except extremely trustworthy, capable people. Worse, you can't grant access to code related to those things withou
Read the rest
t also revealing the purpose of those sensitive areas. Abuse detection is possibly the mildest example, but I imagine the standard deviation on surprise is extraordinarily high for a random HN feature. Those two things (esoteric knowledge requirements + {competent,trustworthy,motivated}) means that Dan and his team are often short-handed. I am speaking from ignorance here, but it would be remarkable if they somehow weren't. The YC software team also covers a huge amount of territory as of today, from maintaining ycombinator.com's Rails codebase to keeping "Work at a Startup" infrastructure running smoothly. Some of it needs HN integration, like the ability for the YC application to cross-reference the HN usernames of each founder, which seems to use some sort of oauth token that I'm sure wasn't effortless to get working in Arc. The most well-placed person to do all of this work is Dan, but Dan himself has to spend a majority of his time moderating HN. Yet through all that work, he still finds the time to create megathread indexes like this one. So, knowing what I know, when I see that, I can't help but take off my hat for a moment and say something. Apologies for the confusion. Dan also helped design Lumen, a Lisp that bootstraps itself entirely in Javascript or Lua: http://github.com/sctb/lumen I fell in love with it around... five years ago? Something like that. And to this day I still write code in it all the time: https://news.ycombinator.com/item?id=26804484 (I've been meaning to ship a Tensorflow/Pytorch code generator, one of these days.) The last aspect of it is that I once had an opportunity to work on getting him some desperately-needed tooling back in 2016, but I totally blew it. The first thing he asked for was an Arc indentation plugin for Emacs, which I had never touched till then. Unfortunately my life was in complete chaos at the time (https://gist.github.com/shawwn/3110ab62fa027c7811578f167fa5a...) and various things were happening that made me turn bitter, to say the least. ("Crazy-seeming dickhead" might be a better phrasing, depending on who you ask.) I ended up delivering nothing, when I could have improved his workflow substantially. (I myself have been using some Arc vim indentation plugin I found somewhere around a decade ago, and god bless whoever wrote that; it's been so lovely. So I know how important that kind of thing can be.) Once I got my life in order, and have had several years to reflect, I realized what a unique and cool opportunity it was that I squandered, and I've regretted it ever since. So that was the middle paragraph talking. But! I had my shot, and you might be able to get one too. If you (or whoever's reading this) happens to have a unique combination of skills, a lot of free time, and are willing to work extremely hard, then I encourage you to email hn@ycombinator.com and tell 'em you're willing to lend a hand with whatever odd jobs they need. They'll probably appreciate it. Or maybe they don't need anything. That was 2016, this is 2021, and HN has been running fantastically well all this time. That's why I shall settle for the cheerleader role. :) Anyway, this is all entirely off-topic, so if it's inappropriate then please feel free to collapse or detach it, of course. But in my opinion that whole infrastructure hemisphere is one of the more interesting places in the world, a bit like Hogwarts, so I wanted to ramble a little bit. Back to ML!
In $365B wiped off cryptocurrency market after Tesla stops accepting Bitcoin
Glad you asked. Like this: https://github.com/shawwn/pymen/blob/68b66dccc96910869ab370d... (=defun choose-bind-test () (choose-bind x '(0 1 2 3 4 5 6 7 8 9) (write (str x)) (if (= x 6) x (fail)))) If it's confusing why it looks like Lisp, it's because it's a self-hosted Lisp that runs entirely in Python. (Basically, there's no "runtime" -- it uses native python constructs, and all of the code compiles directly to real python.) What does the python look like? Well: https://github.com/shawwn/pymen/blob/12753ebb1251970f29eb04e... setenv("choose-bind-test", macro=__choose_bind_test__macro1) def L_61choose_bind_test(L_42cont42=None): def __f73(x=None): write(L_str(x)) if x == 6: return x else: return L_61fail(L_42cont42) __L_42cont428 = __f73 return L_61choose(__L_42cont428,
Read the rest
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) As you can see, macros do an incredible amount of work under the hood. This code is reminiscent of the parent comment's try.then.then.catch example, yet the source code is wonderfully readable. Thus, lisp's power. If you want to play around with it: git clone https://github.com/shawwn/pymen cd pymen git checkout ml # optionally, brew install rlwrap rlwrap bin/pymen > (load "lib/onlisp.l") > (choose-bind-test) 0123456 Note, I didn't invent this technique. This is courtesy of Scott Bell's Lumen lisp: https://github.com/sctb/lumen I "merely" ported it to Python, and a few billion other things over the years. The specific example listed in the tweet was made using this: (=defun path (node1 node2) (if (null? (neighbors node1)) (fail) (in node2 (neighbors node1)) (=values (list node2)) (=bind (n) (choose (neighbors node1)) (=bind (m) (path n node2) (=values `(,n ,@m)))))) (defconst nodes* (obj a: '(b d) b: '(c) c: '(a) d: '(e) e: '())) (def neighbors (n) (get nodes* n)) Which you can run like this: > (print (str (path 'a 'e))) ("d" "e") > (print (str (fail))) ("b" "c" "a" "d" "e") > (print (str (fail))) ("b" "c" "a" "b" "c" "a" "d" "e") > (print (str (fail))) ("b" "c" "a" "b" "c" "a" "b" "c" "a" "d" "e") The (print (str ...)) stuff is because otherwise it would pretty-print the actual python objects, which are dictionaries. It's a bit harder to read. > (fail) {0: 'b', 1: 'c', 2: 'a', 3: 'b', 4: 'c', 5: 'a', 6: 'b', 7: 'c', 8: 'a', 9: 'b', 10: 'c', 11: 'a', 12: 'd', 13: 'e'} As you can see, lists are represented as dicts, not cons cells. So, to sum up, Lisp is so powerful that I was able to port pg's On Lisp examples with almost no modification whatsoever, directly into Python. These examples are almost verbatim from the book. (There are more in the github links above.) Since this will be my last chance to demonstrate this for some time, I close with an example of the "ballet" from On Lisp, where multiple processes are collaborating together to reach some goal: (program ballet () (fork (visitor 'door1) 1) (fork (host 'door1) 1) (fork (visitor 'door2) 1) (fork (host 'door2) 1)) expands into: setenv("ballet", macro=__ballet__macro1) def L_61ballet(L_42cont42=None): global L_42procs42 L_42procs42 = None ____x179 = ["visitor", ["quote", "door1"]] def __f60(__id39=None): L_61visitor(L_42cont42, "door1") return pick_process() L_42procs42 = join([make_proc(state=__f60, pri=1)], L_42procs42) ____x183 = ["host", ["quote", "door1"]] def __f61(__id40=None): L_61host(L_42cont42, "door1") return pick_process() L_42procs42 = join([make_proc(state=__f61, pri=1)], L_42procs42) ____x187 = ["visitor", ["quote", "door2"]] def __f62(__id41=None): L_61visitor(L_42cont42, "door2") return pick_process() L_42procs42 = join([make_proc(state=__f62, pri=1)], L_42procs42) ____x191 = ["host", ["quote", "door2"]] def __f63(__id42=None): L_61host(L_42cont42, "door2") return pick_process() L_42procs42 = join([make_proc(state=__f63, pri=1)], L_42procs42) while True: L_print("looping") time.sleep(0.5) if not is63(pick_process()): break return catch(L_42halt42) Notice the incredible amount of leverage you get from macros. Most programmers simply go ahead and write that kind of callback-hell-style code in other languages. Bugs become very difficult to spot. Here, you don't need to.
In Can continuation passing style code perform well?
Ah, finally, the ideas from Lumen are percolating slowly into the mainstream: https://github.com/sctb/lumen (It's a lisp for both JS and Lua.) Eslisp is 2015, which is around the same time that Lumen was started: https://news.ycombinator.com/item?id=10264970 Though from the commit history, Lumen was actually started in 2012. Regardless, both projects seem to be roughly the same idea, which I find very interesting. "block" is better named "do", and "lambda" is better as "fn", but those are minor details. Most of the power of the idea seems present.
In Eslisp – An S-expression syntax for ECMAScript/JavaScript, with Lisp-like macros
For a very simple, very readable Lisp reader, see Lumen's reader.l file: https://github.com/sctb/lumen/blob/master/reader.l It compiles to Javacript and Lua, so if you prefer reading those, you can: https://github.com/sctb/lumen/blob/master/bin/reader.js https://github.com/sctb/lumen/blob/master/bin/reader.lua It turns out that you can greatly simplify the code by e.g. "does an atom start with 0x, 0-9, or dash? if so, try converting it to a number." That turns out to handle cases like 1e9 too. Whereas "1e" becomes a valid lisp symbol. So you can set it to your own value. The emacs source code is also worth reading. It's not simple, but it's simple enough to be understandable: https://github.com/emacs-mirror/emacs/blob/7b3e94b6648ed00c6... I like emacs source code because it handles "every possible real-world case that you can think of."
In Compiling a Lisp: Reader

Showing the 15 most recent of 54 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.