HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
akkartik
wart
C++2011–2023
akkartik / wart · Archived

wart

An experimental, small, readable Lisp with thorough unit tests and extensible functions/macros.

2Distinct people
30Mentions
0Explicit recommenders
145GitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

An experimental, small, readable Lisp with thorough unit tests and extensible functions/macros.

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

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

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 5:04:57 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 year30 dated mentions

Observed mentions: 2011-02-20 04:09:41 UTC to 2023-08-07 01:24:55 UTC.

02468102011-01-01 00:00:00 UTC to 2012-01-01 00:00:00 UTC (exclusive): 10 mentions; 0 submissions, 10 comments20112012-01-01 00:00:00 UTC to 2013-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments2013-01-01 00:00:00 UTC to 2014-01-01 00:00:00 UTC (exclusive): 4 mentions; 0 submissions, 4 comments2014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2016-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): 3 mentions; 0 submissions, 3 comments20172018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2023

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
20112011-01-01 00:00:00 UTC2012-01-01 00:00:00 UTC10010
20122012-01-01 00:00:00 UTC2013-01-01 00:00:00 UTC808
20132013-01-01 00:00:00 UTC2014-01-01 00:00:00 UTC404
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC101
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC202
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 UTC303
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC000
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC000
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC000
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC000
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC000
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC101

What Hacker News says

