4 ms·
>> Eliminate special cases. [...] > True enough, but that's so much easier in a language with sum types. These languages make it easier to have more special ca
by jstimpfle 8y ago
>> Eliminate special cases. [...]
> True enough, but that's so much easier in a language with sum types.
These languages make it easier to have more special cases. There's a difference.
> That's a pretty controversial viewpoint, since it makes composition impossible (indeed taken to its logical extreme this would mean never writing a library, whereas the grandparent was convinced that more use of libraries was the way to write good code).
I don't see why that should be the case. Aside from the fact that composition/"reuse" is way overrated, libraries can always opt for process- or thread-wide global state. Another possibility would be to have global state per use (store pointer handles), and passing a pointer only to library API calls. The latter is also the most realistic case since most libraries take pointer handles. I absolutely have these handles stored in process global data. For example, Freetype handle, windowing handle, sound card handle, network socket handle, etc.
Also called "singleton" in OOP circles. Singletons are nothing but global data with nondeterminstic initialization order and superfluous syntax crap on top. Other than that, they are indeed good choices (as is global data) since lifetime management and data plumbing is a no-brainer.
> I find modern language features mostly guide us down the path that most of us already agreed was good programming style
But just the paragraph before you said you didn't agree with mine? In my opinion, OOP, or more specifically, lots of isolated allocations connected by pointers/references, make for hard to follow code since there is so much hiding and indirection even within the same project/maintenance boundaries without benefit. In any case I absolutely agree that this style is not doable in C. You need automated, static or dynamic (runtime) ownership tracking.
- lmm 8y ago> I don't see why that should be the case. At the most basic level, if project A makes use of library B and library C, then you want to be able to verify the behaviour of library B and library C independently and then make use of your conclusions when analysing project A. But if library B and library C use global state then you can't have any confidence that that will work. E.g. if both library B and library C use some other library D that has some global construct, then they will likely interfere with each other. > Another possibility would be to have subproject-wide global state, and passing a pointer only to library API calls. The latter is also the most realistic case since most libraries take pointer handles. At that point you're not using global state in the library, which was the point. > you can always opt for process- or thread-wide global state That doesn't solve the problem at all. > Also called "singleton" in OOP circles. Singletons are nothing but global data with nondeterminstic initialization order and superfluous syntax crap on top. Indeed, and they're seen as bad practice for the same reason as global state in general.
- jstimpfle 8y ago> At that point you're not using global state in the library, which was the point. Yes. But I want to make clear that you are still using global state for all uses within the project itself. The library can be implemented in whatever way. For example, setting the pointer in a global variable on API entry ;-) > That doesn't solve the problem at all. WHICH problem? I don't think there is one. > Indeed, and they're seen as bad practice for the same reason as global state in general. This is foolish. There is no problem with global state. Global state is a fact of life. Your process has one address space. It has (probably) one server socket for listening to incoming request. It has (probably) one graphics window to show its state. Whenever you have more (e.g. file descriptors, memory mappings, ...), well then you have a set of that thing, but you have ONE set :-). And so on. You are not writing a thousand pseudo-isolated programs. But ONE. One entity composed of a fixed number of parts (i.e. modules, code files) that work together to do what must be done. Why add indirection? Why make it hard to iterate over all open file descriptors? Why thread a window handle through 15 layers of function calls when you have only one graphics window? It adds a lot of boilerplate. It even brings some people to invent hard to digest concepts like monads or objects just to make that terrible code manageable. It makes the code unclear. Someone once described it with this analogy, "I don't say ''I'm meeting one of my wives tonight'', unless I have more than one".
- lmm 8y ago> Yes. But I want to make clear that you are still using global state for all uses within the project itself. But if we believe in using libraries then often our project will itself be a library. > The library can be implemented in whatever way. For example, setting the pointer in a global variable on API entry ;-) And then you have the problem I mentioned: if there is a diamond dependency on your library then the thing using it will break. > WHICH problem? I don't think there is one. The problem of not being able to break down your project and understand it piecemeal. > Global state is a fact of life. Your process has one address space. It has (probably) one server socket for listening to incoming request. It has (probably) one graphics window to show its state. All those global things are a common source of bugs, as different pieces of the program make subtly different assumptions about them. Perhaps a certain amount of global state is unavoidable. That's not an argument against minimizing it. > You are not writing a thousand pseudo-isolated programs. But ONE. One entity composed of a fixed number of parts (i.e. modules, code files) that work together to do what must be done. If you write a program that can only be understood in its entirety, you'll be unable to maintain it once it becomes too big to fit in your head. Writing a thousand isolated functions gives you something much easier to understand and scale.