Something that Scala and most other modern languages allow these days is variable names that contain what you might think of as non-traditional characters from the Unicode character set, e.g., Greek symbols such as π and τ. If you’re a C programmer, you have to settle for spelling out the name of the character:
const double PI = 3.141592654; double delta = x1 - x2;
But that’s OK, right? What’s the difference, really? The value π is one thing: it’s a universally recognized constant. But even with the example delta
above, don’t you want to name it something more descriptive, like marginOfError
anyway?
Well, yes, many times instead of using the characters verbatim from your physics textbook …
val f = m * a
… you spell it out so that the code is clearer:
val force = mass * acceleration
Likewise, even though Scala allows you to write the following:
val ω = 2 * math.Pi * f
… it’s probably better practice to write …
val angularVelocity = 2 * math.Pi * frequency
What’s the point of this post then? Sure, you can use “special” characters in variable names, but so far, I’ve discouraged you from doing it!
Nevertheless there are times when it is appropriate. If you are coding up an algorithm that consists of a series of well-known equations in a certain field of study, and the more your code looks like those equations, the easier it is to check it against the literature.
Consider the following—a series of values and equations for converting latitude and longitude to universal polar stereographic (UPS) coordinates, a way of representing coordinates at the earth’s poles:
UPS coordinates consist of a hemisphere—either northern or southern—and two distance components, easting and northing, both in meters:
object Hemisphere extends Enumeration { type Hemisphere = Value val Northern = Value('N') val Southern = Value('S') def fromLatitude(lat: Double): Hemisphere = if (lat < 0) Southern else Northern } case class UniversalPolarStereographic( northing: Double, easting: Double, hemisphere: Hemisphere)
Now compare the code below to the equations from the literature above:
def latLonToUps( lat: Double, lon: Double): UniversalPolarStereographic = { val hemisphere = Hemisphere.fromLatitude(lat) val φ = lat.abs val λ = lon val π = math.Pi val FN = 2000000.0 val FE = 2000000.0 val a = 6378137.0 val f = 1 / 298.257223563 val e_2 = f * (2 - f) val e = math.sqrt(e_2) val eOver2 = e / 2 val Cₒ = ((2 * a) / math.sqrt(1 - e_2)) * math.pow((1 - e) / (1 + e), eOver2) val kₒ = 0.994 val πOver4 = π / 4 val esinφ = e * math.sin(φ) val φOver2 = φ / 2 val tanZOver2 = math.pow((1 + esinφ) / (1 - esinφ), eOver2) * math.tan(πOver4 - φOver2) val R = kₒ * Cₒ * tanZOver2 val Rcosλ = R * math.cos(λ) val Rsinλ = R * math.sin(λ) val N = hemisphere match { case Hemisphere.Northern => FN - Rcosλ case Hemisphere.Southern => FN + Rcosλ } val E = FE + Rsinλ UniversalPolarStereographic(N, E, hemisphere) }
It’s not perfect: you still cannot set numerators above denominators, for instance. But isn’t that easier to compare to the literature than if we had to write out RsinLambda
or eSinPhi
?
(Note: UPS coordinates are only valid for latitudes near the poles. For simplicity, the code above does not check to make sure that the input latitude falls within those bounds. I mean, it’s complex enough as it is for the sake of exemplifying the point of this post.)
(Note: I’m aware that some of the characters in the code don’t show up correctly on all browsers, e.g., the subscript “O” and perhaps the φ. I’m working to correct that. Nevertheless you should be able to use such symbols in your source code.)