Unbound Generics: an Open and Closed Case

There's a well known saying that goes something like "Please engage brain before shifting mouth into gear". And another that says "If you can't stand the heat, get out of the kitchen". Yet, after what's now more than a year as a fulltime 'Softie, I've managed to avoid being flamed for any of my weekly diatribes; and neither has anybody pointed out (at least within earshot) how stupid I am. So I suppose it's time to remedy that situation. This week I'm going to throw caution to the winds and trample wildly across the green and pleasant pastures of generics, and all without a safety net.

OK, I'll admit that it's probably not going to be a hugely technical venture, so if you are expecting to see lots of indecipherable code and complex derivations of generic functionality, you're likely to be disappointed. You might as well get your email client warmed up now ready to fire off some nicely toasted opinions. I mean, it's not like I actually know much about generic programming anyway. No, where I'm going with this is more slanted towards the requirements of my daily tasks - the terminology.

You see, I'm of the opinion that when I write technical documents, they should be comprehensive enough to ensure that readers can follow the topic without having to dive off and consult an encyclopedia or dictionary every five minutes. While I don't want to reduce it to the "grandmother sucking eggs" level, which would just be boring to average developers and probably excruciatingly useless to the really clever people out there, I do need to make it reasonably comprehensive and self-sufficient. I mean, I really hate it when I'm reading something and have to keep stopping and re-reading bits to try and figure out exactly what the writer actually meant.

You've probably been through this process, where you come across what seems like a simple word but the meaning in the current context is not familiar. The person who wrote it probably knows the topic inside out, and so the meaning is obvious to them. Yet not grasping what it really indicates in terms of the surrounding text can make the rest of the article pretty much meaningless. You need to know the topic before you can understand the bits that will help you to learn about it. Like: "Ensure you check the reverse osmosis defractor for discompulation before remounting the encapsulator". Does that mean you need to buy a new one if it's gone rusty? Who knows...

Anyway, getting back to generics, I recently seem to have stirred up a discussion about the meaning of three fairly ordinary words without really trying. In fact, I suspect I've put our current project back by a week as all of the brains-the-size-of-a-planet people on the dev team try and figure out exactly which words mean what, and how we should use them. So what are these three amazingly complicated and unfathomable words? Would you believe "unbound", "open", and "closed". In fact, you can throw in "constructed" as well if you like.

These are the definitions according to Microsoft's Glossary of .NET Framework terminology (see https://msdn.microsoft.com/en-us/library/6c701b8w(VS.80).aspx):

  • Constructed generic type: A generic type whose generic type parameters have been specified. A constructed type or method can be an open generic type if some of its type arguments are type parameters of enclosing types or methods; or a closed generic type if all of its type arguments are real types.
  • Open generic type: A constructed generic type in which one or more of the generic type arguments substituted for its generic type parameters is a type parameter of an enclosing generic type or method. Open generic types cannot be instantiated.
  • Closed generic type: A constructed generic type that has no unspecified generic type parameters, either of its own of or any enclosing types or methods. Closed generic types can be instantiated.

It seems that most folks agree that a generic type can be unbound or constructed, and a constructed type can be open or closed. Microsoft seem a little shy about defining what an unbound generic type is, though in the Generics FAQ they mention that "the typeof operator can operate on unbound generic types (generic types that do not yet have specific type arguments)". And there are plenty of definitions elsewhere that firmly identify it as being a generic type where the types of the type parameters are not defined.

Meanwhile, it seems like most people also agree on what a "closed" type is, so the main issue is: what's the difference between an "unbound" and an "open" generic type. If an open type is also a "constructed" type, does that mean that it has the type arguments actually populated - even though the code that's executing to achieve this only contains the placeholders? Or does "constructed" in the realm of generics mean something other than the more usual "built" or "assembled"? Does a generic type start out as "unbound" and then, as it becomes "constructed", does it move through "open" to "closed"?

Perhaps there is a nanosecond or two, as the electrons fight their way through the layers of silicon in the CPU constructing and closing the type, that it's no longer unbound, but not quite closed yet? Smacks of Heisenberg's Theory of Uncertainty and Schrodinger's cat if you ask me. Maybe it's all wrapped up in particle and wave theory. Or could it be that unbound types don't actually exist at all? Maybe, as they aren't actually instantiated "things", they only exist in an ethereal context; or they only exist when you aren't looking at them. I suppose that, as the only thing you can instantiate is a closed type, we don't actually need to worry about the other types anyway.

Trouble is, when working with a dependency injection mechanism (like what I'm doing now, as Ernie Wise would have said) you need to discuss registering types and type mappings for generic types that do not have all of the type arguments fully defined as concrete types. So we're trying to decide if these registrations are for "unbound" types or "open" types. When you define a registration or mapping in a configuration file, you use the .NET Framework type name without specifying the type parameters or type arguments; for example MyApps.MyTypes.MyGenericType`2 (where the "back-tick" and number define the number of type parameters in the type). When you use code to perform the registration, you also omit the type parameters; for example RegisterType(typeof(MyTypes.IMyInterface<,>)). As you saw earlier, Microsoft says that "the typeof operator can operate on unbound generic types (generic types that do not yet have specific type arguments)", and these sure look like they don't yet have specific type arguments.

But could these registrations really be "open" rather than "unbound"? All the definitions of "open" talk about the types of the type arguments being provided by "an enclosing generic type or method". When I'm just creating a type registration, how can the type parameters be populated from an enclosing type? If I'm registering the generic child of a parent type, I can't specify what (who?) the parent will be. If I'm registering the parent, I can't prevent somebody adding a class that has a different child. And I can't register a method at all. So there is no way I can specify where the type arguments will come from.

The problem is that almost everything else I read about using dependency injection mechanisms talks about only "open" and "closed" types. It's like "unbound" types are unclean, or unattractive, or maybe just not worthy of use in a shiny new container. Perhaps it's a conspiracy, and we'll discover that NASA actually buried them all in the Nevada desert so they can't be used to confuse clever programmers. And if I talk about unbound type registrations in my documentation, will people just make those "phhhhh" noises and toss it aside with a comment such as "...obviously doesn't have any idea what he's talking about...", "...waste of time reading that...", and "...who let him out again...?"

Or, could it be that I'm the only one who knows the truth...?