Skip to content

Tutorial ~ Example (Part 1)

Vlad Ureche edited this page Jun 9, 2015 · 15 revisions
ildl logo 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 = 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? 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 a toy example, it is clear that refactoring by hand is tedious and very error-prone, even with the advanced features of Scala, such as implicit conversions. Furthermore, we have to introduce different artifacts in the code, which makes it lose its high level.

From Here:

Frog Work Ahead: 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).

Clone this wiki locally