Monday, March 10, 2014

Why I like Scala (part 2)

It happens sometimes that nature of ordinary features is more interesting and more tricky than it is on the first glance.
Let's get back to example from previous post with using function and reconsider it again:

val resource : Closable = ... // take closable resource
using(resource){
  // return something meaningful
}

Many of you might have noticed that implementation based on Closable trait  did not support sugar like accessing resource variable inside using's code block without external variable. That is the problem which lies on fact that using is ordinary carried function, and there is no way to share arguments between curried calls, at least obvious and simple way. Well, let's add some sugar to using implementation.

Attempt 1
The first idea and right idea that comes up is to use lambda like this
def using[B]( a : Closable)(code : Closable => B) = {
    try{
      code(a)
    }
    finally{
      a.close
    }
  }

Then we might use it this way:
using(getResourceFromSomewhere()){ resource =>
  // doing something with resource
}
The problem is that type of resource is Closable and it is only one method close we can call there. Verdict: Unacceptable.

Attempt 2
Let's open up some information about type to using function.

def using[A <: Closable, B]( a : A)(code :A => B) = {
    try{
      code(a)
    }
    finally{
      a.close
    }
  }
Generics make some fog here, this solution will work nice for objects that already implement Closable trait. What's if we try to "protect" foreign objects that does not know nothing about Closable?
Previous post used implicit boxing. What if we used it again?

implicit def transform(r : InputStream) = new Closable{
  override def close(){
    r.close();
  }
}

...
using(new FileInputStream("/myfile")){ res =>
  // read from resource resource 
}
Alas. Variable res will have Closable  type again.
Verdict: Unacceptable.

Attempt 3
Eventially the idea is to provide entire information about type to our using function without narowing and... to add dedicated implicit convertion function.
def using[A, B]( a : A)(code :A => B)(implicit convertor :A => Closable) = {
    try{
      code(a)
    }
    finally{
      convertor(a).close
    }
  }

Aha, it works now:

implicit def transform(r : InputStream) = new Closable{
  override def close(){
    r.close();
  }
}

...
using(new FileInputStream("/myfile")){ res =>
  // read from resource
  res.read()
}


The last thing is to add implicit convertion from Closable to Closable. Indeed,  calling using with Closable instance will not work because there is no implicit convertion  in the current scope. So we have to add default implicit implementation like this:

implicit def trivialConvertor(a: Closable) = a


Tadam. There are many ideas how to continue improving using feature, but let's move on to something else. Stay tuned!