Arc Forumnew | comments | leaders | submitlogin

Very interesting. This would have made learning Forth easier!

You're right that in general any such example can be refactored to reduce stack operations. That's even a fun game for many Forth programmers to play. (We lispers have our own equivalents.) But it usually makes the code less comprehensible, in my experience.

Yes, that is definitely simpler, although the formula is more esoteric. At least, for me, it triggers the "I was taught this in school and haven't used it since" filter.

Maybe something like:

  total-price cost-per-item number-of-items shipping-cost-per-item rush-cost = cost-per-item number-of-items * shipping-cost-per-item number-of-items * + rush-cost +
This doesn't seem super great; it could be refactored to use each thing once, but it's late and I can't think of any better example.

Yeah, some other polynomial might be a better example. The Pythagorean formula has a clean separation when it comes to which args each term uses. How about the roots of a quadratic equation when you start out with a, b and c on the stack?


Is it that you're trying to reduce stack juggling? That's hard to understand from simple examples (that you need to have to grok the syntax). Maybe compare something like finding the hypotenuse of a right triangle given the two other sides. Without naming arguments, you'd have to do something like:

  hypot = dup * swap dup * + sqrt
But with named arguments:

  hypot x y = x x * y y * + sqrt
That seems a little easier to read, but even the original isn't so complicated. Is there an example that makes it more obvious why it's better? Even your `square` example is more tokens than with stack juggling (without arguments, `square = dup *` is just four tokens, compared to six with named arguments). I'd say the with-arguments one is easier to read, but I've far less familiar with stack-based postfix programming.

That's a great point. As I've built this, the keystone example in my mind has been the `square` function:

    x square = x x *
So it's really funny that I haven't added it yet. Now added.

Super interesting! I like how you can zoom into the system, almost like using a microscope to see how code works.

One thing that isn't quite clear from your examples here (and maybe that wasn't the point, but I am wondering): why have a different stack when a function is called? Your examples would work fine if they used the same stack. That is, if functions used the same stack, `1+` could be defined as `1 +`.

Perhaps it would be more clear if an example used two variables, or used the same variable twice.

Sounds interesting. Sorry about the RSI. Went to using an ergonomic keyboard, which seems to have squelched it for me.

I've been hanging out on the Handmade Network Discord channel recently. Mostly because I've developed RSI so can't work as much on Mu, and because I'm bored staying home for months on end.

The site seems to be some sort of offshoot from a channel where a game developer used to screencast himself developing a game:

From what I can piece together, Handmade Network periodically has some sort of Jam for a few days where a few people hack on projects following some theme.

The most recent theme was Lisp:

Flip was built for the Lisp Jam. You can see a demo about it here: (though you'll need to sign up to that Discord group)

There was a discussion about it, a sort of debrief after the Jam, that you can read at

It's already sparked a bunch of ideas for me: So decent payoff on being bored and finding this community :)

Tell us more
2 points by zck 58 days ago | link | parent | on: Still alive?

I use a static site generator I wrote in Arc to generate two websites. It's more fun to do that in Arc than any other language I know of.
2 points by akkartik 61 days ago | link | parent | on: Still alive?

It's still running HN :)

Previous thread:

Edit: Another thread from 5 years ago:

Thanks! The goal is to make it _way_ easier to reproduce a single test, and investigate what's going wrong.

And it's a testament to the power of Lisp that I didn't have to change much to make it happen.

This is awesome. Particularly the instructions for rerunning a single test in isolation.

"See, most data structures aren't really data structures at all. They're _acceleration_ structures _for_ data."

I just encountered the GraphBLAS project which feels like maybe a more mature approach to this idea:

Well... That's a good question.

I haven't read any more than a few papers on it, and maybe only one of those in depth (which I'll mention below). Mostly I'm going by forum threads, wiki articles, and the design choices certain languages make (like Inform's multimethods and Haskell's type classes).

As far as I understand the history, Philip Wadler's work basically defined the strict parameters of the expression problem and explored solutions for it. Separate compilation and the avoidance of dynamic casts were big deals for Wadler for some reason.

That work was focused on Java, where it's easy to define new classes that implement existing interfaces but impossible to implement new interfaces on existing classes.

