NineOnions and PurpleCarrots

|

The search for nonsense.

Archive for February, 2009

Finding a Better Overload

Tuesday, February 3rd, 2009

One of the latest issues I’ve been working on with C5 is to close the loop on a few compilation warnings that are not #pragma warning warnings. The last of these involves CLS compliance. The C5 library currently advertizes itself as CLS-compliant, but it actually is not. The one example that I am currently wrestling with is the IDictionary<K,V>.Find() overloads. There are two overloads for the Find() method. Their signatures are:

bool Find(K key, out V value);
bool Find(ref K key, out V value);

These two methods are unambiguously distinguished in C# because the compiler requires the use of ref or out as the method signature dictates. However, for other languages, such as Boo, this “by reference” argument passing is inferred. The Common Language System dictates that each overload of the same method must differ by more than just the pass-by type of its arguments (CS3006).

Normally this could be remedied by applying a [CLSCompliant(false)] attribute to the method in question and move along, probably creating a new, compliant method and marking the old one obsolete. The confounding issue is that a core C5 principle is programming to interface. Thus, every method (save one or two) on the public classes is implementing a method defined in an interface, including this one. Interfaces do not have the option of having certain methods be non-CLS compliant. It’s all or nothing (CS3010).

This leaves me in a bit of a catch-22. I cannot ensure cross-language accessibility without changing the interface method signature, but that introduces a breaking change. I have a feeling that both method implementations are widely used among C5 users too.

The first overload is apparent as the default use case, but the second overload is useful when your key may be prototyped. For example, in our dictionary we have a Person as the key and a Account as the value. The person has a bunch of information on it, but the equality comparer is only concerned with the personId:

public class Person : IEquatable<Person> {
  private int personId;
  public string Name {get; set;}
  ...

  public bool Equals(Person other) {
    return this.personId.Equals(other.personId);
  }
}

Then if we knew the personId we could construct a prototype of the person and use the Find() method to get not only their account, but the actual person.

var person = new Person(1);
Account account;
if (Accounts.Find(ref person, out account)) {
  Console.WriteLine("Found {0}'s account", person.Name);
} else {
  Console.WriteLine("Account not found");
}

So, with that example in mind, how do I proceed? The current idea I have is to mark the second overload [Obsolete], create a new overload or method, and deal with the non-CLS compliance for a minor release or two before removing the non-compliant overload. Perhaps I should remove the non-compliant overload outright, like a band-aid.

As well, what should the new method signature be? After thinking for a while, here are my two top choices:

bool Find(K key, out KeyValuePair<K,V> pair);
bool FindWithKey(ref K key, out V value);

Which of those two would be better from an API user’s point of view?  My instinct says the second one is most clear, but the first follows the existing naming.  What’s your take?

Getting Back to Coding

Sunday, February 1st, 2009

After taking a couple of months away from my open source coding, I’ve decided to delve back in some. In the off time, I’ve been playing a good deal more EVE, but I’ve gotten that itch again, and so I’m going back to my old standbys.

I’m extremely happy to see that Boo‘s development has picked back up again. Rodrigo recently released the Boo 0.9 and it has greatly increased the extensibility of the language, including making pattern matching a key part of the experience.
I have also started working on my C5 fork again. Unfortunately, I have not been able to get the backing of the original developer of C5, Peter Sestoft. The “official” development of C5 remains an opaque process and no work appears to have been done in over a year. After failing to receive any encouraging response, I decided to go ahead with creating the fork. My reasons in full are the matter for another post.
In restarting my work with C5, I’ve also begun transitioning over to using MbUnit and the Gallio framework for unit testing. I’ve found the framework to be great for parameterized unit testing, and it integrates pretty well with Pex. I’m submitting a few patches to MbUnit as I go along, including one that allows the [Parallelizable] attribute to propogate to Pex tests, letting multiple tests run simultaneously.
As this is one of my first posts again, I’ve decided to put a cap on how much I put into any one post. I found that my previous, failed attempts to start blogging revolved around the fact that I wrote too much, making blogging too time consuming to keep up. So, hopefully there will be many short postings to get me into the habit.

Unexpected Behavior

Sunday, February 1st, 2009

“0929″ != 929.

This is the premise of a recent bug that I needed to bash. I’m processing a flat file wherein a time is specified in “HHmm” format. The mapping calls for the two left characters to be interpreted as the hour and the two right characters to be interpreted as the minute. This works great for times such as “2204″ or “1039″, but was failing miserably for some reason. The exception that was raised stated the DateTime was not in the correct format. I looked into the mapping file and saw a proper time “0929″ equivalent to 9:29 AM. In debugging, however, the hour being passed to the DateTime constructor was 92. This was the obvious source of the error.
Looking back at the mapping file, I learned that the mapping software we are using transparently converts the “0929″ string into the number 929 before passing it along the pipe. Converted back into a string for the LEFT(2) and RIGHT(2) gave a nonsense time of 92:29. This type of we-hid-the-details-from-you bugs are absolutely irritating. The workaround was relatively simple, just adding a few more controls into the mapping, but it shouldn’t have been necessary. The datum was a string value and had no sense being converted to an integer before actually being turned into a DateTime value.