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?

Tuesday, September 10, 2024

Different meanings of "any" in C# and Python

I first learned to program in Python and now work in C#.

Python has the function "any", which takes in any number of parameters and checks whether any of them are truthy. This makes sense since Python has a concept of object truthiness.

C# has the Linq method "Any", which takes in an IEnumerable<T> and a Func<T,bool>. It calls the function on each element of the collection and checks whether any of these calls returns "true". This makes sense since C# has no concept of truthiness; a hypothetical C# "Any" method that did not pass in a function could only take a collection of boolean values.

Additionally, C# has a separate Linq method "Any" that takes in only a collection. It checks whether the collection has any elements in it. C# can have two methods called "Any" since it, unlike Python, has method overloading.

Despite this very logical reasoning, I periodically end a long bug hunt by discovering that, having a list of bools, I called "Any()" on it instead of "Any(a => a)".

I miss F#'s "IsEmpty".

If you can, avoid using the same term for two different concepts.

Saturday, September 7, 2024

Puzzled by the beginning of Good Omens by Terry Pratchett and Neil Gaiman

This morning I tried this Terry Pratchett novel I picked up at the thrift store. (I know next to nothing about Neil Gaiman. I do have friends named "Gehman". Maybe they are distantly related?) I read up through the Eleven Years Ago section, or page 64 of 369, and my opinions should not be construed to reflect the bits I have not read. I will read and consider any comments telling me that I am missing the point and should continue.

