HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
arclanguage
anarki
Arc2008–2025
arclanguage / anarki

anarki

Community-managed fork of the Arc dialect of Lisp; for commit privileges submit a pull request.

116Distinct people
176Mentions
1Explicit recommenders
1.2KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Community-managed fork of the Arc dialect of Lisp; for commit privileges submit a pull request.

GitHub

Where it is used

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

What people are saying

> Anyhow, sorry to derail the black bar discussion. IMNHO Not at all! Just like I love the fact that someone that didn't realize the meaning of the bar, simply asked (and so…

e12e 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 8–Nov 15 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/15/2026, 2:59:53 AM 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 year176 dated mentions

Observed mentions: 2008-04-15 03:02:27 UTC to 2025-11-25 00:11:41 UTC.

05101520252008-01-01 00:00:00 UTC to 2009-01-01 00:00:00 UTC (exclusive): 6 mentions; 0 submissions, 6 comments20082009-01-01 00:00:00 UTC to 2010-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments2010-01-01 00:00:00 UTC to 2011-01-01 00:00:00 UTC (exclusive): 23 mentions; 1 submissions, 22 comments2011-01-01 00:00:00 UTC to 2012-01-01 00:00:00 UTC (exclusive): 24 mentions; 2 submissions, 22 comments2012-01-01 00:00:00 UTC to 2013-01-01 00:00:00 UTC (exclusive): 5 mentions; 1 submissions, 4 comments2013-01-01 00:00:00 UTC to 2014-01-01 00:00:00 UTC (exclusive): 10 mentions; 0 submissions, 10 comments2014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 5 mentions; 0 submissions, 5 comments2015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 9 mentions; 0 submissions, 9 comments2016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments20162017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 5 mentions; 1 submissions, 4 comments2018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 16 mentions; 1 submissions, 15 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 13 mentions; 0 submissions, 13 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 12 mentions; 2 submissions, 10 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 9 mentions; 0 submissions, 9 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 9 mentions; 0 submissions, 9 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 4 mentions; 1 submissions, 3 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2025

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
20082008-01-01 00:00:00 UTC2009-01-01 00:00:00 UTC606
20092009-01-01 00:00:00 UTC2010-01-01 00:00:00 UTC808
20102010-01-01 00:00:00 UTC2011-01-01 00:00:00 UTC23122
20112011-01-01 00:00:00 UTC2012-01-01 00:00:00 UTC24222
20122012-01-01 00:00:00 UTC2013-01-01 00:00:00 UTC514
20132013-01-01 00:00:00 UTC2014-01-01 00:00:00 UTC10010
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC505
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC909
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC808
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC514
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC16115
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC13013
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC12210
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC808
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC909
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC909
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC413
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC202

From the project README

An excerpt from the repository, fetched Nov 12, 2026.

