Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why we need implicit parameters in scala?

Tags:

scala

akka

I am new to scala, and today when I came across this akka source code I was puzzled:

def traverse[A, B](in: JIterable[A], fn: JFunc[A, Future[B]], 
      executor: ExecutionContext): Future[JIterable[B]] = {
    implicit val d = executor
    scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(
        Future(new JLinkedList[B]())) { (fr, a) ⇒
      val fb = fn(a)
      for (r ← fr; b ← fb) yield { r add b; r }
    }
  }

Why the code is written using implicit parameters intentionally? Why can't it be written as:

scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(
  Future(new JLinkedList[B](),executor))

without decalaring a new implicit variable d? Is there any advantage of doing this? For now I only find implicits increase the ambiguity of the code.

like image 574
CharlieQ Avatar asked Dec 11 '12 01:12

CharlieQ


1 Answers

I can give you 3 reasons.

1) It hides boilerplate code.

Lets sort some lists:

import math.Ordering

List(1, 2, 3).sorted(Ordering.Int) // Fine. I can tell compiler how to sort ints
List("a", "b", "c").sorted(Ordering.String) // .. and strings.
List(1 -> "a", 2 -> "b", 3 -> "c").sorted(Ordering.Tuple2(Ordering.Int, Ordering.String)) // Not so fine...

With implicit parameters:

List(1, 2, 3).sorted // Compiller knows how to sort ints
List(1 -> "a", 2 -> "b", 3 -> "c").sorted // ... and some other types

2) It alows you to create API with generic methods:

scala> (70 to 75).map{ _.toChar }
res0: scala.collection.immutable.IndexedSeq[Char] = Vector(F, G, H, I, J, K)

scala> (70 to 75).map{ _.toChar }(collection.breakOut): String // You can change default behaviour.
res1: String = FGHIJK

3) It allows you to focus on what really matters:

Future(new JLinkedList[B]())(executor) // meters: what to do - `new JLinkedList[B]()`. don't: how to do - `executor`

It's not so bad, but what if you need 2 futures:

val f1 = Future(1)(executor)
val f2 = Future(2)(executor) // You have to specify the same executor every time.

Implicit creates "context" for all actions:

implicit val d = executor // All `Future` in this scope will be created with this executor.
val f1 = Future(1)
val f2 = Future(2)

3.5) Implicit parameters allows type-level programming . See shapeless.

About "ambiguity of the code":

You don't have to use implicits, alternatively you can specify all parameters explicitly. It looks ugly sometimes (see sorted example), but you can do it.

If you can't find which implicit variables are used as parameters you can ask compiler:

>echo object Test { List( (1, "a") ).sorted } > test.scala
>scalac -Xprint:typer test.scala

You'll find math.this.Ordering.Tuple2[Int, java.lang.String](math.this.Ordering.Int, math.this.Ordering.String) in output.

like image 112
senia Avatar answered Nov 15 '22 05:11

senia