Part B
val a = 2.0
val b = Math.sqrt(2)
val c = b * b
println(a == c) // prints false
println(a ~ c ± 1E-12) // should print true because Math.abs(a - c) < 1E-12
So, we need to define a ~ operator. But we can't add it to
the Double class. Instead, define a class
class ApproxValue(value : Double) {
def ~(other : Double) = ...
}
and a companion object
object ApproxValue {
implicit def double2ApproxValue(d : Double) = new ApproxValue(d)
}
Then a ~ c will compile. What do you know about the result
of that expression? (a) It should support ... (b) It needs to store ...
ApproxValue with |value - d| or you could return an object of
another class that stores value and d. Whichever you chose in 1, add the
code for the ± method to that class.
The method name ± is a bit curious because it is not an ASCII character. However, in Java and Scala, you can use any Unicode character as a method name. (The ± is U+00B1.)
How do you enter such a thing? That depends on your operating system. In
Linux, hit the Compose key (which I mapped to the otherwise useless Caps
Lock) followed by + -. You know your operating system—just follow its
procedure. Or copy/paste it from this document. Or wimp out and use
+- instead.
Your ± operator should return a Boolean,
true if the absolute value of the difference is less than the
threshold. What is the code of that method?
Part B
1.5 kg 10 m / (s^2)
To keep things simple, we will support grams, meters, and seconds only. We start with this class:
class Units(val value : Double, val g : Int, val m : Int, val s : Int) {
// Unit is something else :-)
// g = grams, m = meter, s = seconds, e.g. new Units(10, 1, 0, -2)
// is 10 m/s^2
override def toString() = "" + value +
unitToString(g, "g") +
unitToString(m, "m") +
unitToString(s, "s")
private def unitToString(u : Int, uname : String) =
" " + (if (u != 0) uname + (if (u != 1) "^" + u else "") else "")
}
How do you construct an object of type Units that
represents the speed
of light? How do you construct an object representing 1.5 kg?
1.5 kg. So, kg
should be a method that yields a Units object. A method of
which class? It can't be a method of Int. So, let's define a
class
class UnitsValue(value : Double) {
def g = new Units(value, 1, 0, 0)
def m = new Units(value, 0, 1, 0)
def s = new Units(value, 0, 0, 1)
def kg = new Units(1000 * value, 1, 0, 0)
def in = new Units(0.0254 * value, 0, 1, 0)
...
}
What is the result of new UnitsValue(1.5).kg()?
1.5 into
new UnitsValue(1.5)?object Units, the
companion object to class Units. What is your
method?object Main {
def main(args: Array[String]) {
import Units._
val x = 10.0 in
println(x)
}
}
(Note that the import is restricted to the body of
main.)
What happens?
val x = 10.0 in val y = 0.254 m val z = x + y println(z)
Supply a method
def +(other: Units) =
if (m == other.m && g == other.g && s == other.s)
...
else
throw new IllegalArgumentException("Incompatible units " + this + " and " + other)
x to
val x = 10.0 in + 0.254 m
What happens? How can you fix it? (This is a bit disappointing, and it demonstrates a common problem with internal DSLs—a leaky abstraction. Users cannot fully comprehend the error messages without knowing about details of the implementation.)
* and / operators for units. For
example, we want
val x = 10.0 m / ((1.0 sec) * (1.0 sec))
to yield 10 m/s2. (Remember to multiply the values, add/subtract the powers of g, m, s.)
val x = 10.0 m val y = 2.0 * x
Why doesn't it work? How can you make it work?
10.0 m / s^2 compute 10 m/s2? If so,
outline a strategy. If not, explain why not.