Read the project’s introduction
Anarki A publicly modifiable 'wiki-like' fork of Arc Lisp The intention is to be extremely permissive in accepting patches. For commit privileges, just submit your first pull request or ask on Arc Language Forum. Getting started Installation First install Racket (v7.7 or later), then in a terminal, run: $ raco pkg install sha $ git clone http://github.com/arclanguage/anarki $ cd anarki You can start the interactive repl with: $ ./arc.sh Or start news (and the repl) with: $ cd apps/news $ ./run-news Tutorial If you're new to Arc, or Lisp in general, a good starting point is the Tutorial. Documentation and help If you run into trouble, check Arc Language Forum Arc Language Wiki Arc Language Documentation Arc's built-in help system by typing (help) at the repl News Site Anarki comes bundled with News, a Hacker News style app. Admins To run News, first pick your (the admin's) username, $ cd anarki/apps/news $ mkdir www $ echo __username__ > www/admins You can have multiple admins. Add them all to www/admins, separated by whitespace. Starting the server Warning: If you are already running a News site, migrating to this fork might mess up your site's data. Come talk to us first, and be sure to make a backup before the migration. Now bring up the server, $ ./run-news There'll be a pause while the server loads up, with some messages, then you'll see the arc> prompt. Go to http://localhost:8080. Click on login, and create the account for your username. You should now be logged in as an admin. (Don't worry about user break or error writing messages.) Customization To customize News, change the variables at the top of apps/news/news.arc. To change the port your server runs at, modify apps/news/run-news.arc. Any interactive changes to the prompt will be reflected immediately in the server, without needing to restart it. Hence if you make any changes to apps/news/news.arc, you can load them, by typing (load "apps/news/news.arc") in the repl. If you stop the server or it dies for some reason, previously entered commands are available in the command history (at least if you have Readline installed). HTTPS You may want to serve News securely over HTTPS. An example configuration for running Nginx with SSL as a reverse proxy for News can be found in anarki/extras/news.nginx.conf. Resetting passwords For password reset mails to be sent out, your must set up an email account to send these from, $ echo "someone@example.com" > www/app-email ; email $ echo "smtp.example.com" >> www/app-email ; smtp server $ echo "someone@example.com" >> www/app-email ; user $ echo "1234" >> www/app-email ; password Development Tests Anarki has thorough automated tests. # start in the anarki directory $ ./arc.sh arc> (load "tests.arc") Anarki is explicitly not constrained to maintain compatibility with upstream releases, and compatibility status may swing wildly from commit to commit depending on who takes an interest. To see a list of what's different, type (incompatibilities) at the repl. If you make an incompatible change, please update this list. Anybody is welcome to make changes to Anarki, but please make sure the tests continue to pass. Since we rely on the unit-test.arc test harness, please also run its tests before committing any changes: $ ./arc.sh (require 'lib/unit-test.arc/unit-test.arc) (load "unit-test.arc/tests.arc") (test-and-error-on-failure) Racket interop Racket expressions can be evaluated with the ($ ...) syntax. For instance, (($ crypto-random-bytes) 16) obtains the crypto-random-bytes function from Racket and makes a call to it. If you write lots of Racket code and you just want to use Anarki for part of a bigger project, you can raco pkg install anarki and use Anarki to write Racket modules: #lang anarki (:provide function-for-use-in-racket) (= racket-import ($.dynamic-require ...)) (load "relative/path.arc") (def utility-for-use-in-racket (x) (* x (racket-import x)) Scope Note that Anarki does not have private module scopes; the (:provide ...) section is just there to make it easy to require an Anarki-based library from Racket. Keyword arguments Anarki has some issues passing keyword arguments to Racket functions, but Racket's keyword-apply can be used for that.
Read the full README on GitHub

What Hacker News says

