4,224 karma · joined May 21, 2015
I'd be interested how feasible complete 2D UIs using dynamically GPU rendered vector graphics are. I've played with vector rendering in the past, using a pixel shader that more or less implemented the method described in the OP. Could render the ghost script tiger at good speeds (like 1-digit milliseconds at 4K IIRC), but there is always an overhead to generating vector paths, sampling them into line segments, dispatching them etc... Building a 2D UI based on optimized primitives instead, like axis-aligned rects and rounded rects, mostly will always be faster, obviously.
Text rendering typically adds pixel snapping, possibly using byte code interpreter, and often adds sub-pixel rendering.
Performance is mostly a consequence of clear and direct code. You mostly don't achieve performance by saving individual copies, but by being in control of the code and architecture.
foo->bar.baz
instead of (in Rust and other modern languages that decided to get rid of the distinction) foo.bar.baz
For example, the former lets me easily see that I can copy foo->bar and I now have a copy of baz (and indeed bar). In a newer language, it's harder to see whether we are copying a value or a reference.I of course get where you're coming from, but don't you think it is intellectually dishonest to try and police certain "obviously true statements"? Isn't it similar to banning kitchen knifes because they can be used to kill? Doesn't it put under suspicion a lot of people who are simply following their intellectual curiosity?
I would argue that the ideas you seem to be advertising can lead to similar societal catastrophes as the ones you're trying to prevent from reoccurring.
For sure, the misguided idea that men and women are absolutely, 100% the same, and that any other outcome than some equal distribution between males and females means there must be mysoginy and patriarchy at work (which I don't say you're proclaiming directly), has lead to a lot of real problems in the past decades. And that includes aggressive propaganda against males in general, and against some actually valuable male virtues as well as female virtues, in some circles.
Never forget that the social neglect is not exactly healthy, and programming isn't actually that prestigious and externally rewarding, except for maybe the compensation that you can currently earn in some places.
Adding that for example in math or other sciences, we are much closer to gender parity.
> Blow is a full-bore fascist sympathizer who also doesn't seem to think that women have any role to play in his profession.
The latter part being argued with a post where he merely opines that the sexes have different interests.
I say that as someone who once made him angry myself when I live-commented in one of his streams because I had a rare disagreement. I was maybe not in shock but at least startled by his reaction. I had presented my disagreement relatively casually.
Now, my impression is that he's tuned down his considerably and developed a more well meaning stance on things over the years. Recently I've found him more on the side of "here's how most people are doing this, I don't like this, maybe I don't think it's sustainable or how you get good results, but anyway here's what I like to do instead, make of it what you want".
It _boggles_ my mind that someone might find it controversial that there are on average differences between the sexes in terms of behaviour and interests. And to throw extremely strong accusations like "fascist" for a totally reasonable assumption or observation like that, I don't have words for that, I think those people have been smoking too much pot.
Elsewhere in the arstechnica comments you linked
> But, uh... this isn't a "Linus Torvalds is a jerk" sort of situation. "Controversial" undersells just how outlandish and inappropriate Blow's views are. Blow is a full-bore fascist sympathizer who also doesn't seem to think that women have any role to play in his profession.
What's going on on these platforms? Is there any serious evidence to the strong claims?
Thank you! I don't get to hear that often. I have to say I was almost going to delete that above comment because it's too long, the structure and build up is less than clear, there are a lot of "just" words in it and I couldn't edit anymore. I do invest a lot of time trying to write comments that make sense, but have never seen myself as a clear thinker or a good writer. To answer your question, earlier attempts to start a blog didn't go anywhere really... Your comment is encouraging though, so thanks again!
A database would not work as mostly unstructured storage for uncoordinated processes. Databases are quite opinionated and require global maintenance and control, while filesystems are less obtrusive, they implement the idea of resource multiplexing using a hierarchy of names/paths. The hierarchy lets unrelated processes mostly coexist peacefully, while also allowing cooperation very easily. It's not perfect, it has some semantically awkward corner cases, but if all you need is multiplexing a set of byte-ranges onto a physical disk, then filesystems are a quite minimal and successful abstraction.
Regarding STL containers, I think they're useful and useable after a little bit of practice. They allow you to get something up and running quickly. But they're not without drawbacks and at some point it can definitely be worthwhile to implement custom versions that are more straightforward, more performant (avoiding allocation for example), have better debug performance, have less line noise in their error messages, and so on. The most important containers in the STL are quite easy to implement custom versions with fewer bells and whistles for. Maybe with the exception of map/red-black tree which is not that easy to implement and sometimes the right thing to use.
You'll notice yourself when you try to actually apply this idea in practice. But a possible analogy is: How many tall buildings are around your place, what was their cost, how groundbreaking are they? Chances are, most buildings around you are quite low. Low buildings have a higher overhead in space cost, so especially in denser cities, there is a force to make buildings with more levels.
But after some levels, there are diminishing returns from going even higher, compared to just creating an additional building of the same size. And overhead is increasing. Higher up levels are more costly to construct, and they require a better foundation. We can see that most higher buildings are quite boring: how to construct them is well-understood, there isn't much novelty. There just aren't that many types of buildings that have all these properties: 1) tall/many levels 2) low overall cost of creation and maintenance 3) practical 4) novel.
With software components it's similar. There are a couple of ideas that work well enough such that you can stack them on top of each other (say, CPU code on top of CPUs on top of silicon, userspace I/O on top of filesystems on top of hard drives, TCP sockets on top of network adapters...) which allows you to make things that are well enough understand and robust enough and it's really economical to scale out on top of them.
But also, there isn't much novelty in these abstractions. Don't underestimate the cost in creating a new CPU or a new OS, or new software components, and maintaining them!
When you create your own software abstractions, those just aren't going to be that useful, they are not going to be rock-solid and well tested. They aren't even going to be that stable -- soon a stakeholder might change requirements and you will have to change that component.
So, in software development, it's not like you come up with rock-solid abstractions and combine 5 of those to create something new that solves all your business needs and is understandable and maintainable. The opposite is the case. The general, pre-made things don't quite fit your specific problem. Their intention was not focused to a specific goal. The more of them you combine, the less the solution fits and the less understandable it is and the more junk it contains. Also, combining is not free. You have to add a _lot_ of glue to even make it barely work. The glue itself is a liability.
But OOP, as I take it, is exactly that idea. That you're creating lots of perfect objects with a clear and defined purpose, and a perfect implementation. And you combine them to implement the functional requirements, even though each individual component knows only a small part of them, and is ideally reusable in your next project!
And this idea doesn't work out in practice. When trying to do it that way, we only pretend to abstract, we just pretend to reuse, and in the process we add a lot of unnecessary junk (each object/class has a tendency to be individually perfected and to be extended, often for imaginary requirements). And we add lots of glue and adapters, so the objects can even work together. All this junk makes everything harder and more costly to create.
> structure things as a loosely coupled set of smaller components
Don't build on top of shoddy abstractions. Understand what you _have_ to depend on, and understand the limitations of that. Build as "flat" as possible i.e. don't depend on things you don't understand.
The idea that the data model looks like
enum XYZ {
case A(B, C, D, E);
case F(G, H, I, J, K);
case L();
case M(N, O);
}
is just not true in practice.I think messaging is one case where it can happen, but even there it's often good to combine fields and share them (and common code) over multiple types of messages. If Messages A and B both have a field "creationTime" or whatever (with identical semantics), it's probably a bad idea to model them as separate fields, because that leads to code duplication, which is unmaintainable.
Maybe I can be more precise by proclaiming that ADTs can be good to be clear what's "there", so they can be used to "send" information. But to write any useful usage code, typically a different representation that folds common things into common places is needed. And it might just happen that field F is valid in cases A and B but not C. That's life! Reality is not a tree but a graph.
That's why it's a bad idea to try and model the exact set of possible states and rule out everything else completely in the type system. Even the most complicated type systems can only deal with the simple cases, and will fail and make a horrible mess at the more complicated ones.
So I'm saying, there is value to preventing some misuse and preventing invalid states, but it comes at a cost that one has to understand. As so often, it's all about moderation. One should avoid fancy type system things in general because those create dependency chains throughout the codebase. Data, and consequently data types (including function signatures) is visible at the intersection of modules, so that's why it's so easy to create unmaintainable messes by relying on type systems too much. When it's possible to make a usable API with simple types (almost always), that's what you should do.
Actually, I have some similar concerns about powerful type systems in general -- not just OOP. Obsessing about expression and enforcement of invariants on the small scale can make it hard to make changes, and to make improvements on the large scale.
Instead of creating towers of abstraction, what can work better often is to try and structure things as a loosely coupled set of smaller components -- bungalows when possible. Interaction points should be limited. There is little point in building up abstraction to prevent every possible misuse, when dependencies are kept in check, so module 15 is only used by 11 and 24. The callers can easily be checked when making changes to 15.
But yeah -- I tend to agree with GP that immutability is a big one. Writing things once, and making copies to avoid ownership problems (deleting an object is mutation too), that prevents a lot of bugs. And there are so many more ways to realize things with immutable objects than people knew some time ago. The typical OOP codebase from the 90s and 00s is chock-full with unnecessary mutation.