(I usually use this observation from the other side, as a reductio ad absurdum about coding guidelines that could as well be implemented as editor settings.)
3,057 karma · joined February 21, 2008
(I usually use this observation from the other side, as a reductio ad absurdum about coding guidelines that could as well be implemented as editor settings.)
I was moved to tears when I used Google Street View to explore streets of a city that I know factually my father and I had walked through extensively, but I couldn't recognize a single thing or feel any personal familiarity with it. It's hard to explain why that upset me so much.
(Initially I started using -m to avoid getting trapped in Vim. But even after I gained the option to use e.g. Notepad++ as the editor, I never saw the point in using anything more than "-m 'message'".)
The problem is not the material per se but using inappropriate fastening methods for the type of material and/or a design that stresses the material in a modality where it is weak rather than where it is strong. An example of an inappropriate fastening method would be a screw into the edge of a laminated panel and the joint not glued.
For an example on the done-right side, if you look at any knockdown speaker cabinet kit from e.g. Parts Express, the MDF parts will have bracing and grooves where the user is meant to use wood glue, and screws mainly just to hold it together until the glue sets.
https://www.parts-express.com/Knock-Down-MDF-4-ft-Subwoofer-...
In furniture an example of using MDF in a way that almost has to be intentionally bad design: I have a tall chest of bedroom drawers where the drawer slides are suspended 2.5" off the internal walls by horizontal rectangular blocks of MDF. The blocks are oriented in a way that gravity and the torque on the block causes the layers of the MDF to de-laminate. (Picture riffle shuffling a deck of cards.) The resulting sag in turn makes the drawer slides no longer align.
https://en.wikipedia.org/wiki/Shuffling#/media/File:Riffle_s...
Almost anything else could have been done to make this design better. They could have rotated the grain direction of the MDF blocks 90 degrees. They could have oriented the blocks vertically in the long direction. They could have used 2x3 lumber (vertical or horizontal) instead of MDF. They could have actually made the sides as thick as the facade pretends they are. (The sides are hollow.) They could have brought the sides in to where the drawer slides actually are. Etc. etc.
Some of those things would have cost more or made the (lying) appearance of the furniture less substantial looking. But some of these solutions use the same (MDF) material and same or similar amounts, just different orientation. Either the person who designed it was an absolute dumbass, or it was deliberately made in a way that would fail after more than 90 days but less than 2 years.
Mandating different materials doesn't protect against stupidity or malice.
I noticed the motor is a brushed "universal" motor like you'd find in a vacuum cleaner; that is, old technology that can run on DC or AC and needs only two wires to run. This complex circuit board... wasn't even justified for being a motor controller, because there was nothing to control! I replaced the entire burnt out circuit board with a hefty 20A 120-240 VAC momentary switch from my parts bin.
Since then the grinder's been working perfectly for about 14 years. But if I hadn't been willing and able to do that repair I might have gone through 10 of them in the same time. Part of making a quality product is restraint: to NOT put something on it (like that circuit board) that is unnecessary.
My late father was so sensitive to poison ivy that it skips the bubble phase and melts the skin and flesh off his body. One time he got poison ivy on his hand, and it resembled the scene from Terminator Genisys when the good T-800, "Pops," holds the T-1000 under the acid shower and lost the flesh covering on its cyborg arm.
Edit: it's like climate change: individual decisions that are all locally justifiable according to some decision framework, but add up to a world which is objectively crappier than a world in which we did collectively behave this way.
Of course "progressing in reverse" is the same thing as saying we're regressing.
And yet, full disclosure and admitting to cognitive dissonance, for a (hobbyist) C++ game engine I'm currently working on that targets Emscripten for a web build and native for Debug build, I'm considering not even having a native-Release build at all.
The idea being if it's web-first and the native build being only for developer use for debugging, I could do things like supporting only one desktop graphics API (e.g. just DirectX) and optimizing the native graphics pipeline for simplicity over performance. End users would/could just use the web version.
Granted this is a bit different because I wouldn't be distributing a browser too a la Electron; it would just use the browser the end user already has. Just thought it's interesting that it's easy for me to criticize others, but with the choice of how to spend my own limited developer time (my free time) it's looking like this way of doing it makes the most sense.
For many years I worked on my own game engine which combined a C++ core with an interpreted scripting language. I had developed a system of language bindings which allowed the interpreted language portion to call functions from C++. The engine quickly grew to a multiple-gigabyte executable (in the debug build), and no matter how much I tried to optimize the size it was still unconscionably huge.
One of the reasons I eventually gave up on the project was I realized I was overlooking a simple mathematical truth. The size was NxM, where N is the number of bindings and M the size of each binding. I was focusing on optimizing M, the size each binding added to the executable, while not just ignoring N but actually increasing it every time I added bindings for a new library I wanted to call from the game engine.
There were diminishing returns to how much I could improve M because I was relying on compiler implementations of certain things I was doing (and I was using then-new next generation C++ features that weren't well optimized); it would be a lot easier to simply reduce N. And the easiest way to do that would be some sort of tree shaking.
Unfortunately due to the nature of interpreted code it isn't known at compile time which things will/will not be ultimately called. That determination is a runtime thing, by calls via function pointer, by interpretation of scripts that start out as strings at compile time (or even strings entered by the user during runtime).
From a compile time perspective, static usage of every bound function, feature or library already exists - it is the C++ side of the cross-language binding. That's enough to convince the linker to keep it and not discard it.
In fact, the mere presence of the bindings caused my game executable to grow more per each included library than would a similar C++ -only, all-statically linked program. If a library provided 5 overloads of a function to do a similar thing with different arguments, an all- C or C++ application that uses only one of them would need only include that version in the compiled executable; the others would be stripped out during the linking step.
Since I don't necessarily know ahead of time which overload(s) I'm going to end up using from the interpreted language side of the engine, I would end up binding all 5. Then my executable grew simply from adding the capability to use the library, whether or not I make use of it, but moreover if I did use it my executable grew even more than an equivalent C/C++ - only user of the library because I also incur costs for all the unused overloads.
You can see why something like Electron would have the same problem. Unused functions can't be automatically stripped out because that information isn't known at compile time. To do it by static analysis the developer of the Electron app would have to re-run the entire build from source process of the Electron executable to combine that with static analysis of the app's Javascript to inform the linker what can be stripped out of the final executable.
And it bears mentioning neither such a static analysis tool for Electron app Javascript nor the compiler/linker integrations for it currently exist. In theory they could exist but would still have trouble with things like eval'd code.
Manual configuration would be possible but necessarily either coarse-grained or too tedious to expect most developers (of Electron itself or users of Electron) to go into that much detail. That is, you may have manual configuration to include or not include Xbox 360 controller, but probably not for "only uses the motion controls" while not including other controller features.
Either way you wouldn't be able to add-back support for it Javascript written after build time turned out to actually need the function or feature after all, unless you distributed a new executable. If you're building so much from source with configuration and static analysis, at that point why not write your whole application in a statically compiled language in the first place?
My thesis here is not that we should accept things like Electron being bloated because they cannot be any other way. My point is (as happens time and again in Computer Science) we had certain things already (like tree shaking and unused symbol stripping during the linking stage of statically compiled languages) and then in the name of "progress" let them either be Jedi-mind-tricked away or the people developing the new thing didn't understand what was being left behind.
Oh, sure, you can manipulate this by inserting magic formatting objects, but that's not different from saying all functions take one argument a la Category Theory or Haskell without syntactic sugar. Or you could write a class or function that allows you to tag your object or value by changing its type to a different one so a different operator<< overload is called. In the iostreams design, you're only indirectly selecting which print function to use by manipulating either the stream or the printed object. But in actuality the types of your two arguments are not different, so either you must munge global settings on the stream or "hack" which operator<< overload is selected.
And that's the crux of the issue; what you "really want" is a stream API with three arguments: the stream, the object or value, and a pointer to a function (or function object) that takes the value and transforms it to a stream of characters. Why not just pass that function directly and explicitly? Of course that requires something that's not just a single chained infix operator; my thesis being that starting with that quirky choice leads to down a path that ends in a bad design.
But from a developer user experience standpoint, what is the difference between having to write things twice for this reason:
// header.h
int foo();
// library.c
int foo() { return 42; }
Versus writing things twice for this reason:
// interface.cs
interface IBar { int foo(); }
// library.cs
class Bar : IBar { int foo() { return 42; } }
Granted in C it was for dumb reasons whereas in a modern language it's for better (?) reasons, but: You're still writing things twice!
Also the interview was super early, not to accommodate me interviewing off-hours, but because they (interviewers) "weren't allowed" to impinge on work hours to conduct the interviews!
- Companies just aren't interested in training people if they can hire someone trained somewhere else.
- Project-based time accounting doesn't account for time spent mentoring and transferring knowledge.
- Some older workers withhold knowledge and aren't punished for it. Some older workers share knowledge and aren't rewarded for it.
All this existed prior to COVID and expansion of work from home.
If Spotify won't give me those toys, are such things possible via the API that Spotify has provided?
I'm alternately impressed that I can port C++ in this way at all, and sad at the state of the tooling still after so long. When something goes wrong with the web assembly version of the project, I can't debug it from first principles. This could be because, as an embedded and C++ programmer, I'm unfamiliar with Javascript debugging tools, but it's still unfortunate I can't single step through at the C++ source code level when debugging the web assembly build. I just want gdb for web assembly.
I've also run into silly/stupid bugs like keyboard input not working only in fullscreen, except if you leave it on overnight in which case 1 out of N tries the keyboard input comes back. I spent days trying to debug this, only to have someone on a forum answer it in about 2 seconds that it's because a Firefox change now causes keyboard input to be captured by the awesome bar during fullscreen (which of course you can't see). I could have tried this for a million years to figure out what was wrong and not had that insight. It doesn't bode well for my game engine project if things are this fragile; no other C++ compilation target is this janky.