30 sources
"I have no idea why Lisp doesn't let you call functions like foo(1 2 3) ...If it looks confusing, don't write it that way. Just like every other language feature." I used a similar ethos to create a whitespace-sensitive lisp with infix operators without compromising macros. The rule for whitespace-sensitivity was: if whitespace sensitivity was even slightly ambiguous, you lost it. Inside parens you lost it. Inside macros, you lost it. But it was still useful in a wide variety of situations: https://github.com/akkartik/wart/blob/master/004optional_par... However, when it came to foo(1 2 3) I ended up just saying no. It has absolutely no benefit except being familiar to non-lispers (where whitespace sensitivity actually makes Lisp easier to read for anyone outside an editor). Particularly if you permit infix, (foo 1 2) and (1 + 2) have a
Read the rest
nice correspondence in syntax. Function calls just become a form of prefix operator. More details: http://arclanguage.org/item?id=16924 (particularly the last couple of paragraphs) I think a lot of the reason lispers look down at attempts to make indentation significant is that it's been bundled in the past with other syntax sugars to make Lisp more familiar to non-lispers. The goal shouldn't be familiarity, it should be power. More details on the infix syntax I designed: http://akkartik.name/post/wart
In D-Expressions: Lisp Power, Dylan Style (1999) [pdf]
Yeah! Even more so if you have first-class macros (fexprs) like say Kernel does. I had a lot of fun building a fexpr-based lisp-1 with quasiquote (which Kernel eschews) and also with a first-class apply that works on both functions and macros. Not even Kernel can do that. $ git clone https://github.com/akkartik/wart $ cd wart $ ./wart ready! type in an expression, then hit enter twice. ctrl-d exits. mac (foo x) `(+ ,x 1) => (object function {sig, body}) (foo 3) => 4 (foo @(list 3)) # apply analogous to the ,@ splice operator => 4 (foo @'(3)) => 4 More info at http://akkartik.name/post/wart. Lots of example programs at http://rosettacode.org/wiki/Category:Wart.
In Building a Lisp from scratch with Swift
I somehow missed previous submissions from this blog, but I've now favorited some of them and subscribed to its feed. In particular, http://vicsydev.blogspot.com/2017/01/on-tests.html resonates very much with my approach to organizing programs: 1. https://github.com/akkartik/wart/blob/91356d9385/organizatio... 2. http://akkartik.github.io/mu/html/000organization.cc.html (https://github.com/akkartik/mu/blob/61fb1da0b6/000organizati...) The ability to 'tag' tests into multiple suites is particularly nice. I might steal that at some point, but I'm first going to think about how it might synergize with my Literate Programming approach (http://akkartik.name/post/wart-layers)
In Lispy sets
No, the two are independent. The rules for parentheses: 1. Multi-word lines without leading parens are grouped with later indented lines. 2. Indentation is not sensitive inside parens. More info, unpacking the implications: https://github.com/akkartik/wart/blob/47e3572a29/004optional... Infix works by using a disjoint set of 'operator characters' that isn't available to prefix symbols. More info: https://github.com/akkartik/wart/blob/47e3572a29/006infix
In Spry: a programming language inspired by Smalltalk/Rebol and written in Nim
Thanks for the kind words! I'm particularly gratified that you found the literate/ subdirectory. There's some details about it at http://akkartik.name/post/wart-layers, but it sounds like you understand the point already so I'm preaching to the choir :) "What kind of things have surprised you about Wart during development?" I stopped working on wart at some point for two reasons. First, I realized that I was falling into a blind spot shared with many contemporary programmers: blindly assuming that the way to improve the state of programming was by creating a new language. But there is more to programming than languages. Languages just happen to be memetically tempting sirens to chase after. Second, I painted myself into a corner with wart because I made it too late-bound, so late-bound that it was incredibly inefficient and so hard to opt
Read the rest
imize. (I think it might still be possible, but I lost patience partly because of the previous point.) I still like my infix scheme and have a particularly soft spot for the way wart allows keyword arguments anywhere in any function call. See, for example, the use of :from in a webserver implementation that makes everything so much clearer: https://github.com/akkartik/wart/blob/ce64882c69/071http_ser.... However, I've forced myself to harden my heart and ruthlessly ignore considerations of syntax, and focus on what I think are more impactful directions: conveying global rather than local structure of programs. More details: http://akkartik.name/about. In brief, I'm working on ways to super-charge automatic tests (by making them white-box, and by designing the core OS services to be easy to test) and version control (using the literate layers you noticed). It's a whole new way of programming where a) readers can ask "why not write this like this?", make a change, run all tests and feel confident that the change is ok if all tests pass; and b) readers can play with a simple version of the program running just layer 1, then gradually learn about new features by running layers 1+2, 1+2+3, and so on. --- "The problem is that it throws away the ability for the code to pull updates from library maintainers. If the import is "pinned" at commit f1d2d2f9, then you push a security-related patch to akkartik/bar, most existing code will stay vulnerable forever, because most code is never maintained after being written." I hadn't considered hard-coding hashes in imports, because it's historically been very hard to truly distinguish compatible changes from incompatible ones without introducing bugs. I think it's more important to give people control over upgrades than to try to improve over the current advisory+pull method of communicating security issues. You're right that most code is never maintained after being written. But most code is also utterly unimportant to anyone including the author. So that failure of security is ok :) Trying to force concern about security only increases the moral hazard -- people get used to other people thinking about their security for them. "were there any ambiguities with indentation-as-parens? I haven't thought about it rigorously, so I was just wondering if there are any corner cases to watch out for when I add it." The big insight, I think, is to not fear parentheses too much. A common failure mode of such approaches (like http://readable.sourceforge.net and http://dustycloud.org/blog/wisp-lisp-alternative) is to create too many new token types and lose the simplicity of lisp syntax. My approach instead is to a) just use parens sooner than look "outright barbarous" (https://www.mtholyoke.edu/acad/intrel/orwell46.htm), and b) take it a step further and disable all indent-sensitivity inside parentheses. Anyways, that's my two cents. The precise rules I use are at https://github.com/akkartik/wart/blob/master/004optional_par.... (They're really in the first couple of sentences there.) --- "Whoaaa, you use tangle/weave! How do you like it / what do you think about literate programming?" Like I said above, I like it very much, especially in combination with tests and a notion of layers. Here's my take on why literate programming has failed so far: http://akkartik.name/post/literate-programming
In Show HN: `diff arc0 arc3.1`
It is indeed. I'm in the process of porting from Racket to Lumen. It's taking a bit, and it'll be tricky to add true greenthread support to js and lua, but it's a win so far. Wart is freaking cool! Thanks for showing that. Whoaaa, you use tangle/weave! How do you like it / what do you think about literate programming? I was batting around the idea of writing code that way, but it seemed like tangle might not be the best solution. But I love your 000prefix naming scheme; I instinctively do that for branches. After reading through a few files starting at https://github.com/akkartik/wart/blob/master/literate/000org... it became pretty clear how wart worked, pretty quickly. I'm mainly wary of the "compile" step that using Tangle implies; do you find it slows you down much? Would you choose to do it that way if starting fresh? What w
Read the rest
ould a language ecosystem be like without any backwards-compatibility guarantees, super easy to change and fork promiscuously? It's fun to find someone has been mulling over the same problems! I was considering taking Laarc in a similar direction: (import "akkartik/foo") That would check whether the folder "../foo/akkartik" exists, and if not, clone foo from github.com/akkartik/foo. It would also rewrite its own source code to look like: (import "akkartik/bar" "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") That means whenever someone clones your codebase, running the program would cause them to clone github.com/akkartik/bar using commit f1d2d2f9. So it's static linking, basically. It's a guarantee that "This source code will always work, the same way (+ 1 2) will always yield 3. You could copy-paste this source code into a gist, and whoever ran it would end up running the same program." That removes dependency hell / worrying about versions entirely. But it's not the right solution, and I've been searching for a better one. What do you think? The problem is that it throws away the ability for the code to pull updates from library maintainers. If the import is "pinned" at commit f1d2d2f9, then you push a security-related patch to akkartik/bar, most existing code will stay vulnerable forever, because most code is never maintained after being written. I was thinking of following that model, but using the convention that libraries are expected to use git tags in order to indicate major.minor.patch, and (import "akkartik/bar" "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") will bump itself to the next commit if it's tagged as an increment of the patch version. What kind of things have surprised you about Wart during development? Any unexpected pitfalls or interesting discoveries along the way? Thanks again for pointing it out! By the way, were there any ambiguities with indentation-as-parens? I haven't thought about it rigorously, so I was just wondering if there are any corner cases to watch out for when I add it.
In Show HN: `diff arc0 arc3.1`
In similar vein to tomp's comment: if someone's interested in the bonus project of implementing macros, I've implemented a simple lisp with first class macros (fexprs): http://github.com/akkartik/wart. Moreover, you can write anonymous macros out of more primitive constructs. Over time it's accumulated other features: http://akkartik.name/post/wart. But hopefully it's still easy to understand, because it has thorough unit tests. If you have a question about some line of code you can try changing it. Seeing what tests break is a great way to get feedback as you learn, in my experience.
In Learn C and build your own Lisp
Great article. But it seems somehow unfair to characterize this as a 'one-line change'. He had to run various performance experiments that will be invisible to someone else reading the code, and the optimal prefetch distance might silently shift over time, like say if each object grows larger. It's a little like the Carmack magic number in that respect: https://en.wikipedia.org/wiki/Fast_inverse_square_root I've had this idea for some time that our representation of programs is fundamentally broken, because it excludes a detailed characterization of the input space. I took a first stab at a solution with http://akkartik.name/post/tracing-tests, which lets me write all my unit tests at the top level, showing how each piece of functionality augments the space of inputs my program can address. See, for example, https://github.com/akkartik/wa
Read the rest
rt/blob/3039fe6d03/literate/02.... I suspect reading other people's 'code' could be an order of magnitude easier and more convenient if 'code' included the right information. I'm still working to flesh this idea out.
In How I Got a 2x Speedup With One Line of Code
Yeah, I agree. Lisp is best with small functional blocks. "It used to be thought that you could judge someone's character by looking at the shape of his head. Whether or not this is true of people, it is generally true of Lisp programs. Functional programs have a different shape from imperative ones. The structure in a functional program comes entirely from the composition of arguments within expressions, and since arguments are indented, functional code will show more variation in indentation. Functional code looks fluid on the page; imperative code looks solid and blockish, like Basic." -- On Lisp (http://www.paulgraham.com/onlisptext.html; page 30) As function size grows, it doesn't look quite so pretty. An example from news.arc: (if no.user (submit-login-warning url title showtext text) (~and (or blank.url valid-url.url)
Read the rest
~blank.title) (submit-page user url title showtext text retry*) (len> title title-limit*) (submit-page user url title showtext text toolong*) (and blank.url blank.text) (let dummy 34 (submit-page user url title showtext text bothblank*)) (let site sitename.url (or big-spamsites*.site recent-spam.site)) (msgpage user spammage*) (oversubmitting user ip 'story url) (msgpage user toofast*) (let s (create-story url process-title.title text user ip) (story-ban-test user s ip url) (when ignored.user (kill s 'ignored)) (submit-item user s) (maybe-ban-ip s) "newest")) There's a multi-branch if here, with lots of else ifs. But it's hard to see. In my toy dialect[1], I've added[2] colons as syntactic sugar that expands to nothing: (if no.user : (submit-login-warning url title showtext text) (~and (or blank.url valid-url.url) ~blank.title) : (submit-page user url title showtext text retry*) (len> title title-limit*) : (submit-page user url title showtext text toolong*) (and blank.url blank.text) : (let dummy 34 (submit-page user url title showtext text bothblank*)) (let site sitename.url (or big-spamsites*.site recent-spam.site)) : (msgpage user spammage*) (oversubmitting user ip 'story url) : (msgpage user toofast*) : (let s (create-story url process-title.title text user ip) (story-ban-test user s ip url) (when ignored.user (kill s 'ignored)) (submit-item user s) (maybe-ban-ip s) "newest")) [1] https://github.com/akkartik/wart#readme [2] http://arclanguage.org/item?id=16495
In Source Code Typography
I often run git blame to track down and reread old commit logs. But yeah the new codebase kinda dilutes the point because I'm changing so many things at once. It's an exercise in making a codebase as self-documenting and approachable to outsiders as possible in a variety of ways. The numbered filenames are one example. Each file is intended to be self-contained, and later files build on earlier ones. No need to hack on makefiles to add a new file. Just create it, number it right, and it'll get compiled, with access to everything in earlier files. Since this is a lisp interpreter the ethos translates to lisp functions as well. I can improve performance by deleting a lisp function, reimplementing it in a new .cc file, and restarting the interpreter. The interpreter will transparently run make, which will transparently compile the new file.
Read the rest
And tests on the lisp function will continue to pass if I did the translation right. There's a README (http://github.com/akkartik/wart#readme) that tries to convey my goals, but I haven't had too many people read it yet. I'd love to hear what isn't clear. You aren't supposed to know lisp to understand it.
In All the Ganglia. The Nerves. On leaving a programming project unfinished.

Showing the 15 most recent of 30 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.