Arc Forumnew | comments | leaders | submitlogin

Yeah, I guess I could have phrased that better.

In my mind, "not necessary" didn't imply "necessarily not", but I can see how it might sound like I wanted to just let the world burn and walk away. I only intended to suggest not worrying about it and not putting that popular-opinion cart before your original-objective horse.


Why are runtime type errors acceptable in Lisp but not in Java? Or was there some other reason for dynamically-scoped variables to be acceptable in Lisp?

Yes, if it was a game that would be true, because (within the rules) you can't just walk away from the game, and there is only one winner.

However, software like most things in real life isn't a game. There aren't any rules. You can just fork it, or ignore it, or employ any number of other subversive strategies.

You keep thinking in zero-sum terms, and in ultimate terms like "doom" that I don't think actually apply to the domain.

How do you define "doom" anyway? Sounds like there's an objective you want the meta-system to be moving toward, but you expect it to fail. I'm not convinced there is such a perfect destination, or that there's anything like predictable determinism in the system.


I feel like much of the disagreement in this conversation comes down to a question of degrees, as you mentioned in your other comment.

Many of your comments seem to imply an absolutism that you may not actually mean; I know I sometimes overstate my arguments when I'm trying to keep a point concise anyway. Or possibly I'm being pedantic by requesting that you qualify all of your claims.

I agree that laws and software last a long time. Most other systems would wear out and need replacing eventually. The need to explicitly repeal laws gives them incredible inertia, and is a good argument for sunset clauses. At first glance software seems like it could last as long, since it's only information, but in practice I think the underlying systems and surrounding environment change enough that the software must also adapt or die.

Perhaps it's a change in perspective. If you look at the software application itself, it doesn't have to change and can last forever. But systems change, and eventually leave the application behind. That's a (possible) victory for those of us on the side of active replacement.

> As a result they're impossible to safely delegate.

That doesn't follow. Long-lasting results could just as easily be an argument for hiring an expert to do the best possible job. E.g. an architect for a stone cathedral vs. a diy shed.

> You can't pay programmers to write code for you.

I would think that the existence of the software _industry_ rather than a network of niche artisans flatly disproves this statement. People do hire others, and it proves to be a more effective and profitable way to develop usable products than trying to do everything by oneself.

> because they'll drown you in technical debt

If that's the only argument against hiring someone else, it's not a very good one. For one thing, companies can choose to set style and documentation requirements, so that little information is lost when they leave. On the other hand, I am quite capable of drowning myself technical debt on my own; I might even be more likely to take shortcuts if I have to do everything myself. To paraphrase my father, software written by someone else is hard to understand and maintain, and software you wrote more than 6 months ago was written by someone else.

I think my main point in all of this is to try to convince you not to be so pessimistic, because a lot of the things you're focusing on either aren't actually that big of a problem, or aren't necessary to solve. Part of it is also about strategy. I generally agree your design decisions and objectives, but based on the HN discussion it's going to be a hard sell if you present your ideas in direct opposition to everything else. Someone pushed back on the idea that everyone needs to understand implementation details. Truth is, they're right, but instead of explaining how the availability of details can still be valuable even if they aren't needed by everyone on a daily basis, you tried to counter. I'm not holding that against you; I have a tendency to do the same thing (it's easy to just respond point-by-point to all the flaws in a post, and forget that I should be working towards an objective). But I think you can do better.

Still, kudos to you for getting this far in the first place. I have too much fun talking about ideas to actually build them.


That makes sense. I think my criticism is really for Guice, which has the effect of making Java a dynamically typed language that can throw type errors at run-time. But if you start out with a dynamic language like Lisp, dynamically-scoped variables are much more acceptable.

Regarding the term "injection", I'm following https://en.wikipedia.org/wiki/Dependency_injection. It feels like a marketing term, but now we're stuck with it.


Yes, I suppose "dependency injection" as a concept doesn't actually require anything sophisticated like a framework or IoC containers etc. But the term "dependency injection" sounds to me like it's doing more than just passing in a parameter, and I normally wouldn't expect it to be used unless it meant something more.

