Tutorial ~ Example (Part 1) - miniboxing/ildl-plugin GitHub Wiki
$ cd ildl-plugin
$ mkdir -p sandbox
$ cd sandbox
$ vim example.scala # or your favorite editor
Let us take a small example:
object Test {
// "define" type complex based on integer pairs
type Complex = (Int, Int)
// add the addition and multiplication operation to complex numbers
implicit class IntPairAsComplex(val p1: Complex) extends AnyVal {
def +(p2: Complex): Complex = (p1._1 + p2._1 , p1._2 + p2._2)
def *(p2: Complex): Complex = (p1._1 * p2._1 - p1._2 * p2._2,
p1._1 * p2._2 + p1._2 * p2._1)
// we could define other operations here as well...
}
// test the output
def main(args: Array[String]): Unit = {
val x1: Complex = (3, 5)
val x2: Complex = (2, 8)
println(x1 + x2)
println(x1 * x2)
}
}
If we compile this code, we get:
$ ildl-scalac example.scala # invoke the Scala compiler + ildl-plugin
$ ildl-scala Test # invoke the Scala runner + ildl-plugin
(5,13)
(-34,34)
If running the
ildl-scala
command produces:
$ ildl-scala Test No such file or class on classpath: Test
> Then you need to unset the `CLASSPATH` environment variable:
> ```
$ unset CLASSPATH
$ ildl-scala Test
(5,13)
(-34,34)
(since we do not use ildl
in this section, scalac
and scala
can be used equally well)
So far, so good. Let's now assume we heard from a fellow programmer that we could encode our complex numbers as long integers. The first step would be to change the alias:
type Complex = Long
Now, suddenly, none of our code compiles anymore:
$ ildl-scalac example.scala
example.scala:7: error: value _1 is not a member of Test.Complex
def +(p2: Complex): Complex = (p1._1 + p2._1 , p1._2 + p2._2)
^
...
example.scala:16: error: type mismatch;
found : (Int, Int)
required: Test.Complex
(which expands to) Long
val x2: Complex = (2, 8)
^
14 errors found
An instinct would be to define implicit conversions from what we previously had, pairs of integers to long integers and back:
// implicit conversions:
import language.implicitConversions
implicit def intPairToComplex(p: (Int, Int)): Complex =
(p._1.toLong << 32l) | (p._2.toLong & 0xFFFFFFFFl)
implicit def complexToIntPair(c: Complex): (Int, Int) =
((c >>> 32).toInt, (c & 0xFFFFFFFF).toInt)
Great, with these implicit conversions, our program compiles again:
$ ildl-scalac example.scala
$ ildl-scala Test
21474836493
146028888104
But wait! What are those results?!? Those are not our resulting complex numbers! Of course, we need to convert back to complex numbers before printing:
println(complexToIntPair(x1 + x2))
println(complexToIntPair(x1 * x2))
Now, with these lines, our program became twice as large:
object Test {
// "define" type complex based on integer pairs
type Complex = Long
// add the addition and multiplication operation to complex numbers
implicit class IntPairAsComplex(val p1: Complex) extends AnyVal {
def +(p2: Complex): Complex = (p1._1 + p2._1 , p1._2 + p2._2)
def *(p2: Complex): Complex = (p1._1 * p2._1 - p1._2 * p2._2,
p1._1 * p2._2 + p1._2 * p2._1)
// we could define other operations here as well...
}
// implicit conversions
import language.implicitConversions
implicit def intPairToComplex(p: (Int, Int)): Complex =
(p._1.toLong << 32l) | (p._2.toLong & 0xFFFFFFFFl)
implicit def complexToIntPair(c: Complex): (Int, Int) =
((c >>> 32).toInt, (c & 0xFFFFFFFF).toInt)
// test the output
def main(args: Array[String]): Unit = {
val x1: Complex = (3, 5)
val x2: Complex = (2, 8)
println(complexToIntPair(x1 + x2))
println(complexToIntPair(x1 * x2))
}
}
Let's compile and run now:
$ ildl-scalac example.scala
$ ildl-scala Test
(5,13)
(34,40)
Wait, what? The first time we ran the example, the results were (5,13)
and (-34,34)
. The addition seems okay, but what about multiplication? Well, long integers have +
and *
operations of their own, so the IntPairAsComplex
methods are not being invoked anymore. And while the long integer addition corresponds to some extent to the encoded complex number addition (there are corner cases), the multiplication does not correspond at all.
We started trying to transform a toy example, but even for this toy example refactoring has become a nightmare: it's both tedious and error-prone, even with the advanced features of Scala, such as implicit conversions. Furthermore, we have to manually transform parts of the code, which obfuscates the original intent. Thus, although it was easy to introduce complex numbers, it is much more difficult to transform their representation.
Then next part shows how the ildl-plugin
automates the transformation and does it correctly.
From Here:
- continue with the example, part two
- return to the home page