Saturday 28 November 2009

Custom value types are like buses

You wait years to write one… and then six of them come along at once.

(Cross-posted to the Noda Time blog and my coding blog as it's relevant to both.)

When we started converting Joda Time to .NET, there was always going to be the possibility of using custom value types (structs) – an opportunity which isn't available in Java. This has meant reducing the type hierarchy a fair amount, but that's actually made things simpler. However, I didn't realise quite how many we'd end up with – or how many would basically just wrap a long.

So far, we have 4 value types whose only field is a long. They are:

  • Instant: an instant on the theoretical timeline, which doesn't know about days of the week, time zones etc. It has a single reference point – the Unix epoch – but that's only so that we can represent it in a concrete fashion. The long represents the number of ticks since the Unix epoch.
  • LocalInstant: this is a tricky one to explain, and I'm still looking for the right way of doing so. The basic idea is that it represents a day and a time within that day, but without reference to a time zone or calendar system. So if I'm talking to someone in a different time zone and an Islamic calendar, we can agree on the idea of "3pm tomorrow" even if we have different ideas of what month that's in and when it starts. A LocalInstant is effectively the instant at which that date/time would occur if you were considering it in UTC… but importantly it's not a genuine instant, in that it doesn't unambiguously represent a point in time.
  • Duration: a number of ticks, which can be added to an instant to get another instant. This is a pure number of ticks – again, it doesn't really know anything about days, months etc (although you can find the duration for the number of ticks in a standard day – that's not the same as adding one day to a date and time within a time zone though, due to daylight savings).
  • Offset: very much like a duration, but only used to represent the offset due to a time zone. This is possibly the most unusual of the value types in Noda, because of the operators using it. You can add an offset to an instant to get a local instant, or you can subtract an offset from a local instant to get an instant… but you can't do those things the other way round.

The part about the odd nature of the operators using Offset really gets to the heart of what I like about Joda Time and what makes Noda Time even better. You see, Joda Time already has a lot of types for different concepts, where .NET only has DateTime/DateTimeOffset/TimeSpan – having these different types and limiting what you can do with them helps to lead developers into the pit of success; the type system helps you naturally get things right.

However, the Joda Time API uses long internally to represent all of these, presumably for the sake of performance: Java doesn't have custom value types, so you'd have to create a whole new object every time you manipulated anything. This could be quite significant in some cases. Using the types above has made the code a lot simpler and more obviously correct – except for a couple of cases where the code I've been porting appears to do some very odd things, which I've only noticed are odd because of the type system. James Keesey, who's been porting the time zone compiler, has had similar experiences: since introducing the offset type with its asymmetric operators, found that he had a bug in some of his ported code – which immediately caused a compile-time error when he'd converted to using offsets.

When I first saw the C# spec, I was dubious about the value of user-defined value types and operator overloading. Indeed I still suspect that both features are overused… but when they're used appropriately, they're beautiful.

Noda Time is still a long way from being a useful API, but I'm extremely pleased with how it's shaping up.

Friday 13 November 2009

Porting fidelity

Before Noda Time took off, I had assumed that we would port it by keeping the same class hierarchy (with maybe a few structs or enums instead of classes and constants), changing names to be .NET-idiomatic, and turning getters/setters into properties.

As it happens, we've actually done a fair amount of redesign for the core concepts. For example, in Joda time an Instant has an associated chronology – despite that not making any conceptual sense. It's handy for the implementation, but it's not really "right" from a user's point of view.

My current aim is to create as clean an API as possible without changing the design so much that we need to radically change how it's implemented. To put it starkly: I don't trust myself to fully understand the subtleties of the Joda Time code. Most of the tricky stuff is implemented in the classes which handle calendar systems and time zones, and I want to leave those guts well alone. (We may be using a model closer to JSR-310 for storing/loading time zone information, but we'll see how that pans out.)

To put it another way, I want to be a plastic surgeon, not a heart surgeon. I believe that Joda Time is as strong as an ox, but has a few warts. (Don't get me wrong, it's a lovely library in general – but it would be insane to expect there to be no possible improvements after all this time.) This approach seems to be working for us at the moment, but the proof of the pudding will be in the eating.

Interestingly enough, we're violating the agile "no big upfront design" idea somewhat… we're trying to design a lot of the public-facing concepts at least before implementing anything. I think this is important for something like Noda Time – and it's only possible because we have Joda Time (and JSR-310) to look at and learn from. We're not doing completely blue-sky thinking (i.e. guesswork) – we're just honing the thoughts before making them concrete.

So that's two options (the original idiomatic but high-fidelity warts'n'all approach and the current idiomatic but more liberal one). You can go further in each direction, of course. Some ports appear to be very definitely ".NET with a Java accent" – effectively a mechanical conversion with the very barest nod given to .NET idioms, leaving a fairly unpleasant experience at the end. Others are more "let's take a good idea and rearchitect it from the ground up" – which means you really need to understand the subject matter thoroughly.

Just to make it clear, I do hope that by the end of the project I've learned an awful lot about time. In fact, I've already learned quite a lot. That doesn't mean I would trust myself to try to write Noda Time without borrowing heavily from Joda, however.

All of these approaches have their pros and cons in terms of time-to-useful-implementation, comfort level for developers on the original platform moving over to the new one, elegance for developers who have never used the original version, and freedom to innovate. It's always going to be a tricky path to navigate… but at the moment it feels like we're doing the right thing. Only time will tell for sure.

First week report – phew!

Exactly a week ago, to the very train1, I created a Google group, a Google code project, and a blog post announcing Noda Time to the world.

In the course of the last week:

  • The mailing list / group has 87 members and 789 posts (admittedly quite a few of them being "can I have a Wave account please," "yes," or "+1 for NUnit" – but lots of meaningful discussion too)
  • The wiki has a good skeletal structure, and some useful topics already
  • We've opted for NUnit 1.5.2 (despite the reported Mono issues – if we run into them, we'll try to help fix them)
  • We've discussed many of the other "meta" issues that need sorting – continuous builds, mocking frameworks (if necessary), code signing, style checkers, build systems etc.
  • We've discussed the concepts of Joda Time themselves, and made some changes to clarify the model (more on that in the next post). Fixing the concepts has removed the DateTime naming issue, which is a major relief.
  • We've got a skeletal class structure in our Mercurial repository, which has now been refactored a couple of times and still isn't quite right…
  • Stephen Colebourne (the driving force behind Joda Time) is on the mailing list, giving us incredibly useful insights based on his experience with Joda and JSR-310.
  • I've done less work on C# in Depth and Groovy in Action than I should have, really :(

I hadn't anticipated even slightly this level of interest. I don't know whether this is because of a perceived need for Noda Time, or the attraction of working on a project which aims to give guidance for other projects, or because people just want to work on an open source project, and Noda popped up at the right time.

I'm really excited by the whole thing. I'm looking forward to the first commit where we can ask the Unix epoch what its year is in UTC (ISO calendar) and get back the answer "1970". Maybe by next week, that will be reality.

1 A date/time field which we don't plan to support by default, but one which is meaningful to me. The 17:18 from Paddington to Oxford has a lot to answer for.

Inaugural post

This will be the blog for Noda Time, containing news of progress, interesting problems encountered (or design patterns discovered) and introspectives about decisions we've been mulling over. It's a team blog, so expect even more inconsistency in posting style than you'd normally get from me...

I'm hoping to write two posts this evening to explain how far the project has come in a week, and the choices available when porting a project from another language/platform.