8 sources
Arc is a lisp variant: https://en.m.wikipedia.org/wiki/Arc_(programming_language) The current[1] distribution comes with a hn clone, that as far as I know, is mostly equivalent to the code that used to power hn at some point in the not too distant past. [1] current might not be the best name for it, but as far as I can tell it a) works, b) has been ported to Racket (as opposed to being trapped on some ancient version of PLT Scheme), and c) is maintained (ish): https://github.com/arclanguage/anarki
In Ask HN: Do you use FreeBSD as web server? Why or why not?
e12e·Mar 22, 2016Recommendation phrase
> Anyhow, sorry to derail the black bar discussion. IMNHO Not at all! Just like I love the fact that someone that didn't realize the meaning of the bar, simply asked (and so helped others figured it out), I love your input on accessibility. I'm sure I'm not the only one that hadn't considered the accessibility implications of such "semantic" style/design choices. FWIW, I think it's a bit crazy to use a table with a 1px image and background colour (if I'm reading the source right?), when a "border-top: thick solid black" would do the trick. But then again, HN does stick to a strict 90s table-layout. That still leaves out the question of where/how to communicate the change to those that are vision impaired. I wish development of the HN code was a bit more open - as far as I know, the current code, has evolved[1] a bit from what's currently
Read the rest
readily accessible as FOSS[0] -- but doing something with the layout to help accessibility while still staying true to the spirit of HN really shouldn't be that hard. And I'm sure the result would be greatly helped by some of HN readers that are able to do the change themselves, for themselves. As for general accessibility, I think everyone would want vote-buttons that work (I'm using vimperator, so on Desktop, it doesn't really matter if the arrows are invisible on a big monitor - I just know that the "first" link-hint is "up", and the second is "down", indications of which way one voted (and a grace period to change/cancel), and a way to hide threads. The latter is especially useful if the first comment on a story is a controversial tangent. As for the black bar: Having those that change the bar submit a story at the same time (eg: the "Andy Glover has died"-story that was on the front page earlier), and have the bar link to the HN submission might not be a bad idea. We sadly live in a time where it might not be obvious, even in a relatively small community, for who or what the black bar express sympathy. [0] "Anarki: a publicly modifiable 'wiki-like' fork of Arc Lisp (http://www.paulgraham.com/arc.html)" https://github.com/arclanguage/anarki [1] "Ask HN: Is the Hacker News Team Actively Developing Arc?" https://news.ycombinator.com/item?id=11240350
In Ask HN: Why is there a black bar added on top?
Ok. What I'll describe is a common problem in web programming (and other kinds of programming too) that most people here will probably recognize. We ended up modifying Arc to make the code that has this problem simpler. We weren't sure how well it would turn out, but in the end it was a net win and we kept it. Consider the following function from https://github.com/arclanguage/anarki/blob/master/lib/news.a..., which is similar to what HN looked like before our change. I've added comments beneath the code to explain the relevant bits. (def user-name (user subject) (if (and (editor user) (ignored subject)) (tostring (fontcolor darkred (pr subject))) (and (editor user) (< (user-age subject) 1440)) (tostring (fontcolor noob-color* (pr subject))) subject)) ; Print colored usernames for editors, plaint
Read the rest
ext otherwise. ; 'user' is the account that sent this request, or nil if logged out ; 'subject' is the user whose name we need to print out For example, if you've requested the front page, then 'user' is your account name at the top right of the page (or nil if you're not logged in), and 'subject' is the account name of a submitter of a story on the page. If we're printing a story that says "218 points by tokenadult", then 'subject' is "tokenadult". Now, go back and notice how the only thing the function 'user-name' does with its argument 'user' is pass it to another function, 'editor'. That function looks like this: (def editor (u) (and u (or (admin u) (> (uvar u auth) 0)))) ; Return whether user 'u' has editor privileges 'user-name' calls 'editor' to find out whether the currently logged-in user should see usernames in colors that non-editors don't get to see. This example is typical of HN's code. Every request to the site involves a set of standard variables, like 'user', that last for the lifetime of the current page request. (Two others are the page being requested and the requesting IP.) HN has lots of functions (like 'editor') that need one or more of these variables, but it has even more functions (like 'user-name') which don't need any of them, but make calls to other functions that do. In the code above, both types of argument—the "transient" one ('user') that is merely passed down the chain, and the "real" one ('subject') that is actually used—appear in the function signature. This can lead to confusing function signatures, since it isn't clear which arguments are the "real" ones. There's even an example of such confusion in the code above. The function is called 'user-name' and its first argument is 'user', so you'd naturally assume—I always assumed, reading the code—that the name to be printed is that of 'user'. Wrong! It's 'subject'. This kind of thing can, and did, lead to bugs. Many web frameworks bundle all the request properties (like 'user') into a Request object, and pass the Request around everywhere or keep it around as a field. That works fine; there are lots of ways of solving this problem. Still, it would be nice to know from a function's signature exactly what state it does and doesn't use. We decided to take advantage of the fact that in the HN code, each page request gets its own (green) thread, and Racket, which Arc is implemented in, has thread-local variables. At the beginning of processing a request, we save all the request parameters—including the currently-logged-in user, which we call 'me'—as thread-local vars. We made two changes to Arc for accessing these. The first change we made was to add a form 'the' that takes a symbol and returns the value of the thread-local variable with that name, or nil if there isn't one. So, to get the currently logged-in user, we just say: (the me) ... an odd phrasing, but charmingly succinct. However, we were nervous about scattering forms like '(the me)' and '(the page)' and '(the ip)' willy-nilly through the code. That's basically programming by global variable. ("Thread-local" variables could also be called "thread-global".) Accessing them randomly would wreck the discipline of knowing which variables are used where—a good thing that having to declare them explicitly in function signatures imposes on you. To solve that, we made a second change to Arc: we added a new kind of optional argument to let functions declare which of these variables they'll use. Optional arguments work like this in Arc: (def foo ((o bar 123)) ... If you supply the arg explicitly as in '(foo 99)', then 'bar' is 99. If you omit the arg, as in '(foo)', then 'bar' is 123. If you don't supply a default, 'bar' is nil. By analogy with 'o' for optional, we added 't' for thread-local, allowing you to declare a function like this: (def foo ((t me)) ... Now you can call '(foo)' without an argument and the variable 'me' will be bound to the current logged-in user. If you need more than one thread-local value, that's fine: (def foo ((t me) (t ip)) ... And you can mix ordinary function arguments in along with these: (def foo (bar (t me) (t ip)) ... Say you want a function to take an optional argument and use a thread-local value if the argument isn't supplied. You can do it like this: (def foo ((t u me)) ... That allows you to call 'foo' with an explicit arg, e.g. '(foo "pg")', making 'u' be "pg", or without an arg, '(foo)', making 'u' be the currently logged-in user. We can use that trick to make a new version of 'editor': (def editor ((t u me)) (and u (or (admin u) (> (uvar u auth) 0)))) Now we can call it without an arg, '(editor)', to find out if the currently-logged-in user is an editor. And that is what allows us finally to simplify 'user-name': (def user-name (subject) (if (and (editor) (ignored subject)) (tostring (fontcolor darkred (pr subject))) (and (editor) (< (user-age subject) 1440)) (tostring (fontcolor noob-color* (pr subject))) subject)) We've gotten rid of the transient argument 'user', because it was only 'editor' that needed it, and it no longer needs it from us. This was not a sophisticated language change. Under the hood, the 'def editor' signature above simply translates to: (def editor ((o u (the me))) But it did require changing the Arc implementation to make it work. This change to the language allowed us to simplify hundreds of cases where arguments were passed through functions that didn't actually need them. It also allowed us to clarify the code in many places. For example, now that 'user-name' no longer has any ambiguity about which user is which, we don't need to call its argument 'subject'. It can just be 'user': (def user-name (user) (if (and (editor) (ignored user)) (tostring (fontcolor darkred (pr user))) (and (editor) (< (user-age user) 1440)) (tostring (fontcolor noob-color* (pr user))) user)) All these simplifications were nice indeed, but it wasn't clear at first whether the change was a good idea overall. There's still something magical about pulling thread-local vars out of thin air when you need them. It still feels uncomfortably globalesque. But in the end, two considerations won us over. First, it turns out that this solution strikes a nice balance between convenience and discipline. You still have to declare which variables you plan to use in each function signature, so it's possible to look at the function and know exactly what state it depends on. That's a lot more systematic than loosey-goosey random access would be. And there's something pleasingly crisp about declaring state only at the place where it's about to be used, even though as a style it took a little getting used to (e.g. it's a bit weird that '(editor)' tells you something about the currently logged-in user). On the whole, it was a significant win for expressiveness. Here, being able to modify the language to suit the application was the big win. The new language construct is much more lightweight than any code we might have written purely at the application level, even with Lisp macros. Second, these request parameters like 'user' that we smuggle around in thread-local variables are a genuine design invariant for HN. They're always present, their meaning is always the same, and they never even change value once assigned. This decreases the risk of having them universally accessible through the code. That's the main thing we were worried about, and I was surprised to find how little of a problem it turned out to be. It hasn't led to any bugs. In fact I don't believe it has caused any trouble at all. There is one downside. Some functions that no longer take arguments they don't need are no longer so easy to call from the REPL. For example, to find out how the username "dang" is printed when pg is logged in, we used to be able to evaluate this from the REPL: (user-name "pg" "dang") But now there is no way to pass in "pg" as the logged-in user, and if I evaluate (user-name "dang") that's going to show me how "dang" is printed when nobody is logged in. To get around this, you must set up a thread-local value before evaluating the form. We have macros for this: (w/me "pg" (user-name "dang")) It's not as convenient as just passing an argument to a function, but still nicer than having to, e.g., set up a stub Request object. In practice it's not too annoying. The last thing I'll mention is that the above was a good hack for the HN codebase but probably wouldn't make a good extension for Arc in general, because it's clearly a poor man's dynamic scope, and proper dynamic scope shouldn't be that hard to add to Arc. That would subsume the above hack and give us something both more standard and more powerful. Lexical scope by default, dynamic scope when appropriate is what the Lambda Papers advocated, what Common Lisp does (also Racket, in its verbose way), and by far the best thing for practical Lisp programming.
In Ask HN: Is the Hacker News Team Actively Developing Arc?
I have to admit, I like long files. Arc's compiler is all in one file (https://github.com/arclanguage/anarki/blob/master/ac.scm) and it was a big influence on me. It was just so great to be able to have it open on one side while I wrote programs in Arc. You're right that there are points beyond which a single file becomes too unwieldy, but with a good editor that limit is quite high for me. Maybe 10k LoC. Directories can grow even larger before they start having problems.
In Mu: making programs easier to understand in the large

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.