(I do appreciate the dedication to G.K. Chesterton. It helps explain why I like Pratchett's humor so much.)

WARNING: I am making no attempt to avoid spoilers and don't know what will turn out to be a spoiler anyway.

Caring for the first humans

The book begins by taking shots at God's judgment in the Garden of Eden. Whatever; Pratchett baiting the Christians is nothing new. God has shut us all up under sin so that he may have mercy on us all. I like the point about Adam and Eve needing protection after the Curse from wild animals, which I had not considered before. I am a bit ticked that God leaves them to fend for themselves and an angel has to go behind his back to help them--a subtle shot at God that I didn't notice until I reread it. Pratchett does this quite often, though without using God's name. (You can hurl all sorts of blasphemies without offending anyone so long as you use such euphemisms as "nature" or "religion" or "tradition". Or, slightly more respectably, you can do as Pratchett does and imply theological conclusions without explicitly stating them. If you are reading or listening, always consider logical implications.)

Good and evil

The point of the introduction, however, is to introduce the good/evil dichotomy and the beings who participate in it. God and the angels do good and the demons do evil. Humans, we will later learn, do either depending on the situation and their own reasons, having free will. Presumably, the authors do not believe in beings that do not have free will; Crowley is said to have picked it up from humans later, which is obviously nonsense. I rather agree with the authors' skepticism in the existence of conscious beings without free will, though I don't limit free will to humans. God does whatever he wants, so he obviously has free will, and angels and demons probably have free will since they do stuff.

This framework leaves plenty of room for the idea of the depravity of man, by the way, especially once Crowley starts innerly monologuing on how humans don't need temptation to do evil.

I'm never quite sure Pratchett (in this book or others) is arguing for moral relativism or for resisting evil. You can't argue in one breath that "Good and evil...[are] just names for sides" (52) and in the next that some actions are Wrong and others are Right, or however you split morality. Actually, many moral relativists attempt to do just that, but I expect more of Pratchett.

Good is not good?

My main miff with the book is with the oxymoronic popular idea that Neutral is better than Good (and I would like to bestow an especial "may your memory be forgotten" on the webcomic Looking for Group by Ryan Sohmer and Lar DeSouza, of which this fallacy is the entire worldview and point). The chain of reasoning goes like this: "Good, as I understand it, is boring and ineffective. Boredom and ineffectuality are bad. Therefore, Good is not all that good." Only, most people state only the first premise aloud, leave the second to be assumed, and make jokes implying the conclusion to loud laughter, somehow entirely missing the obvious logical contradiction. If boredom and ineffectuality are not good, then Good is logically neither boring nor ineffective. Therefore, if you perceive Good as boring and ineffective, then, necessarily, either boredom and ineffectuality are not so bad after all or you perceive incorrectly.

When the Antichrist is on the way, we find that the forces of Good want Armageddon and eternal Heaven, the forces of Evil want Armageddon and eternal Hell, and the Neutrals want to preserve Earth because Hell is bad and Heaven is boring. First, if I interpret my Bible correctly, God lives in Heaven, whereas the dead in Christ go to the New Jerusalem, which is some sort of terrestrial habitation designed for humans. Second, Crowley claims that all interesting aspects of culture belong to Hell. What on Earth, or whichever realm? Crowley says that Beethoven, Bach, and Mozart go to Hell, but we sing songs composed by them in church. He says that Heaven doesn't get salt or eggs, but God created both. Whatever we see that is desirable, either our desire is wrong or we see good, and our new home can have all the good. I'm sure I'm missing something here. Perhaps something satirical?

And whyever was it necessary to provide the young supposed Antichrist with an evil influence as well as a good one? The Force doesn't need balanced1; more good is truistically better. The kid wasn't going to bring about Armageddon if he was good; the angels were waiting for the demons to do that.

Other picked nits and miscellany

"God does not play dice with the universe; He plays an ineffable game of His own devising, which might be compared, from the perspective of any of the other players, to being involved in an obscure and complex version of poker in a pitch-dark room, with blank cards, for infinite stakes, with a Dealer who won't tell you the rules, and who smiles all the time" (pages 11-12). But didn't God tell us the rules in the Bible?

"Hastur cleared his throat. 'I have tempted a priest,' he said. 'As he walked down the street and saw the pretty girls in the sun, I put Doubt into his mind. He would have been a saint, but within a decade we shall have him'" (page 16). That seems a bit horrifying for the comedic context: The man was not only damned but also turned into a creep who lusts after women significantly younger than himself. Also, demons don't know the future, do they? Or maybe Hastur is merely boasting rather than stating fact, in which case two wrongs do make a right.

"Technically Aziraphale was a Principality, but people made jokes about that these days" (page 38). Principalities and powers are demons, whereas Aziraphale is an angel.

"No one bothered to take up this matter with the nice Mr. A. Ziraphale, who ran the bookshop two doors along and was always so helpful with the translations, and whose handwriting was instantly recognizable" (page 45, footnote). I so badly now wish for an angel eyewitness to help us with the Bible translation. That's not happening, though, since we are to judge the angels by the Scriptures, not the other way around (Galatians 1:8, which, by the way, is one use of the word anathema, which appears elsewhere in the book).

Crowley says that Heaven will have the movie The Sound of Music (page 50). After Maria and the Captain fall in love, Maria has a song where she speculates that she must have done "something good" to merit a future with the Captain, thus showing ingratitude to God for his undeserved gift of human love. Either Heaven will not have The Sound of Music in the form we know it, or at least its viewers will not "enjoy it". (Anyway, the book was much better.)

"Crowley looked up slyly. 'Then you can't be certain, correct me if I'm wrong, you can't be certain that thwarting it isn't part of the divine plan too. I mean, you're supposed to thwart the wiles of the Evil One at every turn, aren't you?'" (page 51). Good on Crowley for identifying a huge problem with fatalism as commonly exercised. The Christian must never allow evil to stand unopposed, arguing that whatever happens must be the will of God. By that logic, if you oppose the evil, your opposition must have also been the will of God.

Predictions in case I continue reading: The supposed Antichrist is totally going to name the hellhound; children love dogs. And the actual child of the Devil is going to be a girl.


1 This is a reference to Darths and Droids, not to Star Wars. I recognize that the balancing of the Force in Star Wars is not between Good and Evil.

Friday, May 17, 2024

OOP for FP programmers, part 2: Why bother when we can pass functions?

I am still exploring, but since the previous post I am having difficulty finding a use case in which object-orientation provides any functionality or simplicity that function parameters do not. So I intend to test the hypothesis that object-oriented programming offers nothing over function parameters.

The point of OOP is inheritance-based polymorphism. (Polymorphism, I think, means "choosing which entity is called at runtime", but that would technically define if/then statements as polymorphic, so my definition might not be specific enough.) OOP's inherited classes can be passed into any function that takes the parent type. But you can do the same thing by taking a function of a specific signature (or an object of specified functions, the equivalent of an interface). OOP does offer the ability to find all inheritors of a class or an interface, but the functional programmer can do the same thing by extracting a record of parameters (the equivalent of an interface) and then finding all uses of that. A class also has the advantage of a debug-visible name; I highly doubt that debuggers will start recording the names of functions passed as parameters anytime soon, so the best alternative would be to pass with each function a string describing it (ugh).

So class inheritance does not not appear necessary. Is it useful? I intend to use the scientific method to find out. I determine never to type a colon character in C# until I find a good use for OOP. I leave myself two exceptions: I may use inheritance for compatibility with existing systems and for domain modeling with immutable data.

Wednesday, May 15, 2024

OOP for FP programmers, part 1

The following content is egregiously oversimplified.

I learned to program in Python, where classes are a sign of overthinking, and then worked using F#. Now I need to learn object-oriented programming for working in C#. This is difficult because:

  1. No one ever explains object-orientation to be understood by functional programmers.
  2. Many OOP and FP constructs are semantically quite similar, yet not quite identical.
  3. No one ever compares OOP and FP constructs; at best, they ask whether we should use object-oriented or functional programming, a highly questionable question.

Here is what I have so far:

Polymorphism is accomplished using discriminated unions, inheritance, and runtime type checks. Runtime type checks are a Wrong Thing because they do not give you exhaustive matching, whereas either other style requires you to say somewhere that you are purposely choosing not to implement logic. Discriminated unions require definition of all subtypes in one place but allow adding arbitrary use cases elsewhere. Inheritance requires definition of all use cases in one place but allows adding arbitrary subtypes later. Discriminated unions can be faked using Church encoding or the Visitor pattern. So you should use inheritance if you want to see all defined behaviors in one place, but discriminated unions if you want to see all defined subtypes in one place.

Inheritance works only on the instance level. Static inheritance is probably theoretically possible but would be a Wrong Thing for reasons I do not yet understand. The proper OOP solution is to model all polymorphic behaviors themselves single-method classes with no state, at which point the Jack Diederich in my head tells me to convert them to functions and just pass in a function parameter. I have not tried enough times to know yet why he is wrong.

Classes hold encapsulated state. They also hold the behaviors that may operate on that state. They also hold polymorphic behaviors, with or without state. They are also a means of organizing static methods. Each class should have its own file all to itself to reflect the class's status as the basic unit of programming. You cannot program in OOP effectively unless you dream at night of the soft embrace of the class.

Interfaces are objects of functions, sometimes with field references. They serve the same role as function parameters except without duck typing. Interfaces are different from function (and ref) parameters in that they can hold multiple functions, so we should probably use function parameters at first and then extract multiple function parameters into interfaces. Except that the Interface Segregation Principle might imply that at least most interfaces should have a single method?

This all seems highly complicated. I don't know why anyone goes to the trouble to learn OOP instead of matching on DUs and passing functions around. But if there is a reason, I want to find out.

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...