I think that's because "injection" is active. It sounds like intrusively making some code use a different dependency without its explicit cooperation. Passing parameters to a function isn't really "injecting"; it's just normal operation.

> Automating the injection decisions defeats most of the purpose of DI, I think.

I don't know about "automated" decisions, but the value of something like "injection" to me seems that you could avoid explicitly mentioning the dependencies at intermediate layers of code, and only reference them at the entry points and where they are used. The way your code works, every function that depends on anything that could call 'print has to have a screen in its parameter line. For Mu, that may be a reasonable design decision; you want to keep things transparent and obvious every step of the way, and don't want to add any more complexity to the language than necessary. However, I think there is a case to be made for something like dynamic variables to improve composability in a higher-level context. That's a discussion for a different language (like the one I'm designing, which gets around the various problems of scoping by making almost everything static anyway).

This is probably talking past your point, but I'm trying to argue that there might be value in some degree of DI beyond parameterizing functions. I have not necessarily justified "automation", and since I don't have a good definition for that anyway, I don't think I'll try.


It's the difference between a game of chess and a game of Nomic (https://en.wikipedia.org/wiki/Nomic). Being able to change the rules makes every move much more powerful, and the game much more chaotic. If you have inequality between people who can make moves and people who can't, I think that dooms the system in the long run. (It may well be doomed in the long run anyway, but again, I think there's only one direction to go here.)

Ah, I see.

> > It is not necessary to save or change the rest of the world.

> I absolutely disagree.

In my mind the poles of this disagreement were zero change to the world vs non-zero change to the world. I was saying it seems futile to try only to change myself but not some others. The thought of a universal quantifier, zero vs infinity, that didn't occur to me at this point.


Maybe I should clarify that I mean dependency injection but not a dependency injection framework. Automating the injection decisions defeats most of the purpose of DI, I think.

Here's an example of the ways that laws affect your life in umpteen ways: http://akkartik.name/post/2010-12-19-18-19-59-soc

> You haven't clearly explained how software is like laws, or made clear arguments for why leaving laws to experts is the source of the problem, why software would suffer the same fate, and what should be done differently.

Both are systems of rules that have unanticipated long-term effects on the lives of people. As a result they're impossible to safely delegate. You can't pay programmers to write code for you, because they'll drown you in technical debt that only washes over you long after they're paid and gone. You can't expect legislators to make good laws because they'll constantly take the easy way out and leave a mess for future generations to deal with. (Now maybe you can't expect people to make good laws in general. But if there's a way out, that's the direction to look for it.)


Based on the fact that we're discussing this on the arc forum, and you've built languages to replace-ish assembly and C, and I'm designing a language to replace pretty much everything else, I'd say we're much more on the same page than nearly everyone else.

This particular back-and-forth was that I said we "don't need to save the whole world", and you "absolutely disagreed". It sounds like you've agreed with all of my points or softened yours, so I'm not really sure where that leaves us.

Somewhat ironically, I sometimes think of the design I keep hinting at basically as a rebase of most of computer science onto a virtual computer with 32byte pointers to ROM.


> Such abstractions are great -- if you can find them. Once in a lifetime things.

Maybe we're discussing different when it comes to abstractions.

They don't have to be paradigms and approaches, like your reference Lisp as a whole. I'm not even sure how "Lisp" fits into the "don't lie" model; it's on a completely different scale. I'm just concerned with how things are represented. UDP doesn't lie; it says up front that its datagrams are unreliable. TCP on the other hand pretends to be a reliable ordered stream; that pretense comes at a cost, and sometimes fails anyway.

Another one that came up recently was Golang vs Rust and how they handle file paths (or pretty much anything really). Golang takes the path of least short-term resistance, and tries to make things easy by pretending paths are strings. Turns out that isn't always true. Rust in contrast works hard to ensure correctness; for file paths they use a custom OsStr type that covers the possible cases. There are lots of examples where Rust uses result types to present a more complex but accurate set of outcomes.

> Not lying is not easy. You have to take responsibility for basically every misunderstanding someone may make with your ideas.

That sounds like a good point, but after thinking for a second I don't think I agree. If I was really trying to guarantee that no one misunderstood my work, that would be a problem. However, that's not the objective of the principle, which is a guide for choosing representations. Apply the same logic to human honesty. Is it really that impossible to avoid lying in conversation? Am I suddenly lying if someone misinterprets what I said?

I think avoiding lying in abstraction design should be similar to personal honesty in conversation with other people. Don't intentionally misrepresent something. If someone misreads the specification and makes a mistake, that's their fault.

I will go a step further though and say that simply having a disclaimer doesn't match the spirit of the principle. Surely the TCP documentation (or a little critical thinking) will reveal that it can't truly make streams reliable. The problem though is that it tries, and wants you to believe it does except for "rare circumstances." I would prefer a system built using UDP with full expectation of the potential failures, than one on TCP that thought it had covered all the edge cases that mattered only to run into a new one.

I guess the Erlang "let it crash" philosophy is almost a corollary. If you don't have illusions that your code won't crash, and prepare for that worst possible case, then any unhandled errors can just be generalized to a crash.

The purpose of the principle is to make better designs by giving more accurate and flexible options to the user. If a design choice is between exposing the internals as they are, or trying to cover up the complexity to coddle the user, choose the former. Give the user the flexibility and power of handling the details themselves (possibly via library). Don't lie.


I have a metaphor I like to use sometimes to describe the perils of over-extending metaphors. That's not exactly the same, but maybe it's relevant enough to share.

  A metaphor is like an old rusty wheelbarrow; if you put too
  much into it and push it too far, it will break down, you'll
  trip over it, cut yourself, get infected with tetanus, and
  end up in the hospital filled with regret.
I'm still not satisfied with the ending and tweak it slightly each time. It fits the pandemic humor rather well though.

Yep, that answers my question perfectly.

You had said "dependency injection" somewhere, so I thought there might be more to it.


> my analogy with legislators a superior alternative to computers as cars or planes.

I think what you're trying to say is that modifying software is more like amending a law than tinkering with a personal car. Legislators are to laws what programmers are to software, and we have gotten bad results from leaving law to experts, so we shouldn't leave software to experts either.

You haven't clearly explained how software is like laws, or made clear arguments for why leaving laws to experts is the source of the problem, why software would suffer the same fate, and what should be done differently.

> So the fallacy in the argument is that implementation details are relevant to everyone. They aren't. They're relevant to system builders and people who like tinkering because that is a separate domain to the user domain.

Quoting the guy you responded to, his main point seems to be that implementation details aren't relevant to everyone. He uses cars as an example; some people tinker, others just drive. Empirically his point is borne out: most people just use Instagram on their iPhone without any concern for implementation details.

> Given the outsized effect that software infrastructure has on our lives, I think computers are more like laws

I think I disagree regarding the "outsized effect". I don't have many personal interactions with the law so I don't think it effects me much, but I use software every day so I feel its effects. Also, the effect that laws have is community-wide and enforced, but nobody is compelling me to use any particular piece of software.

> laws are too important to be left to legislators

Too important in what sense? What other alternatives? The problem is, laws are fundamentally a community rather than individual concern. Laws are hard to modify because they can only be enforced by a community as a whole. You could pick a different process, but however you propose changes to a law they have to survive the politics of the community to be enforced, and to the degree that a community does not unanimously agree on general interests those laws will be created for special ones. In contrast, software use is voluntary and personal.

> it would be great to separate the user experience from the developer experience if we knew how

The points you take up in this argument are 1) implementation details are relevant to everyone, and 2) the developer and user experience can't be separated. With these points, I disagree. There are many users who are not developers, and don't care about internals. Just like both cars and laws, most people don't care about the details until they have to. At that point, some will look for professional help (mechanic, lawyer), and others will do it themselves. Yes, the details are important, but that's why we hire mechanics and lawyers who know them well, and not an argument for learning them yourself.

However, your main point was that mind-bikes should be see-through. The argument that not everyone cares about internals is actually irrelevant to that point, and I don't think you need to quixotically defend against every attack. Details should be accessible simply because that makes tools ergonomic and maintenance easy, regardless of who's doing it. I really like the fact that the oil filter on my car is right on top of the engine; makes changing it really easy. Even if I didn't change the oil myself, it would still make the task easier for the mechanic. That's not just altruism either; if it's easier for him, I probably get better service at lower prices.

I don't always care about the details, but when I do, I like them to be accessible and easy to understand.


> My reference to math was not an analogy, but a description of an approach to abstractions. In Mathematics, abstractions are held to rigorous standards: they must actually be proven to behave as claimed, any exceptions must be included in the definition, and any application outside of the assumptions is invalid or at least highly suspect and must be justified.

Such abstractions are great -- if you can find them. Once in a lifetime things. Pretty much nothing we use in software comes close to this. Not even Lisp.

Not lying is not easy. You have to take responsibility for basically every misunderstanding someone may make with your ideas.


I think we're saying the same thing but misunderstanding the words used by each other. Don't get hung up on my careless use of the word "win". Like I said elsewhere, I'm not trying to genocide competing ideas :)

The rest of this thread is just a reminder to me to avoid analogies like the (ahem) plague.


Heh, I notice now that I never actually said "replace" in the paper. I said, "take it out and think about the problem anew." That sounds like we're on the same page?

> ...we'll see over time whether coming up with the right abstractions is a sufficient approach

What other approaches are you considering? And sufficient for what purpose?

> it's not at all inevitable that the right abstractions will win out

I addressed this more thoroughly in my other wall of text, but I don't see why "winning" is a necessary objective. If you have an abstraction that works, it won't suddenly disappear on you. Or maybe I don't understand. "Win out" over what? And by what standard of success?

> We have to very carefully set the initial conditions to ensure the paths to them aren't prematurely discarded.

That's a slightly different consideration, trying to pick initial conditions that result in better abstractions. Honestly, it sounds like premature optimization and harder than the halting problem. In some ways though I think the general principles I mentioned are precisely intended to address this challenge. Basically, whenever you come to a design decision where you don't know what the right answer is, or if there might be more than one, don't hide the complexity but pass it on as flexibility to the user. The system stays correct and transparent without lying about how it works, and the user doesn't lose any options. This approach seems to satisfy your desire to not prematurely discard paths.

> The analogy with math is misleading here, because math doesn't have syscalls

My reference to math was not an analogy, but a description of an approach to abstractions. In Mathematics, abstractions are held to rigorous standards: they must actually be proven to behave as claimed, any exceptions must be included in the definition, and any application outside of the assumptions is invalid or at least highly suspect and must be justified.

> syscalls let us do fairly arbitrary things. Given this much power, "don't lie" is about as enforceable as it is with legislators.

It's not enforceable with mathematicians either; the best we can do is read eachother's work to check for such lies and avoid using flawed work. Such care is more important in mathematics where any flaws can ruin a proof, whereas in software a bug might only be reached in rare edge cases and even then we can turn it off and on again. That explains why the cost of failure is lower, and programmers don't apply the same effort to write flawless code. But what I'm proposing is not "don't let anyone lie" which sounds impossible, but "don't lie", which is a call for personal integrity. It's a design decision, in which integrity and transparency are chosen over comfort and simplicity. That decision is probably just as costly in software as it is in life, but hopefully proves just as rewarding.

> ...applying it becomes something outside of math. An externality.

That just sounds wrong to me, and needs a stronger argument. It may be a pain to write out all the preconditions and postconditions describing the whole state of the computer and environment (I certainly wouldn't recommend it), but that doesn't mean it is conceptually impossible and thus "outside math". However, thinking that way is mostly irrelevant anyway. Instead, I propose the simpler objective of not misrepresenting what could happen. "Not lying" is not the same thing as "telling the whole truth". Math uses pretty broad lower and upper bounds all the time; precise answers aren't always available, but we can still avoid claiming things we can't prove.


I mostly agree with this. Replacing all of existing software isn't anywhere near on my radar. My goal right now is just for Mu to not die :) You're right about going around things rather than redesigning them. Isn't that what I'm doing? I think this is perfectly in keeping with "replace vs paper over". There's no universal quantifier attached that requires the old thing to be replaced everywhere.

I was only using the analogy with pandemics to point out that there are situations where secondary consequences exist, even if it superficially seems like one can go one's own way. I didn't intend to suggest Mu provides any sort of immunity to anything.

> Unwinding decisions exceeds individual capacity only if you're trying to rebase the rest of the stack onto your changes. That is, only if you try to save the world, which is begging the question.

I think I'm losing the thread of this particular back and forth. Perhaps we're saying the same thing, and you took papering over vs replacing to be more mutually exclusive than I intended. I think it's existential for replacing to take some mindshare away from papering over, because of the overwhelming tendency for everyone around us to go the other way. Once you start talking about not having to rebase the rest of the stack, I feel like you're in my replacing camp. Functional replacement rather than sub-system replacement.

> And now that you've done them, I don't think anyone will have to. They might want to change something, but it will hopefully be much easier to build on your work than it was for you to do it in the first place.

Hah! Thank you, but don't underestimate humanity's ability to forget.


At the moment my ideas on all these areas are extremely hand-wavy. Ranging from "you don't really need it," to "somebody else's problem" :)

I feel fairly strongly that I don't want to add very many more layers. Ideally just one more. As more high-level languages get added, I want the lower levels to be integrated into Mu, so that they can in turn become the new HLL. But that's just an aspiration at this point.

One thing somebody else suggested was Plan9's assembly syntax. It may provide some inspiration for making SubX portable.


> I absolutely disagree.

Maybe I should have said more precisely "it's not necessary to save the rest of the world at once". I think it comes down to something like the second law of motion: F = ma. There's no reason you couldn't move the whole world, it's just a trade-off between time, distance, and force. I think a lot of people get stuck thinking they have to change everybody before they can get moving, or that the idea isn't a success unless it "wins", but neither is true.

New ideas rarely displace old ones. It seems like it in computing, because the space has grown so rapidly, but I would almost wager that most language and platforms are used at least as much now as they were at their peaks, just because there are so many more computers and developers now than there used to be. From that perspective, new ideas don't defeat old ones, they just capture more of the new frontiers. As new companies are founded, and new developers graduate from college, they adopt new technologies while the old teams general stick with what they started with. Sometimes they fail or reorganize, but many times they persist with their old systems.

We could be disheartened by that realization (Cobol and TCL never died...), but I think with the right perspective it can bring more hope. Lisp hasn't actually been destroyed, it just got outpaced - there are probably more people using more variants of Lisp now than at any point in history. As such, we don't have to be disappointed when we don't convince people to switch to our model - it was foolish to expect that in the first place - instead, we can look forward to whatever positive benefit our work does bring to those who adopt it.

> This argument is akin to saying one can survive a pandemic without saving or changing the rest of the world. It's only true if you don't take costs into account. It's far cheaper to stay immune in the presence of herd immunity than without.

To address your counter-argument more specifically, I don't think the analogy is appropriate. The pandemic analogy may be very apropos to our current global crisis, but doesn't describe how ideas work. People don't get "infected" by bad ideas in the same way they are by viruses; they learn and adopt ideas intelligently. We don't have to worry about maintaining our immunity as a small community.

However, other aspects of the community model are more appropriate. If we don't reach critical mass, or have clear motivations and objectives, we'll eventually dissipate and move on to other things. See: the arc language community. Everyone doubtless benefited from the experience, but the outcome wasn't a future in which all of us use Arc as our primary language. So yes, we should invest in community and growth, but that's not the same thing as trying to save the world.

Also, there are economies of scale that come with larger communities, that I think aligns with your reference to "cost" much better. In a small community with an early-stage technology, everyone has to build things from scratch before they can use them. Larger, better established communities benefit from the work of those that came before. No argument there. But that's not really an argument for or against "saving the world" either. It would almost be a category error to observe the advantages of being an established project and say that new one needs to adopt the strategy of "being established".

> Over-engineering has a way of compounding. It's only obvious that something is unnecessary for a brief window of time. Then people start using it, and it starts becoming load-bearing. Compounding efforts to unwind past decisions quickly multiply until they exceed individual limits of effort.

Again, I think this is focusing on the existing mountains and edifices and forgetting we can just go around them. It would indeed be a pain to redesign x86, given that it's main advantage is that it is x86. But instead some people developed RISC-V. Also, I don't think it's true something will only be obviously unnecessary at the beginning, and seem more essential later; the fact we're having these discussions (and things like the UNIX Hater's Handbook) attest that people are quite capable of seeing faults and imagining alternatives. Unwinding decisions exceeds individual capacity only if you're trying to rebase the rest of the stack onto your changes. That is, only if you try to save the world, which is begging the question.

> Even within an individual's limits, I care very much about people being able to make changes to their computers without devoting their lives to the endeavor. We all should have lives outside computers. We should be able to modify our compilers without spending years understanding them. Just by poking and tinkering and getting feedback for an hour here and there.

I admire that objective, and am also addressing that issue in the system I'm currently designing. Imagine being aware of any changes someone made to their own copy of an application without requiring them to submit a pull request, and merging it in if you like the work. No need to track forks or use github pages, the development tools themselves track relevant changes that other people are making to the same codebase. That way, the first time anyone finds and solves a bug, everyone else benefits. And you don't have to worry about your local installation and the upstream repository getting out of sync if you make a personal modification; they aren't treated any differently. I think that would boost open-source development productivity immensely. Such is what I am trying to design.

> Nobody should have to go through what I've been through.

Thanks for your efforts. And now that you've done them, I don't think anyone will have to. They might want to change something, but it will hopefully be much easier to build on your work than it was for you to do it in the first place.

> At the very least we need some critical mass of people to care about implementation complexity. One lone voice seems really fragile, because tiny embers of light can get put out from many sources of bad luck, no matter how lofty their intentions.

I agree with that, and that's where I think I'll close. What we need is sustainable growth and community development, so that good ideas don't die out. But we shouldn't sacrifice any other values for the sake of growth, much less dominance. If our ideas and community have a larger R0 they will eventually become dominant, but I don't see the need to exhaust ourselves forcing the issue and wasting time and energy overcoming irrelevant resistance.


That would be excellent!

Any function that needs to print to screen must take a screen argument. All the way up the call chain. Then tests simply pass in a fake. I end up doing this for every syscall. Memory allocation requires an "allocation descriptor". Exit rewired an exit descriptor that can be faked with a continuation. And so on. Does that help?


> I'd love to build a capability-based OS at some point.

I'm designing a capability-based lisp at the moment that will at some point need an efficient VM implementation and byte-compiler, and I was planning on turning it into a microkernel OS (where the interpreter is the kernel); maybe if I actually build enough of it in a reasonable timeframe we can meet somewhere in the middle and avoid duplicating effort.

> Fakes are just code like any other...

I wasn't really asking how fakes were handled, but rather how you inject them during a test.

> My goal is just to pave everything within a few hours of Hamming distance from my codebase. That seems like a big improvement in quality of life. I'll leave harder problems to those who come after me :)

Now you're starting to sound like my earlier comment about not needing to save the whole world. :P


Concurrency is an interesting topic. I hadn't thought of that when reading about SubX, which mostly covered the part of assembly/machine language I'm familiar with. I always wished, though, that my assembly class (or an advanced sequel) had covered some of the more advanced topics for modern processors instead of the 8080, like how to set up memory management (the boot process on newer CPUs is rather involved...) and how multicore computation works. I know all about the higher-level implications, and can use threads etc., but have never actually learned how those things are handled at the bottom. Or syscalls etc.; I've only ever used them, not implemented them. That's a bit out of scope for the discussion, but something I look forward to "seeing-through" your future work on accessible internals. :)

I guess part of my curiosity was targetted at how you would build Mu up to handle performance and portability. Although the way they are usually implemented does obscure the internals, it seems possible that some compromise might be found. Any thoughts?

Perhaps the answer is at the higher lisp (AST / intermediate representation) level. Mu and SubX stay perfectly aligned to the final binary, but a code generator could optimize the AST and generate code in Mu for the target platform.

On that note, have you thought about giving them a more accessible API for such purposes? Or even just an s-expression syntax? Otherwise the target format for generation is limited to text, which would lose a lot of semantics in translation. Also, how are you thinking of maintaining introspection continuity across levels as you add more layers? Maybe something simple like source map support?


You're welcome! And thank you for the thought-provoking paper and continued discussion. I was a little worried when I posted the first round of comments, after I saw how long they turned out to be.

Now for round two...


Thank you very much for your comments! It's funny, given all the rhetoric around 'feedback' in our society, just how hard good feedback is to come by. When I wrote this paper over 3 months I told myself that my goal was to get 3 people to think hard about the details of what I'm doing. Based on this thread I'd say I've gotten on the scoreboard there.

Oh yes, we're very much thinking in similar ways here. I'd love to build a capability-based OS at some point. For now my ambitions are more limited, to just support testing syscalls with a pretty similar shape to their current one. But it's very much something I hope will happen.

This is the method of science as I understand it. I'm trying to change as few variables as possible (still very many), in hopes that clarity with them will open up opportunities to explore other variables.

> How does Mu handle dependency injection?

Fakes are just code like any other. You can see my code for a fake screen in an earlier prototype: https://github.com/akkartik/mu1/blob/master/081print.mu#L95. Or colorized: https://akkartik.github.io/mu1/html/081print.mu.html#L95 ('@' means 'array'). Does that answer the question? I think maybe I'm not following what you mean.

> it sounds like a fragile pattern for testability, since someone is likely to hard-code their preferred output.

The way I see it, it's a losing battle to treat code vs test as an adversarial relationship. All the tutorials of TDD that start out hard-coding a value in the first test are extremely counter-productive.

There is indeed a lot of fragility in Mu's tests. And when I write them I'm often testing the results in ways that aren't obvious. The test may validate that the code a compiler generates is such and such, but is that really what we want? To make sure I have to actually run the code. Occasionally I encounter a situation where I traced something but didn't actually do what I said I was doing. Mu's emulator requires validating against a real processor, and I've encountered a couple of situations where my program ran in emulation but crashed natively. In each case I had to laboriously read the x86 manual and revisit my assumptions. I haven't had to do this in over a year now, so it feels stable. But still, you're right. There's work here that isn't obvious.

The use case for Mu is someone who wants to make an hour's worth of changes to some aspect of their system. As they get deeper into it I'm sure they'll uncover use cases I haven't paved over for them, and will have to strike out and invent their own experimental methodologies like all of us have to do today. My goal is just to pave everything within a few hours of Hamming distance from my codebase. That seems like a big improvement in quality of life. I'll leave harder problems to those who come after me :)


I've had long debates over drinks with a friend about this. In the end we'll see over time whether coming up with the right abstractions is a sufficient approach.

I think it's not at all inevitable that the right abstractions will win out. We have to very carefully set the initial conditions to ensure the paths to them aren't prematurely discarded.

The analogy with math is misleading here, because math doesn't have syscalls. It's all entirely in the platonic realm, and applying it becomes something outside of math. An externality. Computers have syscalls, and the syscalls let us do fairly arbitrary things. Given this much power, "don't lie" is about as enforceable as it is with legislators.

(Here's the comment you're referring to which cited leaky abstractions: https://news.ycombinator.com/item?id=22599953#22604086. In a sibling comment to it I mentioned my analogy with legislators as a superior alternative to computers as cars or planes.)

More