An introduction to implicit arguments

SBinary and Scalacheck are part of a small set of libraries that make extensive use of implicit arguments in a style very reminiscient of Haskell type classes. I’m hoping this style of programming will get more common in Scala – it’s a really useful technique and, in my completely unbiased opinion, both SBinary and Scalacheck are fantastic and you absolutely should use them. :-) But in order to do so you need to really understand how implicit arguments in Scala work.

This post is actually for work, as we’re using Scala there and this is a subject which has been confusing one of my colleagues.

As a starting point, in Scala you can declare a method to have multiple argument lists. This isn’t a fantastically useful feature, but here’s how it works:

scala> def foo(x : Int)(y : Int)
     | = x + y
foo: (Int)(Int)Int

scala> foo(1)(2);
res1: Int = 3

scala> foo(1, 2);
:6: error: wrong number of arguments for method foo: (Int)(Int)Int
       foo(1, 2);
       ^

scala> foo(1)
:6: error: missing arguments for method foo in object $iw;
follow this method with `_' if you want to treat it as a partially applied funct
ion
       foo(1)
       ^

i.e. “exactly the same as a single method parameter list but you have to use a different syntax for calling it”. Hurray.

This has one advantage though. You can declare the last parameter list of a function to be implicit. The syntax for this works as follows:

scala> def speakImplicitly (implicit greeting : String) = println(greeting)
speakImplicitly: (implicit String)Unit

scala> speakImplicitly("Goodbye world")
Goodbye world

scala> speakImplicitly
:6: error: no implicit argument matching parameter type String was foud.

scala> implicit val hello = "Hello world"
hello: java.lang.String = Hello world

scala> speakImplicitly
Hello world

So, we can call this as normal but, additionally, we can leave out the implicit argument list and the compiler will look for a value in the enclosing scope which has been marked as implicit. If we try to do that and there is no such value in scope then the compiler will complain.

Matching implicit arguments

Implicits are totally typesafe, and are selected based on the static type of the arguments. Here are some examples to show how things work.

Implicits of the wrong type
scala> def speakImplicitly (implicit greeting : String) = println(greeting)
speakImplicitly: (implicit String)Unit

scala> implicit val aUnit = ();
aUnit: Unit = ()

scala> speakImplicitly
:7: error: no implicit argument matching parameter type String was found.

Only an implicit of type String will be selected for an implicit argument of type String.

Implicits of the wrong static type
scala> def speakImplicitly (implicit greeting : String) = println(greeting)
speakImplicitly: (implicit String)Unit

scala> implicit val hello : Any = "Hello world"
hello: Any = Hello world

scala> speakImplicitly
:7: error: no implicit argument matching parameter type String was found.

Implicit selection happens on the *static* type of variables. It’s no use having something of the right dynamic type if the variable isn’t typed accordingly.

scala> def speakImplicitly (implicit greeting : String) = println(greeting)
speakImplicitly: (implicit String)Unit

scala> implicit val foo = "foo";
foo: java.lang.String = foo

scala> implicit val bar = "bar";
bar: java.lang.String = bar

scala> speakImplicitly
:9: error: ambiguous implicit values:
 both value bar in object $iw of type => java.lang.String
 and value foo in object $iw of type => java.lang.String
 match expected type String

If there are multiple implicit arguments of the same type, it will fail as it has no way of choosing between them. But…

Implicit arguments of subtypes
scala> def sayThings (implicit args : List[Any]) = args.foreach(println(_))
sayThings: (implicit List[Any])Unit

scala> implicit val nothingNiceToSay : List[Any] = Nil
nothingNiceToSay: List[Any] = List()

scala> sayThings

scala> implicit val hellos : List[String] = List("Hello world");
hellos: List[String] = List(Hello world)

scala> sayThings
Hello world

If you have an implicit argument of a subtype, it will also match as an implicit argument of this type. Moreover, if you have two implicit arguments which match and one is a subtype of the other, the more specific type will match.

Parameterized implicits
scala> def implicitly[T](implicit t : T) = t
implicitly: [T](implicit T)T

scala> implicit val foo = "foo"
foo: java.lang.String = foo

scala> implicit val aUnit = ()
aUnit: Unit = ()

scala> implicitly[String]
res2: String = foo

scala> implicitly[Unit]

Type parameters can quite happily take part in the implicits mechanism.

Defining implicit arguments

So, we know how to use defined implicit arguments now. But how can we define them? We’ve seen one way:

implicit val foo = "foo";

scala> implicitly[String]
res2: String = foo

If this was all we could do then it wouldn’t be that powerful a feature. A nice to have, but ultimately not *that* exciting. Fortunately there are a few more things we can do. For starters, Scala has the uniform access principle, so any (wait, no. That would be too general. We can’t have features without special cases. Sigh. Ok, let’s say most) things you can do with a val you can do with a def

implicit def foo = "foo"

scala> implicitly[String]
res2: String = foo

This def will be invoked each time we want the implicit. Here’s an example to demonstrate this

scala> implicit def aUnit : Unit = println("Hello world")
aUnit: Unit

scala> implicitly[Unit]
Hello world

scala> implicitly[Unit]
Hello world

scala> implicitly[Unit]
Hello world

In general, implicit defs shouldn’t have side effects. It can lead to some really counterintuitive behaviour. This is just for demonstration purposes.

Now, the ability to use defs opens up a bunch of possibilities. For example, they can have type parameters:

scala> implicit def emptyList[T] : List[T] = Nil;
emptyList: [T]List[T]

scala> implicitly[List[String]]
res9: List[String] = List(Hello world)
// Oops, we still had an implicit List[String] left over from an earlier example. Note how that was used in preference to the parameterized version. Let's try again.

scala> implicitly[List[Int]]
res10: List[Int] = List()

Moreover, implicit defs used in this way can themselves have implicit parameters. For example:

scala> case class Foo[T](t : T);
defined class Foo

scala> implicit val hello = "Hello"
hello: java.lang.String = Hello

scala> implicit def foo[T](implicit t : T) = Foo[T](t)
foo: [T](T)Foo[T]

scala> implicitly[Foo[String]]
res3: Foo[String] = Foo(Hello)

(Note: I originally tried to write this example with Option. It turns out there’s a bug with how covariant types are handled which made it not work)

The basic idea is that anything marked as implicit which you could write as a single identifier (possibly with a type signature to handhold the type inference system) is valid to be passed as an implicit argument.

More reading

This should provide enough to get you started. Your next step should probably be to check out the documentation for Scalacheck and SBinary (the latter of which is… less than stellar at the moment. I’ll fix that, I promise. :-)). If you’re looking for some slightly more hardcore reading, Generics of a Higher Kind has some interesting examples. Other than that, the best thing to do is play with some code.

This entry was posted in programming and tagged , , on by .

10 thoughts on “An introduction to implicit arguments

  1. Michael Roger

    Has this changed recently?
    Your example behaves differently in my 2.6.0-final:

    wednet% scala
    Welcome to Scala version 2.6.0-final.

    scala> def sayThings (implicit args : List[Any]) = args.foreach(println(_))
    sayThings: (implicit List[Any])Unit

    scala> implicit val nothingNiceToSay : List[Any] = Nil
    nothingNiceToSay: List[Any] = List()

    scala> sayThings

    scala> implicit val hellos : List[String] = List(“Hello world”);
    hellos: List[String] = List(Hello world)

    scala> sayThings
    <console>:11: error: ambiguous implicit values:
    both value hellos in object $iw of type => List[String]
    and value nothingNiceToSay in object $iw of type => List[Any]
    match expected type List[Any]
    val res2 = sayThings

    (My scala 2.6.0 implementation behaves the way I expect: I don’t want scala arbitrarily choosing between a subtype value and a supertype value when both are in scope.)

  2. David R. MacIver

    Yes, it has. The implicits spec was incorrectly implemented prior to 2.6.1.

    And regardless of what you want it to do, the new behaviour is both correct and useful. It’s also not arbitrary. :-)

  3. Michael Roger

    (Ignoring for a moment the unpleasantness of a feature being broken in one of its basic use cases through version 2.6.0-final :-( )

    What is the rationale that justifies this decision that, for example, when matching an implicit Any,
    a:String and a2:Int are ambiguous, but a1:String and a3:Any are not?

    There is no connection between the values a1 and a3, except that the type of a1 happens to be a subtype of a3. Why should that fact be demonstrative in the search for an unambiguous match for a value of type a3?

    To turn the question around in an example, suppose I have this:

    implicit val obj:Any = new MyFoo();
    def serialize(implicit arg:Obj) = …

    and then I add this:

    def heartbeat(implicit arg:String) = println _

    implicit val str:String = “still alive”

    Adding the greeting() function silently changes the behavior of serialize(), even though there is no compiler warning or obvious clue in the source code as to why. That behavior contravenes the static type-safe component system one expects from Scala.

    The Scala Reference mentions the rule but doesn’t explain it.

  4. David R. MacIver

    This isn’t actually one of the most basic use cases. Using implicit arguments like this has been historically regarded as quite subtle, and the specified behaviour is *really hard* to read. :-/ I’m just trying to convince people that it’s easy and useful. :-)

    The short answer to why your particular suggested issue is not really a problem is that providing implicit values of particularly generic types like String and Any is kindof a bad idea. :-) Even without this subtyping behaviour there’s too much danger of unrelated things accidentally clobbering eachother. Generally when one uses these one defines a paramterised type specific to the use case and uses implicit arguments of that type.

    The reason this behaviour is useful is that often what you want to do is something like provide a definition for serializing arrays, but provide a special case for serializing boolean arrays (because you end up inflating then by a factor of 8 as you only need one bit per boolean but end up using a byte in the default setup). So you define an implicit Binary[Array[T]] but also provide an implicit Binary[Array[Boolean]]. If there wasn’t some sort of resolution on subtype there would be no way to get this behaviour.

  5. Michael Roger

    Fair enough.

    Throwing around implicit Anys, Strings, and Ints is certainly ill-advised; they were just the easiest examples I could think up to write.

    The bit that worries me is that one reason I like statically-typed languages is that it’s hard to accidentally type something wrong that compiles but misbehaves. Implicits in general make it easier for my mistakes to typecheck :-) One just must be judicious in using implicits, and use discipline in organizing them.

    I guess it’s fair to say that a programmer should always review all implicits in scope whenever any implicit is added, deleted, or modified. (I haven’t written enough Scala to know whether that means scanning the one enclosing source file or chasing down arbitrary ancestors of inheritance)

    A compile flag “+Wnontrivial-implicit-resolution” would be a useful debugging tool, methinks.

    I should read the sbinary code to see how it uses the implicit parameters and subtyping in a real use case.

  6. uncial

    Aren’t you being a bit hard on multiple argument lists? (“Hooray.”) They also provide support for currying:

    def salesTax(ratePct : Double)(amount : Long) =
    (amount * ratePct / 100.0D + 0.5D).toLong

    def salesTaxGotham = salesTax(11.0D)_
    def salesTaxOceanCity = salesTax( 8.5D)_
    def salesTaxHomeTown = salesTax( 6.0D)_

    def netCharge(taxer : (Long => Long), amts : Long*) = {
    val subtotal = amts.foldRight(0L)(_ + _)
    subtotal + taxer(subtotal)
    }

    println(“Gotham: ” + netCharge(salesTaxGotham, 10, 20, 30, 40))
    println(“OceanCity: ” + netCharge(salesTaxOceanCity, 10, 20, 30, 40))
    println(“HomeTown: ” + netCharge(salesTaxHomeTown, 10, 20, 30, 40))

    which produces

    Gotham: 111
    OceanCity: 109
    HomeTown: 106

    so that a more general function (e.g. salesTax) can be used to construct more specialized functions by providing some, but not all, of the parameters.

  7. David R. MacIver

    The thing about Scala’s currying support is that it only works when the type inferencer is feeling smart enough to infer the result of the curried function type.

    Given how poor the Scala type inferencer is, I tend to assume that anything which depends on it working properly doesn’t actually work. :-)

    You can explicitly provide the result type, but this is not actually shorter than just using the cut notation.

  8. Pingback: Best of drmaciver.com | David R. MacIver

  9. Pingback: This language has more than one dimension | Windows Live space

Comments are closed.