import scala.language.dynamics
object SpaceAge extends Dynamic {
private val EarthSeconds = 31_557_600.0
private val PlanetRatio: Map[String, Double] = Map(
"Mercury" -> 0.2408467,
"Venus" -> 0.61519726,
"Earth" -> 1.0,
"Mars" -> 1.8808158,
"Jupiter" -> 11.862615,
"Saturn" -> 29.447498,
"Uranus" -> 84.016846,
"Neptune" -> 164.79132
)
private def calculate(planet: String, seconds: Double): Double =
seconds / EarthSeconds / PlanetRatio.getOrElse(planet, 1.0)
def applyDynamic(methodCall: String)(seconds: Double): Double =
calculate(methodCall.substring(2), seconds)
}
This approach starts by importing from packages for what is needed.
It then defines the SpaceAge
object as extended from the Dynamic
trait.
Then comes the definition of the number of earth seconds in a year.
Note the use of digit separators (_
) makes long numbers more readable.
A Map
is defined that associates the name of each planet with its orbit ratio.
The calculate()
method is defined as a regular function taking two parameters.
The applyDynamic()
method is used to intercept a method call, such as SpaceAge.onMercury(2134835688)
.
It takes a substring()
of the method name (e.g., to remove the "on" from "onMercury")
and passes it to the calculate()
method along with its seconds
argument.
Note that the applyDynamic()
method is defined as a curried function.
The applyDynamic()
method takes the method name as its first parameter, with any other parameters
(including no parameters) defined separately after that.
Otherwise, the applyDynamic()
method would have to support all possible parameters in its initial signature, which would
not be practical.