-
Notifications
You must be signed in to change notification settings - Fork 1
Tutorial ~ Example (Part 1)
This section will transform the code shown in [the introduction](https://github.com/miniboxing/ildl-plugin/wiki/Tutorial-~-Introduction) using the `ildl-plugin`. If you have either the [demo virtual machine](https://github.com/miniboxing/ildl-plugin/wiki/Setup-%7E-VM) or have [set up `ildl-plugin` locally](https://github.com/miniboxing/ildl-plugin/wiki/Setup-%7E-Local), you can use the `ildl-scalac` runner script to execute the code on your machine.
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
$ ildl-scala Test
(5,13)
(-34,34)
So far, so good. Let's now assume we heard from a fellow programmer that tuples are inefficient and that we could encode our complex numbers as long integers. The first step would be to change the alias:
type Complex = LongNow, 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? Still wrong? Yes, because long integers have + and * operations of their own, so the IntPairAsComplex methods are not being invoked anymore. While we started with this toy example, it becomes clear that refactoring by hand is tedious and very error-prone. Furthermore, we have to introduce different artifacts in the code, which makes it lose its high level.
Now, how would ildl-plugin work here? Let's find out in part two.
The `ildl-plugin` is a meta-programming technique aimed at allowing safe, custom transformations across library boundaries. Using `ildl`-based transformations, we were able to obtain speedups in excess of 20x and have optimized code across a wide range of use-cases. **Return to the main page** or **return to the OOPSLA Step by Step Guide**
Some of the pages of this wiki are in flux. If you can't find what you are looking for, please [file a bug](https://github.com/miniboxing/ildl-plugin/issues).