Sunday, July 31, 2011

Scala – Get a taste of implicit parameters through Quicksort

Why is Scala great?

I'm tempted simply to state that Scala is great. However, I know that many developers have not yet heard of it, so I'll try to explain why that's true.

Why? Because it offers a number of goodies that other programming languages don't.

For those who don't know this yet, I would ask them to imagine shopping for the best 10-year-long package tour. 

I wouldn't say that everything inside Scala's Dream Package is unique or without peer, but the price you'll pay and the goodies you'll get make it by far your best overall choice for those years to come.

What are the features that make Scala so great? Among other things, it's concise, elegant, type-safe, object-oriented, functional, extremely fast, compatible/inter-operable with Java, has great pattern matching, and uses a powerful actor model borrowed form Erlang.

Wouldn't that be a delightful 10-year tour on the Scala boat?

So, give it a shot and get onboard -- there's plenty of space, and everyone's friendly too!

In other words, Scala is versatile enough to be used everywhere, and it has a large community with excellent support.

I realize that a newcomer can hardly avoid initial sea-sickness, but there are at least 10 years ahead of real joy. A few months of hardship will more than repay, trust me.

I would like to try to convince you by showing you one of Scala's unique features  -- Implicit Parameters.

What are they? Some parameters used to define a method can be marked with the keyword 'implicit'. Where are such parameters used?

Under certain circumstances if the developer has not provided some parameters in a function call, the compiler will try to implicitly insert the values for all those missing parameters, so the code will be properly compiled and the function call will be properly executed.

In the coming examples I will try to gradually show you when and why is good to have implicit parameters.

I will try to use Scala to implement  the Quicksort algorithm.
Far from being the fastest Scala implementation of Quicksort the implementation shown here is just good enough for our purpose.

Here at the http://en.wikipedia.org/wiki/Quicksort link you can find more about Quicksort.

A naive scala implementation - it only addresses the Int type

object NaiveQuickSort {
  def qsort(list : List[Int]) : List[Int] = {
    list match {
      case List() => List()
      case h :: tail => qsort(tail.filter(_ <= h)) ::: List(h) 
                         ::: qsort(tail.filter(_ > h))
    }
  }
  def main(args: Array[String]): Unit = {
    println(qsort(List(2, 4, 1, 6, 5)))
  }
}

the displayed result is: List(1, 2, 4, 5, 6)

A parametrized implementation - it is a parameteriezed implementation, so it could be used for any types as long as a comparison function for that types has been explicitly provided. You can see the explicitly provided parameter when calling the qsort function. This might be avoided though, but this is a another great topic related to the currying concept.

object ParameterizedQuickSort {
  def qsort[T](less : (T, T) => Boolean)(list : List[T]) : 
    List[T] = {
    list match {
      case List() => List()
      case h :: tail =>
        (qsort(less)(tail.filter(less(_, h)))) ::: List(h) :::       
        (qsort(less)(tail.filter(! less(_, h))))
    }
  }
  def main(args: Array[String]): Unit = {
    println(qsort((x: Int, y: Int) => x < y)(List(2, 4, 1, 6, 
         5)))
    println(qsort((x: String, y: String) => x < y)(List("violin", 
     "piano","whistle", "guitar","saxophone")))
   }
}

A parametrized implementation with implicit parameters
As you can see there are 2 lines that defines the comparison functions for the Int and String types. These lines basically let the compiler know what values it can implicitly supply when necessary.


  implicit val lessIntOp = (x: Int, y: Int) => x < y
  implicit val lessStringOp = (x: String, y: String) => x < y


Having the implicit marker for the 'less' parameter of the following line lets the compiler know which parameters of the qsort function might be missing in a function call, so the compiler could insert them when needed.



def qsort[T](list : List[T])(implicit less : (T, T) => Boolean)
  : List[T]


In the following lines the 'less' parameter is not provided and the code still compiles and is properly executed.


println(qsort(List(2, 4, 1, 6, 5)))
println(qsort(List("violin", "piano","whistle", 
  "guitar","saxophone")))


And, here is the whole code you can use to test this.

object ImplicitParameterizedQuickSort {
  implicit val lessIntOp = (x: Int, y: Int) => x < y
  implicit val lessStringOp = (x: String, y: String) => x < y

  def qsort[T](list : List[T])(implicit less : (T, T) => Boolean)
  : List[T] = {
     list match {
      case List() => List()
      case h :: tail =>
        qsort(tail.filter(less(_, h))) ::: List(h) :::  
        qsort(tail.filter(! less(_, h)))
     }
  }
  def main(args: Array[String]): Unit = {
    println(qsort(List(2, 4, 1, 6, 5)))
    println(qsort(List("violin", "piano","whistle",  
      "guitar","saxophone")))
   }
}


This is it. Just a sec.
Please look at the next chunk of Erlang code (the code was taken from Erlang Programming - Francesco's book):


qsort([]) -> [];
qsort([X | Xs] ->
  qsort([Y || Y <- Xs, Y =< X]) ++ [X] ++ qsort([Y || Y <-Xs, Y > X]).


and compare it with this:



def qsort[T](list : List[T])(implicit less : (T, T) => Boolean)
  : List[T] = {
     list match {
      case List() => List()
      case h :: tail =>
        qsort(tail.filter(less(_, h))) ::: List(h) :::  
        qsort(tail.filter(! less(_, h)))
  }
}



They look pretty similar don't they?


This is another reason Scala is just great. It resembles Erlang in so many ways.