Gah! Lack of covariant return types!! aaaaaaaaaaaaargh!!!!
So I'm adding more functionality in the collections classes that I've written in order to work on what I want to be a one day project this weekend. I want all my infrastructure in place so i can focus on the task at hand.
The functionality that I realized was missing was the ability to map (or fold) a function over all the elements in a collection. I.e. something akin to:
public delegate B Function<A,B>(A a);
public interface ICollection<A>
ICollection<B> Map<B>(Function<A, B> f);
I thought about it a little more and realized I needed a little more functinality. For example, I knew I'd want to have an implementation that just returned a thin wrapper over the underlying collection, that way changes to the returned collection would be reflected in the underlying collection. In order to support that I would need two functions, one to map from the original to the new, and one from the new to the original. In otherwords, a bijection:
public interface IBijection<A, B>
Function<A, B> Function
Function<B, A> Inverse
Using that I could then have the collection interface look like:
public interface ICollection<A>
ICollection<B> Transform<B>(IBijection<A, B> bijection);
This was working perfectly for my needs, and allowed you to do some nifty things. For example I could then write:
ICollection<int> ints = strings.Transform<int>(new Bijection(stringToInt, intToString));
The underlying collection would then contain the string "4". All in all very useful. I then starting writing a bit and I realized that I needed this functionality on my list interface. But, of course, for lists i would instead want an IList<B> returned. When i tried this though I got the message: Warning 1 The keyword new is required on 'IList<A>.Transform<B>(IBijection<A,B>)' because it hides inherited member 'ICollection<A>.Transform<B>(IBijection<A,B>)'
Curses!! This means that if a consumer of my list wants to transform it they will have to call the transform method and then cast the result down to an IList<B>. I hate casts. They usually indicate to me that something is being wrong. However, in this case I need them because the language won't allow me to express this completely safe concept. My alternatives are to create methods like: TranformCollection and TransformList, but then I need to make sure that all implementations remember to call into TransformList from their overridden TransformCollection method.
This is one of the top things I would like to see improved in the language and the CLR (which also doesn't support covariant return types). It's honestly one of the few things left that really impact my ability to use this language and design things that make sense.
Have you run into these issues? If so, is there a way you've found to get around them?
Edit: Omer points to an interesting thread with a mono developer about this. I'll have to see if the thoughts expressed by Jonathan Pryor will help me out of this rut. I don't mind a little extra work in writing the API if it means that consumers of the api will have a nice clean consistent way to use it without unnecessary casts.