Scala has some things about it that are kind of compelling and a lot that is not. I am a huge believer in avoiding any kind of syntactical shorthands and don‘t get excited by things like hashrockets (just the name makes your IQ go down 10 points thinking about it). However, I am intrigued by Scala‘s attempts to fundamentally reorient the language‘s orientation toward expressions. My intrigue grew as I revisited some code I had done in Objective C for doing Solar Positioning, and ported it to Java.
It‘s kind of amazing how many people think the earth just rotates around the Sun and the Sun can be found at the equator each day at noon. Of course, thanks to the tilt of the earth on its axis, the Sun in fact appears at the equator only 2 days a year (the equinoxes), and on two other days a year, it is the farthest distance from the equator it gets: 23.5º (the solstices). That would already make the problem of figuring out where the sun is at any given moment complicated enough, but that‘s just the first bite of the pie. Hopefully you all remember Kepler‘s 3 laws of planetary motion. The orbit is an ellipse (law #1) and the orbiting body sweeps out equal areas in equal amounts of time (law #2), which means of course, the orbiting body is traveling faster at the parhelion than at the aphelion (when it‘s near the focus the sun occupies). Then there are some abnormalities in the latitudinal motion of the earth.
So the net result of all this is that there is a lot of work to answer simple questions like what is the elevation angle of the sun right now?
Here‘s the really sick part: almost every piece of code I found was a messy heap of unnamed constants, conversions all over from degrees to radians and back (the trig functions in C and Java operate on radians), and after wrestling with them, the saddest part is, the programmer comes out the other side not one iota more enlightened about what the hell the various computations are actually doing (of course, (s)he has a wikipedia sidecar going, but even that blurs and descends into white noise as cosines and arccosines are used to coax a result that makes sense. Oh, and wait, there‘s also some historical intrigue here: one dude who published a method years ago that a lot of people were happy with, ended up instead with the name mudd in the positioning universe years later when it was found that his approach didn‘t work for all coordinates and had some other errors in it.
- The code should be readable and should have types. So the offset of the sun at solar noon from the equator is called the declination. That should be a type. That type should enforce the fact that only values between 23.5 and -23.5 are valid.
- There should be an easy way to go back and forth between degrees and radians without dirtying up the code. In C versions, we could just cheat and make macros like cos( x ) Math.cos( toRadians(x) ). Java‘s Math has a toDegrees() and toRadians(), but then an equation like the elevation angle will end up on 3 or 4 lines with every single input being converted to radians.
- The code should communicate what is going on, including even stupid things like units. So another option of course would be make the class Declination, then store the value with a default units of degrees, but then just have a toRadians method inside. I don‘t like that solution. However, I do like the idea that the units and their derivation are part of the definitional aspect of the class Another example is local solar time: it‘s merely a representation of the time at your latitude as a decimal, so hour + (minutes / 60). So 10:30 becomes 10.5. It‘s merely currentTime.asDecimal().
Units are discussed extensively in Fowler‘s Analysis Patterns though mostly to address the common practice of storing things like weight without any notion of units at all (assuming that all users will be ok with whatever the prevailing assumption was). I think there is a strong case for being more serious about units for cases like these, and then having the ability to define conversions easily enough in the classes. But even if you did that, every single astronomical class would have to provide a stupid override, unless you made them all derive from a class.
Finally, there‘s the question of whether the equations really need to be that readable. I thought about literally redoing the equations as a set of class operations, then the overall computation (which has to be done progressively) as a Builder. There are a few advantages to this approach: the equations are not likely to change and they are not being interpreted so leaving them where someone can come read them is a tad silly. Probably better to provide a link to a source in the JavaDoc and the just have operations like: computeEquationOfTime() and correctForLatitudinalDrift(). Math people tend to think that the purest expression of what is going on is in seeing the actual equations, but when you are dealing with things in the real world, there are definitely forms that could communicate much more clearly exactly what is going on.
Oh, one other problem: there are dependencies in the computation. For instance, you can‘t compute the elevation angle until you have the declination, so you are faced with the ugly choices of either holding partial state somewhere as the computation is going on (another possible reason to use a builder), or passing everything that has been computed down the line. I like the functional notion of being able to just consider an equation that has terms in it and as each term is encountered, compute that.
I was not surprised to find that Boost has a geometry package , and while it contains the notion of an equatorial coordinate system, it seems to only address things like distances. It does seem to handle the problem of types by being templated, but the implementation requires the use of Traits. Had not looked at Traits extensively since they were added to the language, looks really good the way it's done here. Some people will think that there is too much genericity setup for the amount of computation that actually ends up occurring.
I think the Builder is probably the most readable approach and has one other added advantage: there are a number of computational approaches, with greater or lesser precision. That could be handled by having different ConcreteBuilders, and even having some core operations in a base and the more or less corrective actions in the derived classes.