The solution I'm most familiar with for Java-style languages is the use of object algebras, as described in Oliveira and Cook's "Extensibility for the Masses: Practical Extensibility with Object Algebras" ( In this approach, when you extend the system with a new type, you define a new interface with a generic type parameter and a factory method for building that type, and you have that interface inherit all the existing factory methods. So you don't have to solve the unsolvable task of implementing a new interface for an existing class, because you're representing your types as type parameters and methods, not simply as classes.

So I think the main subject of research was how best to represent an extensible program's types and functions in a language like Java where the most obvious choices weren't expressive enough. I think it's more of a "how do we allow extensions to be made at all" problem than a "how do we make all the extensions maintainable" problem.

But then, I've really barely scratched the surface of the research, so I could easily be missing stuff like that.

There is already a wiki that isn't getting much use[0].



It sounds like you mean a JSON generator (emitting JSON) rather than parser (ingesting JSON). Is that right?

>I suppose a more ideal form of the documentation would describe on the "Templates" page that template instances were a tagged type with a certain representation. It could describe the direct implications of that, but it would also have a link to the "Type operations" page for more background.

That would have been tremendously helpful, can we do that? Who actually maintains the documentation, can it be updated?

Indeed. I'm curious: does my interpretation of the expression problem miss what the papers tend to focus on?

Ah, you're imagining us having to write and maintain 1000×1000 individual `defextend` forms someday? Yeah, that does seem like a problem that would not feel solved once we got to it. :-p

I don't think that aspect of the expression problem is solvable in a language design. Instead, it's an ongoing conversation in the community. Sometimes the intent of one feature and the intent of another feature interact, leading people to do a nonzero amount of work to figure out the intent of the two features put together. That work is an essential part of what the community is trying to accomplish together, so it's a cost that can't be eliminated. The intent has to be reflected in the code somewhere, so there will be a nonzero amount of code that serves feature-coordinating purposes.

Regardless, I'm optimistic that although the amount of code will be nonzero, it'll still have a manageable size. To the extent we have any kind of consistency around these feature interaction decisions, those consistent principles can develop into abstractions. The only way we'll have 1000×1000 individual intersections to maintain is if we as a community culture are already maintaining 1,000,000 compelling and distinct justifications for them. :)

Yeah, I suppose you could say the problem is 'solved'. I think of it as a trade-off with costs. We don't know how to achieve zero cost.

For example, I absolutely agree with you that 2 lines per method to extend every table method to some new type constitutes a solution for us. But if we had a thousand such types and a thousand such methods, it may seem like less of a solution. But then `defextend` would be the victim rather than cause of bloat.

Perhaps we should maintain the docs only in plain-text and only within the repo, just because of the operational overheads of managing multiple branches. At least that way they won't seem to lie to a newcomer. I'm curious to hear what others think.

Ah, I think I understand: The fact that you can call `rep` on a template instance.

Not that it helps you much now, but the best documentation for tagged types (including the `rep` operation) is probably this:

You can get to it from the "Type operations" link in the table of contents. Of course, you might've been drawn to the "Templates" page instead.

And unfortunately, that documentation describes Anarki's stable branch. Making documentation of similar quality for Anarki's master branch might be quite a bit more work. So even if you were familiar with tagged types already, the knowledge that template instances were a tagged type on Anarki master might be hard to discover.

I suppose a more ideal form of the documentation would describe on the "Templates" page that template instances were a tagged type with a certain representation. It could describe the direct implications of that, but it would also have a link to the "Type operations" page for more background.

"But in general, having incompatible types easily share functions without sharing too much is an open problem: A language can easily add a method to many types, or add a new type to many methods. But we don't yet know how to achieve both sides."

I'm trying to follow, but I think you and I must have different understandings of the expression problem. That article lists several known solutions to the expression problem. The solution Anarki uses is `defextend`.

What do you mean by "sharing too much"?

Is Anarki's `defextend` technique already encouraging a bloated codebase, or is there some other technique you're thinking of that would do that?

I spend all my time with Arc working on the forum, so JSON is more important to me than it might be to someone else who primarily focuses on the language.

I was working on the data export feature, which before just dumped a user's posts and comments as a printed statement, but I wanted it to export JSON because that's more portable. Racket has a really nice JSON parser, unfortunately all the forum data is templated, so I can't just pass it to Racket because it doesn't understand the type. There may be a way to get it to work at that level but I don't know what it is.

So I had to get an old JSON parser for Arc and rewrite a bit of it to be able to generate JSON from forum data. I think it was zck's parser but I'm not entirely sure ATM. It works, but it's an awkward workaround. Any data coming from the forum has to go through one parser, and any other data, such as from a remote API would be going through another (Racket's.)

>Is making it work just a matter of calling the Racket primitive on `rep.template` instead of `template`?

Yes, I had to hunt around to even know that existed, because it's not in the docs.

> ... with language features like macros and templates that have become ubiquitous, I feel like it's kind of cheating not to just fold them into arc proper.

Cheating how?

It's totally fine to move something into arc.arc if you want to do that. It's always felt like a non-existent distinction in my mind whether something is under arc.arc or libs/. Is Anarki all language or all standard library? Depends on how you look at it. Why does it matter?

> But that's just me... one thing I've learned being here is that I seem to flow against the culture more than with it, so I can just agree to disagree.

This doesn't feel like a disagreement, more like a language barrier. If I understood better I might know whether I agree or not.