Tuesday, January 20, 2026

Rethinking my past dislike of C#

In response to my previous post.


We don't use "goto", of course, because we want our iteration to be structured.

But "for" is only for collection iteration with indexing. For everything else, we use "while", even though "for" gives the reader the accumulator variable, means of accumulation, and end condition upfront. And then we scatter in a few "break"s to confuse the hurried reader further.

Still annoyed at the limited idiom of "for".


We use "for" instead of "foreach" for performance

On lists that could have been arrays. We always call "ToList", never "ToArray".

ToArray often builds a list first, then converts it to an array, which takes an extra copy operation. ToArray could be worth it if you're just using Select or other length-preserving transformations.

Also, most codebases don't use "for" instead of "foreach" for performance. That's not idiomatic.


We don't need discriminated unions. Classes and interfaces are enough for any situation.

And then we "switch" on them and have to repair bugs having to do with missing cases that were created later.

Now that I am older and wiser, I realize that my greatest mistake was when I thought that inheritance might have been worth using to get exhaustive matching. If you are programming in C#, learn to accept that you don't get exhaustive matching. If you use inheritance, you will end up with so many layers of indirection that you never quite know where to find the code that does the thing. Don't do polymorphism. Kill it on sight. You are allowed to use "if" and to switch on enums. That is all.

C# is supposedly getting DUs one of these days. I wonder whether that will change my opinion.


We don't use static fields and singleton classes. Instead, we use dependency injection frameworks to ensure that only one instance of each class is generated and that all other classes have access to that instance. Which is different.

I'm withholding judgment on DI until I have been bitten by global objects. To figure this one out, I'll need to use more global objects.


We don't have an order of compilation below the project level. Every class in a project, unless it is private to another class, is accessible from every other class. We deal with circular class dependencies by not worrying about them. Code goes wherever happens to feel best at the moment.

I haven't thought about this in a while. F#'s file ordering was quite nice.

I think that this problem probably mostly goes away if you understand what your program does. Of course, this requires writing a program that a human can understand.

The non-F# solution is to split the program into small units with narrow interfaces. Maybe that is why the early C programmers encouraged us to split functionality into separate executables and communicate between them with plain text. Your interface can't be very complex if you have to parse all input and output.


"IConvertsFooToBar" is supposed to be prettier than "Func<Foo, Bar>" for some reason.

Granted, "Func" is an ugly word. But I guess we had already used up all the punctuation elsewhere.

And we need to have separate "Func" and "Action" delegates since you can't have a value of type "void".

Same answer as for polymorphism: C# wasn't designed for passing functions. Don't do that.

Don't pass interfaces, either, if you can help it. Refer to the previous comment on avoiding polymorphism. Flat code is easy to read.


We use static typing so that we can be sure that all representable cases are valid.

Unless they're null. Anything could be null. (Except structs, but we don't use structs.)

A defined value representing "no value" is highly useful. This is a sub-problem of understanding your program's flow so that you know the range of possible values at any point. This, again, requires writing an understandable program.


"default". Without "default", you have to make two versions of every generic method, one for classes and one for structs. With "default", we still have to make two versions of every generic method since the two have completely different behaviors. For classes, it returns "null", which might blow up your program if you're not on guard. For structs, it returns an instance in which every field is set to its own "default", which is significantly worse since it's very difficult to detect.

Now that I have written a little C, "default" makes sense. Now it is the distinction between structs and classes that I don't understand. Why can I only allocate structs on the stack and classes on the heap? Isn't allocation strategy more related to data access and lifetime than to data type? I have yet to explore functional programming in C, but at least I can pass a reference to stack-allocated data.


Exceptions are unexceptional. We wrap deep calls in try/catch because something in there will probably throw an exception at some point, and just because there was an exception doesn't mean that we need to crash now.

Catching all exceptions from a function call is evil; you don't know exactly where the exception was thrown or how the program state has changed. You must at least take some effort to recover rather than continuing blithely on as though no error had occurred.

That's more a pitfall than a language problem, though F#'s native Either type provides a much nicer alternative to exceptions than C# does.


Everything is a "manager". "Manager" is pretty close to synonymous with "class" when you think about it.

This is an anti-pattern, but also C#'s fault for making us wrap all code in classes while telling us that each class should do one thing. How do you name a class and method that do one thing? WidgetFrobber.FrobWidget? WidgetFrobber.Frob? WidgetFrobber.Do? FrobWidget.Execute? I usually go with WidgetFrobation.Do, but it still feels wrong. Organizing code by verbs is often better than organizing it by nouns.


I don't mind C# as much as I did then; I have since spent much time learning to simplify code to avoid the features that make C# painful to use. C# has records now, which take most of the headache out of classes. If you avoid polymorphism of any sort--interfaces, inheritance, or passing delegates--and structure your code as a straightforward imperative program, then modern C# isn't so bad.

Monday, January 19, 2026

Do you have left-pad syndrome?

The npm left-pad incident

How to parse CLI args:

  1. Iterate over the array of CLI args.
  2. Inside the loop, switch on each item.

