Monday, April 26, 2010

Implicit Parameters

Evidently the topic of implicit parameters has not yet been correctly addressed. There have been several topic that refer to implicit parameters but none that directly discuss them. So before I continue with the topic of implicit parameter resolution I will discuss implicit parameters.

First, implicit parameters are not the same as implicit object conversions. Implicit parameters provide a way to allow parameters of a method to be "found". This is similar to default parameters at a glance but in fact is a different mechanism for finding the "default" value. It differs from implicit object conversion in that it is only a way for parameters for a method to be resolved. Implicit object conversion allows methods to appear to be called on one object when in fact that object is being converted behind the scenes to another type. (more or less)

An implicit parameter is a parameter to method or constructor that is marked as implicit. This means that if a parameter value is not supplied then the compiler will search for an "implicit" value defined within scope (according to resolution rules.) Implicit parameter resolution rules will be discussed soon.

Example:
  1. scala> def p(implicit i:Int) = print(i)
  2. p: (implicit i: Int)Unit
  3. // defining a val/var/def as implicit 
  4. // means that it will be considered during implicit resolution
  5. scala> implicit val v=2
  6. v: Int = 2
  7. // scope is searched for a implicit value to sue
  8. // v is found as marked implicit
  9. scala> p               
  10. 2
  11. // explicit declarations always overrides implicit values
  12. scala> p(1)
  13. 1

Implicit parameters are very nice for simplifying APIs. For example the collections use implicit parameters to supply CanBuildFrom objects for many of the collection methods. This is because normally the user does not need to be concerned with those parameters. Another example is supplying an encoding to an IO library so the encoding is defined once (perhaps in a package object) and all methods can use the same encoding without having to define it for every method call.

One important restriction is that there can only be a single implicit keyword per method. It must be at the start of a parameter list (which also makes all values of that parameter list be implicit). I further understand that only the last parameter list may be implicit.

Here are several illegal examples:
  1. // implicit is not in last parameter list
  2. scala> def pp(implicit i:Int, a:Int)(b:Int) = println(a,i)                 
  3. < console>:1: error: '=' expected but '(' found.
  4.        def pp(implicit i:Int, a:Int)(b:Int) = println(a,i)
  5. // there are 2 implicit parameters
  6. scala> def pp(implicit j:Int, a:Int)(implicit i:Int,b:Int) = println(a,i)
  7. < console>:1: error: '=' expected but '(' found.
  8.       def pp(implicit j:Int, a:Int)(implicit i:Int,b:Int) = println(a,i)
  9. // implicit is not the first parameter of the parameter list
  10. scala> def pp(a:Int, implicit i:Int) = println(i,j)         
  11. < console>:1: error: identifier expected but 'implicit' found.
  12.        def pp(a:Int, implicit i:Int) = println(i,j)
  13.                      ^

Here are several legal examples (Updated with useage examples):
  1. scala> implicit def v = 7
  2. v: Int
  3. scala> implicit var x = 10L
  4. x: Long
  5. // i is implicit
  6. scala> def pp(a:Int)(implicit i:Int) = println(a,i)
  7. pp: (a: Int)(implicit i: Int)Unit
  8. scala> pp(3)
  9. (3,7)
  10. // both i and b are implicit
  11. scala> def pp(a:Int)(implicit i:Int, b:Long) = println(a,i,b) 
  12. pp: (a: Int)(implicit i: Int,implicit b: Long)Unit
  13. scala> pp(4)               
  14. (4,7,10)
  15. // both i and b are implicit
  16. scala> def pp(implicit i:Int, b:Long) = println(i,b)  
  17. pp: (implicit i: Int,implicit b: Long)Unit
  18. scala> pp
  19. (7,10)
  20. // all or none of the parameters must be supplied
  21. scala> pp(2)
  22. < console>:13: error: not enough arguments for method pp: (implicit i: Int,implicit b: Long)Unit.
  23. Unspecified value parameter b.
  24.        pp(2)
  25. // This is syntactically legal but I cannot seem to implicitly invoke this
  26. // I would recommend: def pp(b:Long*)(implicit i:Int) = println(i,b)
  27. scala> def pp(implicit i:Int, b:Long*) = println(i,b)
  28. pp: (implicit i: Int,implicit b: Long*)Unit
  29. scala> pp(3,1,2,3)
  30. (3,WrappedArray(1, 2, 3))
  31. scala> def pp(b:Long*)(implicit i:Int) = println(i,b)
  32. pp: (b: Long*)(implicit i: Int)Unit
  33. scala> pp(1,2,3)
  34. (7,WrappedArray(1, 2, 3))

A related topic is Companion Object implicits.

11 comments:

  1. Thanks for this (as for all your posts). What does def pp(a:Int)(implicit i:Int, b:Long) = println(a,i,b) define, and how would you use it? Are both i and b implicit, or just i? Thanks

    ReplyDelete
  2. Excellent questions. I have updated the post with answers (I hope :) )

    ReplyDelete
  3. Excellent post, as usual! You stated that "One important restriction is that there can only be a single implicit parameter per method" but then show several examples that actually have several implicit parameters. I think it might technically be more accurate to note that the implicit keyword applies to the entire last argument list, however long it is, instead of just to a single parameter.

    ReplyDelete
  4. As you can see. I occasionally/often am figuring out the rules as I write the post. In this case I figured out what was going on thanks to future examples but forgot to correct that statement.

    I have updated the post with more accurate information

    ReplyDelete
  5. Thanks nice code comments

    ReplyDelete
  6. Thank u. You explained the concept very well.

    ReplyDelete
  7. Thanks, great explanation, very well written.

    ReplyDelete
  8. Hi...I've notice than play framework uses implicit parameters inside the actions...why is the reason about this..I can't understand (although I can understand perfectly all your examples) why this:

    Action { implicit request =>
    Ok("Got request [" + request + "]")
    }

    is better than

    Action { implicit request =>
    Ok("Got request [" + request + "]")
    }

    I've other little doubt...with the request key always I define exactly the type...if this is an implicit parameter I write my implicit parameter for "x" type...if it's a implicit method..I write the method for a specific "x" type...but whit "implicit request" I'm not defining what type is request...sorry if it is a really noob question but I've readed almost every article about implicit and I can understand yet what's mean this code...


    thank!!

    ReplyDelete
    Replies
    1. sorry the comparative were between
      Action { implicit request =>
      Ok("Got request [" + request + "]")
      }

      and this (without implicit):
      Action { request =>
      Ok("Got request [" + request + "]")
      }

      Delete
  9. All the scala implicit stuff are good for *code writter* but it is painful for *code reader* and *maintainer*.

    While...decades of history tells us, the later is MUCH MORE important than the former.

    That is why scala will never go prime, and will diminish just like the once almighty PERL do.

    it is not a language developed for large scale industry software development, it is a toy language used by academic purpose.

    ReplyDelete
    Replies
    1. It is funny, I had the same comment the first time someone installed java (probably pre 1.0) on my computer and made me play with it..... it was just a toy and not going anywhere..... years later all my development was done in java / j2ee for 15 years now..... 1st or 2nd most popular language...... now termed an "end of life" or "legacy" language.... Scala is what Java should have been and more..... but then, it could always be better.

      Delete