January 2004 - Posts
Paul Wilson blogs:
In fact, several have went out of there way to tell me how much the hostile attitudes of Frans and Thomas had turned them off!
This is new to me: do I have a hostile attitude towards people? Before you stand up and cry from the top of your lungs: "Yes!", let me explain something.
I'm Dutch, I'm not a native English speaker. We, Dutch, have different constructs for describing politeness in a sentence. Of course we learn at school the difference between:"Give me the book", "Can you give me the book?", "Can you please give me the book?", "Would you be so kind to give me the book, please?" and probably other variants (I might even have made a syntactical error here, but I'm not sure). When to use which variant? I honestly don't know, as in Dutch we don't have these kind of constructions. So when I say "You're wrong", that's a normal construction when you translate the Dutch sentence over to English. However it sounds rude in English, I'm told. But what's the proper sentence in English so it sounds polite enough? I honestly don't know, so I obviously make mistakes. I can of course stay on the safe side and use the most polite construction I know, but that might sound creamy and like sweet-talk, which is not what I want. I'm a true beta, I'm already very happy I can write an English text others can vaguely understand :) so it's truly hard for me to find the right wordings to formulate what I want to say. As I can't and don't want to speak for Thomas here, he too is in this situation by being a non-English speaker (German).
That aside, I can be and often am very direct. I'm not the kind of person who tells you "Good job!" and at the same time thinks "Gawd, what a mess". However as I've learned through the years, you can't always be outspoken about what's on your mind. In the past couple of years I really payed attention to what I said / typed and if it can be interpreted as a rude thing to say: I still tend to be outspoken but now add disclaimers, factors which make it less rude etc.
Sometimes the truth hurts though. Sometimes people fail to accept reality. If a person A, with all his bluntness for whatever reason, tells some other person B how reality really works and looks like, is that person A having a hostile attitude? Personally I don't think so, although A can and should think twice about B's feelings, however that doesn't make the truth less true.
What I'd like to know is (and you can react via email if you'd like): who did I torture with a hostile attitude and when / where did I do that? Not that I want to openly debate all these situations, but perhaps I can elaborate the situation a bit plus understand how others, native English speakers, interpret texts I wrote thus what the difference is between what I meant to say and what was interpreted as what I've said. Thanks.
S.B Chatterjee refers to an article which is a follow up on a blog by Michael Earls concerning a .NET reality check. (If this sentence sounds a little complex, it is, but just start with Michael's excellent blog entry and you're on track :))
I fully agree with Michael Earl. For the past 4 months or so Microsoft and its employees in general published a lot of material about upcoming technologies like Longhorn, Whidbey, Yukon, XAML and other goodies that will make our life easier than it is today. However these technologies are at least 1 year away, if not more, and after that the 'adoption' phase begins which takes at least another year before a majority of people is using them. All that hype is not usable today, as we, developers, are still using .NET 1.1, VS.NET 2003 or even earlier versions. This is frustrating.
For years Microsoft has done this, it's nothing new: release a technology XYZ, keep releasing marginal examples on a marginal schedule and start hyping XYZ's successor shortly after the release of XYZ. For years I too have accepted this approach and thought it was 'normal'. But the last couple of months, especially with the start of a lot of MS blogs, I realized that it isn't normal, it's bad. "It's always better in the Next ReleaseTM", is a well known slogan in software land. MS' behaviour with the hype about next-gen technology is the ultimate implementation of that slogan. And it's frustrating for today's developers, working with technology that's available today (.NET 1.1, VS.NET 2003).
It's frustrating because there are no fixes available nor announced for problems with today's technology, like the dreaded ASP.NET designer flaws in VS.NET, flaky winforms controls, bad design in essential technology like the XmlSerializer (no cyclic reference support, interpretation of IXmlSerializable is flawed and the interface is not documented etc.). What's presented as the 'next big thing', Whidbey/.NET 2.0, is said to solve all of these problems and more. However, it's at least a year away till the early adopters are starting with VS.NET 2004 (2005?) and the majority will follow later, at least 6 months after the initial release date. Besides that, Whidbey will allow you to compile code targeting .NET 2.0 only, you can't compile using the .NET 1.1 compiler, which means your customers will have to upgrade to .NET 2.0 as well.
Every day I check the MSDN 'recently posted' page and I estimate that at least 50% of the articles posted are about technology 1 year or more away (today, at least 11 of the 20 articles listed). Not a single one is about how to deal with problems we all face or will face or have faced with todays technology. Searching the Knowledge Base is as frustrating as reading about the 'solution to all your problems'-articles about tomorrow's technology not available to us: you have more luck searching google groups and in a lot of occasions find fellow developers with the same problems and the same lack of solutions.
Microsoft, stop it. Stop the hype for material we won't see on our desktops for at least a year. Instead give us solutions for the problems a lot of us face today with today's technology: fix the flaws in VS.NET 2003, solve the problems with XmlSerializer so more people can write webservice compatible code using the now undocumented IXmlSerializable interface (which is documented in Whidbey, so a solution is already found apparently). It gives me the idea that you want to obfuscate the problems of today's technology with pretty pictures about tomorrow's technology which seems to solve all our problems for good. I don't think that's a good thing.
I understand that developers working on tomorrow's technology want to tell the world how cool it is (or will be) and that others, naive as I was for years, want to talk about these new technologies for days, weeks, even months. I hope you, Microsoft, will understand that all that hype is not helping us today and it draws attention away from the serious lack of customer support for today's technology. If I would treat my customers like you do, Microsoft, I wouldn't have a single one left, and I wouldn't blame them.
Today I've posted this as a reply in the Microsoft.public.objectspaces newsgroup. I think it's also blogging material, so that's why I post it here too. You can decide to react here, on your own blog, but also in the microsoft.public.objectspaces newsgroup, available on the msnews.microsoft.com USENET server.
The person who wrote the quote has a valid point. This is the basis for the thought experiment: will Objectspaces and its quality (and limits) limit interest in O/R mapping?
My problem w/ #4 is that there's no sense creating a vendor dependency
on a product that wont be around long after ObjectSpaces is released.
Let's face it, without a standards-based approach like JDO to back them
up non M$ vendors have zilch going for them other than the fact that
ObjectSpaces isn't out yet.
If this is true, MS is thus killing businesses by providing an inferior technology (it supports just 1 db) and still can get away with it. An EJB-CMP or JDO equivalent would have been the best choice, so all O/R mapper vendors could target that spec. Now it's indeed an open market which will be dominated by the one who can set the de-facto standard. It's easy to guess which company that will be.
Still, I think that with the limited feature-set of ObjectSpaces (and the fact that it is not yet available), it is not a good thing for O/R mapping for .NET in general: because it will be seen as the de-facto standard and because its limitations are there, a lot of people will/can think O/R mapping is not for them and will stick with Datasets.
The hype around ObjectSpaces is dying too, as it seems. If you look at the MSDN site, every 2 days or so a couple of articles are posted about next-gen technologies like avalon, indigo, whidbey etc, not ObjectSpaces. As if its a 'last minute' tool and not that important. This can (speculation) probably be caused by the fact that there are problems with the implementation or that it simply isn't seen as an important aspect of .NET 2.0, however it is important for O/R mapper vendors out there.
By ignoring the fact that ObjectSpaces will be seen as the de-facto standard and its limited focus/featureset, Microsoft kills more or less the interest in O/R mapping. After all, O/R mapping is a technology that is not yet widely accepted, you see DataSets everywhere; when an article describes data-access it's in the far majority of cases about DataSets, not about O/R mapping.
Now, in that situation, if a developer's first experience with O/R mapping is ObjectSpaces, will he be looking for other tools because of the fact he's interested in the technology? I don't think so: or he's happy with ObjectSpaces, or he's not happy with ObjectSpaces and is dissapointed in O/R mapping and returns to typed DataSets and stored procedures.
It's not hard for me to produce a group of templates for LLBLGen Pro which generates code / xml files compatible with ObjectSpaces code/targeting ObjectSpaces classes, so people can use my O/R mapper tool to create the mappings. ObjectSpaces however is too limited: no Oracle for example. Now, a lot of Oracle databases behind websites power asp-websites and these will (are) be ported to ASP.NET. Developers look at whidbey, and think: "we have two choices: ObjectSpaces or DataSets". ObjectSpaces doesn't work with oracle, so the logical option will be DataSets. Do you think they start with "I want O/R mapping!"? No, they start by looking at how they can ease interaction with the persistent storage. Because ObjectSpaces is seen as the de-facto standard (by then) and because it doesn't work with for example Oracle, the developers will opt for the other de-facto standard: DataSets.
It's a myth to think that the majority of developers start by "I want O/R mapping" and then look for tools and then start coding. They think in: "I want to / have to read/write data into/from a database" and because they are spoiled with the n-tier model for years, they think in "DAL tier" and "BL tier". The DAL has to do the persistence work. They have .NET 2.0, and start looking for options in .NET 2.0 to work with data. ObjectSpaces, DataSets... the works. What to choose? Only if they tried them all and have read that O/R mapping really is great and some tools out there offer solutions for O/R mapping for .NET, they'll start looking for those tools. In all other situations, they'll either stick with ObjectSpaces or if that's a dissapointment, will then try DataSets.
So for O/R mapper vendors, as I see it, it's a choice between a rock and a hard place. If ObjectSpaces is great, interest for O/R mapping will grow, however because ObjectSpaces is great and free and already installed with .NET 2.0, why bother buying another tool? If ObjectSpaces is not that great, interest in O/R mapping will die away ("I looked at it, but I don't understand the hype") for the majority of developers and the reason to buy another tool for O/R mapping purposes is fading away.
I really don't know what to do. O/R mapping really needs a lot of positive air-time in the .NET community to gain interest, otherwise it will die a quiet death and will stay the technology of a few people. I understand the propriety API issues that come with every O/R mapper vendor out there, but because Microsoft has made a fatal error by not offering a great, standard API spec like EJB-CMP or JDO to work with, this will not change.
In the end, the average developer will suffer from this, because the amount of choices for O/R mappers all using the same API is not available. The average developer will also not move to O/R mapping instantly, because it is not an appealing technology because of ObjectSpaces' limitations. (or as the head of DotNed (The Dutch .NET user group) described it (not literally): "It's a very complex technology, only use it if you're really interested.")
For the coming 6 months I've decided to write as much positive things about O/R mapping as I can, to make it interesting for a lot more people so when ObjectSpaces arrives they'll not be dissapointed in O/R mapping because of ObjectSpaces but will know that there are alternatives which do produce what they want.
I hope other O/R mapper vendors will follow that initiative so more and more people will get interested in O/R mapping in general.
Disclaimer
Although I find it absurd to put in a disclaimer, I know for a fact that talking about Multiple Inheritance (MI) is risky,
because it is one of those subjects which can cause irrational reactions, resulting in flame-fests. Discussions about
MI should be theoretical and thus based on theory, not about one of the many different implementations. I'm discussing the theoretical benefits of MI and am not discussing a real implementation like C++'s MI. I've done some research on this subject prior to writing this article and have read many discussions about MI and .NET and also discussions about MI and languages like Eiffel, so I'm aware of the disadvantages of MI, I also am aware of the reasons (of the ones that are publicly stated) why .NET doesn't have MI at the moment. Still I think .NET contains enough functionality and implementations of classes and interfaces which require MI to be fully utilized. MI is a complex concept, however so is the concept of generics. MI can result in unmaintainable code, but so can Single Inheritance (SI) (any language can be used to write unmaintainable, bad code). You can work around MI in most situations, but you can also work around the lack of polymorphism (in C for example, by using function pointers) or even OO. Still it's seen as an advantage to have OO, polymorphism and (soon) generics.
Multiple Inheritance types
There are two types of MI: multiple type/interface inheritance and multiple implementation inheritance. .NET supports only multiple type/interface inheritance (you can derive an interface from multiple interfaces, you can implement in your class multiple interfaces (which makes your class derive from those types). There is no support for multiple implementation inheritance, so when your class Foo inherits from a base class Bar and you want to implement an interface IBlah that is already implemented on another class, say Blah, you are obliged to re-implement that interface IBlah in your class in some form, you can't inherit that implementation from Blah as well, because you already inherit Foo from Bar. This is called redundancy of implementation. It should be noted that every MI class hierarchy can be transformed, in theory, into an SI hierarchy, by using 'redundancy of implementation' and multiple interface inheritance (The Eiffel compiler for .NET does this for example).
It's however not always the case in practise though.
Working around MI in a Single Inheritance (SI) environment
Because .NET doesn't support multiple implementation inheritance, it has to use tricks every SI environment has to use, to mimic multiple implementation inheritance. There are several ways to overcome the lack of MI, and each of them requires extra work. This extra work wouldn't have been necessary if .NET would have supported full MI. Metaphor: .NET will soon support generics, which will save you a lot of work when dealing with data-structures which should supply behaviour on a variety of types without casting, and thus will require less work on your part.
To illustrate the ways around MI in .NET, we use three classes, A, B and C. A implements IA, B implements IB and C should have the functionality of IC, which is the interface derived from IA and IB (IA, IB and IC also can be seen as 'the class as interface': every class has an interface, made up by it's publicly available methods, fields and properties, so they shouldn't necessarily be seen as physically defined interfaces). To achieve this, we can do the following things:
- Inherit IC from IA and IB, inherit C from A or B and re-implement the interface of the class C doesn't inherit from (IA or IB) in full in C.
- Inherit IC from IA and IB, inherit C from A or B and aggregate an instance of the class C doesn't inherit from inside C, where the interface of that class is implemented in C as a wrapper around the aggregated object.
The second option is the least amount of extra work, but still requires that extra work, thus extra time and effort.
Because it's a fact that .NET is SI only, we as .NET developers can only opt for one of these two options and move on. There are however situations where neither of the two will work, or will result in a lot of duplicate code, which
results in extra maintenance issues. Most of these situations involve inheritance from an existing .NET framework class. Let's look at such a situation: adding optional serviced component functionality to a class.
When SI doesn't cut it.
To make a class be able to participate in COM+ services, it has to inherit from ServicedComponent, which is a class of the .NET framework. It can't aggregate a ServicedComponent object and mimic its interfaces nor can it re-implement its interfaces, it has to derive from that class. Say, we have a class C and that's a class derived from another class we wrote, A. A derives from System.Object. To make sure C is able to participate in COM+
transactions, we have to make C derive from ServicedComponent, directly or indirectly. In the case of .NET, we don't have another choice but make A inherit from ServicedComponent. Because A derives from System.Object, this is not a problem per se. Another class, B, also derives from A. Because A is a serviced component now, B is also a serviced component all of a sudden. But perhaps we want to marshall B by value using a remoting network, or we want to keep B very slim, without the overhead a serviced component brings on the table. In short: we have to rework our class hierarchy: we have to duplicate A's code into two classes: A and Adupe. Adupe is now becoming the base class for B, so B's clean from serviced component's inheritance. However, this results in two implementations of A's code in our class hierarchy. We can solve this somewhat by creating a class Aaggregate, which contains A's code and which is aggregated in A and Adupe, and using option 2, we can make A look like it is implementing Aaggregate's interface in full. This solves us from the duplicate implementation of A's code, however in return it gives us the maintenance nightmare of supporting the same interface in three classes while there is just one actual implementation.
A total different story is the situation where A doesn't derive from System.Object, but from another framework class, say, CollectionBase. Because this is a theoretical discussion, the class mentioned is just an illustration. This creates a problem: ServicedComponent can't be aggregated nor re-implemented, so CollectionBase should be aggregated or re-implemented. Because CollectionBase is an abstract class, we first have to derive a new class from CollectionBase which we then can use to be the aggregated class inside A. Another option is the drop CollectionBase and re-implement its functionality in A. Although this maybe seem like a reasonable option, it's silly.
Why would you re-implement an existing abstract base class which is solely there for the purpose to make life easier for you?
This problem occurs in almost all situations where a class C inherits from a base class A and wants to add behaviour defined by an interface which is already implemented in another class: you then have to choose which base class requires the least amount of duplicate code, and aggregate/re-implement the class you didn't pick to inherit C from. You can't design the extra code away, other than merging classes, simplifying a hierarchy, or opt for full MI. In the case of behaviour implementation and inheritance from .NET classes, you can't merge classes or simplify your hierarchy, because the .NET classes are not changeable. Because .NET also doesn't support
MI in full, you have just one choice: duplicate code.
Caveats
Full MI support has caveats. Most of them are however not the problem of the .NET framework user: they are on the plate of the .NET framework developers: implementing MI is hard, it's complex and there are other problems as well: because of the multi-language nature, you can open up MI to languages which don't support it. This is the case when you inherit C from A and B and C, together with A and B are part of a class library. This class library is then referenced in a project using a language which doesn't support MI. C is then available as a type, however C inherits from both A and B. In theory, the non-MI supporting language now supports MI because it can inherit
from an MI type. Another problem is the way how MI should be implemented. Eiffel has a very elegant MI implementation, C++ has another, more raw implementation of MI. Is an implementation of MI ala Eiffel in the CLR enough to support MI in C++ or vice versa? One of the classical problems of MI is the 'diamond' inheritance tree: base class A is inherited by B and C. D inherits from both B and C. What if A contains an abstract method which is implemented differently by B and C, which implementation is then exposed by D? There are solutions for this problem, which is by the way also a problem already possible in .NET, because it is interface related, however these solutions ask for different implementations and which one to pick? A good reason to drop MI? Not at all, since you can run into this (naming) problem today too when aggregating objects for example.
Eiffel is available for .NET today, with generics, full MI and a set of advanced concepts like design by contract. It might seem like a good alternative, but Eiffel is a language with a relative small userbase and there are problems mixing MI constructs written in Eiffel with regular C# code by referencing a library written in Eiffel for .NET.
It shows however that the choices how to implement MI is not solely a responsibility for the CLR team, but also for the language designers. After all, generics is also a technique which is implemented in various ways in a wide range of languages. Still, the CLR implements one way of generics, so it is possible to choose one implementation for the CLR, why can't it be done for MI as well?
Conclusion
Developers run into various problems when they want to build software on top of the framework classes provided. Problems which are the result of the absence of MI in the framework or at least the absence of MI support in one of the major .NET languages (C#, J#, VB.NET and C++). In a lot of cases, the developer can work around the problems by implementing redundant code, code which is already available to the developer by the presence of an (abstract) base class in a class library, for example the .NET framework. Avoiding the re-implementation of functionality that is already available is one of the reasons why Object Oriented (OO) development is preferred in a lot of situations over procedural development (due to its polymorphic nature. Re-use of code is of course available in procedural languages as well, though not in the way OO offers it). Imagine that you would have MI in the framework. All interfaces with 'able' at the end of their name could have a basic, abstract implementation in the framework which could be used as a base class for your own classes and using the strategy pattern, you could add just the functionality required to make the abstract code work for your particular situation. That way, you don't write extra code at all, better yet, you
write less code than when you would have implemented the interface from scratch, because a lot of interfaces require plumbing code which is always the same and which could be perfectly implemented in the abstract base class.
To make .NET the platform of choice, I can't see any other conclusion than that Microsoft implements some form of multiple implementation inheritance. Make it a choice. Now developers don't have a choice, and in some situations are forced to do a lot of extra work, have to redesign their class hierarchies to work around SI limitations. When it is a choice, people can still opt for a pure SI language, for example C#, or for an MI language like MC++.
Links
Below are a list of links to material worth reading. Most of them are usenet discussions, but very recommended if you are interested in the material.
A personal note: in one of the threads, I stumbled upon one of my own postings, arguing against MI because it wouldn't be necessary and it would cause a problem with languages which don't support MI but target .NET. It was funny to see how a person can change his way of thinking once you know more about the subject discussed. :)
I wish everyone a happy, good, healthy, warm and above all, .net-ful, 2004! :)
Update: Because I live in Scheveningen, Holland and I was awake early enough, my wife and I went outside to look at the thousands of people participating in the traditional 'nieuwjaarsduik' (new years dive) into the Northsea. It's -2 degrees Celcius, it's snowing like crazy and a firm cold wind makes it really cold outside. It really was a great sight seeing all those people diving into the sea in this cold weather, most of them wearing boxers or swim suits :) (Sorry, no picture, I still haven't bought a new digital camera).
More Posts