Just make sure, if any of your options take parameters, to use a for-loop instead of a foreach-loop so that you can just increment the counter in the switch case.

If you install a library to parse CLI args, you might have left-pad syndrome.

How to parse CSV:

  1. Call String.Split.
If you install a library to parse CSV, you definitely have left-pad syndrome.

Monday, January 5, 2026

My device.

My device. MINE. You can't have it.

You may not install games or "suggested apps" on it without my permission.

You may not preinstall apps and prevent me from uninstalling them.

You may not show me anything I did not ask for, whether advertisements or pop-ups that helpfully inform me that no malware has been detected and thus no action is required.

You may not send my information somewhere else without my permission, whether my credit card numbers or my browsing habits.

You may not throw new icons on my clean desktop every time I install updates.

You may not redirect my Internet searches to your own search engine.

You may not use my accounts to send phishing messages to my contacts.

You may not use my device to spam anyone else's device with information until it crashes.

You may not add toolbars to my browser.

You may not beg me to make this program my default at any point after I first run it.

You may not throw away files I have downloaded because they are not from an organization you recognize.

You may not install half an operating system with every program and then take fifteen minutes to install updates for them.

You may not do anything involving "analytics". I might make an exception if I specifically trust you as an organization or individual.

You may not add cookies to my browser that will not in any way benefit me. "Targeted advertising" does not benefit me; you know what I want right now by which web page I am visiting.

You may not pop up a survey request that won't go away if I ignore it.

You may not read my emails.

You may not disable my working camera because some sensor has gone bad.

You may not refuse to use my working camera because my other camera is broken.

You may not sneak yet another AI onto my device through program updates.

You may not refuse to do anything whatsoever, including close, unless I reach for my mouse and click you.

You may not turn my Number Lock off when I did not press the Number Lock button.

You may not make my programs to freeze when I tell them to open.

You may not countermand any decision I make because you think that you know what I want better than I do.

My device shall do what I tell it to do. My device shall not do what I do not wish it to do, except if I tell it to do so.

Very little in this post is true. "My" devices mostly obey the orders of various corporations with blithe disregard for me. They might do anything; I would never know. I can foresee no happy ending.

Tuesday, September 9, 2025

Why I Don't Watch Ads (paranoid rant)

First: "Who blogs on Blogspot?" I know that Blogspot is dead. I can't stand Medium's vibe and somebody told me that SubStack was evil. Blogspot is where I first found James Iry and Steve Yegge. It loads slowly, but it doesn't badger you to sign in, as far as I remember [glares at Medium]. So now I'm promoting ad blockers from the servers of the company that owns YouTube.

 

We have been told repeatedly that the battlefield is in the mind, that we in the Bride of Christ must keep our minds pure. So media discrimination becomes vital. Everything affects worldview; worldview affects everything. A novel or movie may teach lies. We must be on guard.

 

The inherent purpose of the modern advertisement is to subvert that. Ads may have once worked by finding people who had a need, then suggesting a way to fill that need. Now, they create a need to fill. They're blatantly manipulative. 'Tis better to be damned than mentioned not at all, for the poor sap who hated the ad jingle stuck in his head is still thinking about your product regardless of whether he so wishes.

 

In purity in media consumption, I may have problems with promiscuity, but at least I won't whore out my mind for a measly $15 per month. (But apparently I'm too cheap for that, either, so I'm watching Doctor Who on archive.org and just kind of not listening to music.)

 

"But advertisements don't work on me!" How do you know they don't? They work on the subconscious, not the conscious. These people pay a lot of money to discover what works. Then they pay a lot more money to discover what you, specifically, can be duped into watching. I should know; I unwisely spent hours yesterday on Facebook, and the ads were approximately as interesting as the posts from pages I follow. I hate Facebook because I love Facebook. I wasted so much time. The doomscroll-inducers do their job well.

 

But let's assume that you are correct--that advertisements do not affect you. So then why watch them? If it doesn't influence you, it's useless to you. The company sponsoring the advertisement makes no money. It's like consenting to a police search: If you know they won't find anything, then you're just wasting their time. The only party helped is whatever bastion of brain rot is serving the ads. You are spending your brain time to move money from some company that makes fake video games to the company that sells them the means of convincing people to download the fake games. You could do this much more effectively by not doing that, waiting for the fake game company to die and Google to find some advertising model that helps you instead of exploiting you.

 

Anyway, here's my point: If you get a mobile game and enjoy it enough to keep playing it, spend the $2 to get the ad-free version. If you prefer a music subscription service over buying another (rippable) CD every month for the same cost, then do that instead of listening to ads. If the service you are paying for gives you ads anyway, change services. And anywhere that won't let you escape ads by paying, use an ad blocker.

 

These companies want to stress you out so that you'll buy their products. Don't give in. Live in peace. 

Tuesday, November 5, 2024

Stuff I don't understand about C#

Rant ahead. Criticisms of the language and the language idioms are interspersed.

