A principled approach to system design and programming
urbit.org
urbit.org
The fact that we have bad platforms does not imply that we have bad systems or bad protocols. I think the rotten core of the bad-ness of the internet is commercial infrastructure. It's prohibitively expensive for users to truly own the most basic commodities of internet infrastructure, i.e names (DNS), numbers (IP) and peers (BGP). These commodities would enable users to make the most of the protocols that we already have.
There are already many projects that try to combat the commercialization of the internet by adding new systems and protocols (Solid, IPFS and ActivityPub come to mind). Users shouldn't only be free within the confines of these systems - they should be first class nodes on THE network.
The spirit of Urbit is spot on but it's at the wrong layer of the OSI model. I have much more faith in people who are trying to operate public networks (i.e NYCMesh) and would like to see more projects that aim to de-centralize the basic commodities of the internet.
Internet infrastructure is not unique in this respect.
The idea that markets optimizing for profitability naturally tackle hard problems requiring immense up-front costs is an attractive lie.
If these costs are inherent to the operation of these systems and protocols, and arguably many of them are, then it sounds like Urbit is on the right track.
For systems, this is going to be painful: you want to be able to do things like "compare-and-swap" which are already an action and a query bundled in one. You need this for 2 reasons:
- consistency: you want the query and the update to be done in one go. atomically
- performance: doing it like this is faster as
you're combining 2 things in one call, iso 2.In theory, it is possible to do "synchronous CQRS", where the update to your write-side is done in a single transaction with the update to the read-side, but that's not how I've ever done it in my (Scala/Postgres based) CQRS app experience.
Ah yes, let's appropriate the Latin word for "peace" and demand everyone use it for paths.
An utterly fanciful take, this. Must be nice to have the time to cultivate and commemorate such a detailed set of principles.
Man, when I read things like this about "design principles" I know I'm getting into a bag of hand wavy opinions with no rigid theoretical proofs. That's fine because not everything is covered. But the minute you say something that is outright false I know you're worthless.
All abstractions leak. I cannot make an abstraction perform better than what's underneath. The performance leaks through every single time. An abstraction will always only be worse performing then what's underneath simply due to the cost overhead of the abstraction itself.
Rust is a great example. It has something called zero cost abstractions, meaning that the abstractions that Rust uses are more complicated than normal in order to maintain the zero performance cost. That is leakage. There is always a cost.
The way it was optimized for IE6 was that the team decompiled it and understood how the JavaScript engine and the garbage collector were working underneath and got around them. The team went out their way to optimize for the large number of emails that are used insided Google.
The project of course was a huge success.
Performance is not within the scope of abstraction. This is a category error.
Also, your claim is incorrect as a general statement. The type structure of a given program is an abstraction of its value flow, but without that abstraction a compiler would not be able to optimize it nearly as well. A typed program will always be faster than an equivalent untyped program.
Creating an abstraction may also allow you to add a transparent memoization layer, which not only can make your program orders of magnitude faster, it can make previously unsolvable problems solvable (see how PEGs handle left recursion).
> Performance is not within the scope of abstraction. This is a category error.
It's not within the scope of abstraction... until it is. When that beautiful abstraction does exactly what you need, but does it too slowly to be usable in your circumstances, performance is not separable from the abstraction. (I mean, yes, performance is part of the implementation of the abstraction, not the abstraction itself. But the moment I have to care about the implementation of an abstraction rather than the abstraction itself, the abstraction has leaked. Performance is absolutely one of the ways that this can happen.)
And, as a concrete example, the STL went to great lengths to specify the performance of their abstractions.
No, that's not what leaky abstraction means. If an abstraction specifies its performance properties, but those properties depend on context that isn't captured by or enforced by the abstraction, that's a leak.
To use your example of the STL, if you can allocate a fast constant-time abstraction at a misaligned address, thus causing it to violate its own performance specification, that's a leaky abstraction.
Having the wrong performance characteristics simply means it's the wrong abstraction, not that the abstraction is leaky.
Performance can be one of those things I have to care about.
> (Almost) Everything should be pubsub?
Gee thanks, I'll just go make everything pubsub. Except the things I shouldn't. The distinction will be clear I'm sure.
I'm not saying I disagree with all their points. But without examples it's not clear what they even mean, let alone how to apply them.
https://github.com/ipenburg/Atkinsome
The script is about 8 years old and depends on MooTools, but shouldn't be too hard to modernize it.
Here's a newer implementation of the Atkinson dither algorithm on Node.js.
https://github.com/rtrvrtg/node-atkinson-dither
Edit: This dither library includes Atkinson:
> A.2 Everything should be CQRS.
Some things require strong consistency, even at the UI level. Eventual consistency is probably the right default though. Unfortunately, we still don't have good abstractions at the language level to manage this.
> A.4 A subscriber shouldn't affect a publisher.
Literally impossible. Either a subscriber is polling the publisher, or the subscriber registers with the publisher to receive updates. Either way, the subscriber is inducing state transitions in the publisher. What other possible meaning can be assigned to "affect" in order to make this precept valid?
> D.1 Academia has many genuinely smart and interesting ideas, but always remember that pursuing them whole-heartedly will never result in a useful product.
Yeah, not at all sure that's correct. Lots of academic research is funded by commercial interests. In fact, currently one of the biggest corporations in the world at this time (Google) started as academic research.
> D.2 Those people (academics) wasted a lot of time finding the right answer, but now that they've done it, you must exploit it.
Spent a lot of time. "Wasted" implies there was a better way.
> D.5 Practice tells you that things are good or bad; theory tells you why. Never use theory to design a good system from scratch; only practice can tell whether the system is good.
I don't think this is coherent. If theory tells you why a system is bad, then that immediately entails what a good system should look like.
What I think typically happens, is that a system designed by theory is so good at solving the problems it was designed for, that it's then applied to similar-but-not-quite-the-same-problems, and thus it becomes inadequate again.
Why those two exceptions? Because those two exceptions are mandatory in the publish/subscribe model. You can't escape them and do publish/subscribe. But the claim is, a subscriber shouldn't do anything beyond that, and that is actually a useful and important claim.
A subscriber shouldn't send data back to the publisher via the return value of a subscribed message, still less via reference parameters. A subscriber shouldn't affect the publisher's performance by taking too long to handle one of the messages. There may be other ways as well.
D.5: There's a difference between theory and practice. If the system is good in theory but bad in practice, then theory will not tell you so. Only practice can tell you.
You can, based on that practice, create an improved theory. That theory will tell you what a good system should look like... in theory. In practice, such a system may still be bad, just in a different way from the previous system. Again, you need practice to tell you this, not theory.
This one... Our entire computing world is a pie of terrible abstractions held together by duct-tape and billions of man-hours spent working fixing those leaks.
People take that as an excuse to be sloppy, which it isn't. You should do the best you can to make non-leaky abstractions. When you have done so, you should look for the leaks, and do your best to plug them. After that, you should do so again. You should really try to make it as solid as you can.
But it will still leak. It will be less garbage than much of what we have to build on, because of the effort you put into making it better. But it will still leak.
2. Simplicity and Unity must be parallel. Pure piping systems meet this principle.
3. Principle should be specific and executable.
4. Principle should not affect flexibility.
5. Principle should be consistent with mainstream modern industrial production principles.
https://github.com/linpengcheng/PurefunctionPipelineDataflow
> Heuristics are evil and should only be used where determinism is infeasible, such as in cache reclamation
are these two things really in conflict? they seem orthogonal, but maybe i’m missing something...
For a more detailed explanation, you can look up the type definition [2] in Arvo (Urbit's Kernel) and here [3] you can find an example of how the stack looks when setting up a timer.
[1] https://urbit.org/blog/precepts-discussion/
Opinion is mine. Apologies to GP if I twisted their metaphor into something much less charitable.