Disclaimer: I've been wrong about C# before. My lack of understanding may be caused by my lack of knowledge. If I am proven wrong, then I become a better programmer, which I appreciate, so contradict me freely.

Why do we do this to ourselves?


We don't use "goto", of course, because we want our iteration to be structured.

But "for" is only for collection iteration with indexing. For everything else, we use "while", even though "for" gives the reader the accumulator variable, means of accumulation, and end condition upfront. And then we scatter in a few "break"s to confuse the hurried reader further.


We use "for" instead of "foreach" for performance

On lists that could have been arrays. We always call "ToList", never "ToArray".


We don't need discriminated unions. Classes and interfaces are enough for any situation.

And then we "switch" on them and have to repair bugs having to do with missing cases that were created later.


We don't use static fields and singleton classes. Instead, we use dependency injection frameworks to ensure that only one instance of each class is generated and that all other classes have access to that instance. Which is different.


We don't have an order of compilation below the project level. Every class in a project, unless it is private to another class, is accessible from every other class. We deal with circular class dependencies by not worrying about them. Code goes wherever happens to feel best at the moment.


"IConvertsFooToBar" is supposed to be prettier than "Func<Foo, Bar>" for some reason.

Granted, "Func" is an ugly word. But I guess we had already used up all the punctuation elsewhere.

And we need to have separate "Func" and "Action" delegates since you can't have a value of type "void".


We use static typing so that we can be sure that all representable cases are valid.

Unless they're null. Anything could be null. (Except structs, but we don't use structs.)


"default". Without "default", you have to make two versions of every generic method, one for classes and one for structs. With "default", we still have to make two versions of every generic method since the two have completely different behaviors. For classes, it returns "null", which might blow up your program if you're not on guard. For structs, it returns an instance in which every field is set to its own "default", which is significantly worse since it's very difficult to detect.


Exceptions are unexceptional. We wrap deep calls in try/catch because something in there will probably throw an exception at some point, and just because there was an exception doesn't mean that we need to crash now.


Everything is a "manager". "Manager" is pretty close to synonymous with "class" when you think about it.


I'm tired. I miss programming functionally.

Sunday, October 20, 2024

SCP-7381/Paranatural crossover

...So this thing kills you once you understand it. It took us a while to figure that out, obviously; we didn't know why our research teams kept suffering casualties.

We don't know why it kills you. Maybe it doesn't exist and the idea itself is fatal to the human mind. We are hoping that it is conscious and just extremely observant, powerful, and paranoid, because at least then we have some hope of dealing with it.

After all, people do keep dying. Every death adds to our knowledge, leading to more deaths. So we find ways to cover up the deaths, attributing them to other causes.

We have a plan. The sleeping mind is not sane enough to comprehend anything coherently. So we work in our dreams and recruit the dreaming. It's safer not to tell most of them what is going on. Some serve as experimental subjects in the effects of various lines of thought. Others attempt to cause pain or injury to the hypothetical being, which may be more dangerous for all we know.

For obvious reasons, we wipe all their memories on awakening. They may remember a few details for a few seconds, but they'll be too groggy to hurt themselves.

My point: Next time you have a nightmare you can't remember, I wouldn't worry about it. Or think about it at all, in fact.

Thursday, October 10, 2024

The Scarlet Pimpernel (in progress): What is (not) going on?

I picked up The Scarlet Pimpernel for a lighter, more fun work. I'm on chapter ten and I'm bored. I'm BORED with THE SCARLET PIMPERNEL. This was not supposed to happen.

SPOILERS AHEAD, probably.


The Scarlet Pimpernel is about daring rescues, romance, political intrigue, and dastardly espionage, except so far without any of those things:

  • The most exciting action: The story told about the heroes by a bad guy in the book's introduction.
  • The best romance: The repeated assurance that two characters were totally going steady on the trip here.
  • Political intrigue: Mostly people complaining about their government. Actually, maybe this book is not about political intrigue.
  • Spy stuff: All the spies are bad guys. They (hyperbolically) wear fedoras with balaclavas and snicker in French. Their sneakiest move is (actually) to slip under the table and hope that nobody notices that they vanished. Their operations have so far succeeded smashingly because the British conspiracy-theorize about the presence of French spies and then trust the spies implicitly, and since no British character is doing either intelligence or counterespionage, their success will probably continue.

The drama camera has been pointed at a character who is about to be blackmailed into helping the French spies; a character whom everyone thinks is a revolutionary for denouncing a French aristocratic family so they could be executed but (claims to have) had extenuating circumstances (but tells pretty much no one about them); and a character who secretly loves her husband but despairs of reciprocation, and thus refuses to show her love in any way. These are all the same character. Most recently, she was on an hours-long road trip with her husband, neither of them talking the whole way, but she looking forward to spending time by herself when they got there.

If I know what the fun bit of a book is supposed to be, I can often focus on that and not overthink the rest. What am I supposed to be focusing on here?

Rethinking my past dislike of C#

In response to  my previous post . We don't use "goto", of course, because we want our iteration to be structured. But "f...