144

How does the following compile:

import scala.concurrent.duration._

val time = 20 seconds

What is actually going on here?

ripper234
  • 222,824
  • 274
  • 634
  • 905

2 Answers2

184

There are a few things going on.

First, Scala allows dots and parens to be omitted from many method calls, so 20 seconds is equivalent to 20.seconds()*.

Second, an "implicit conversion" is applied. Since 20 is an Int and Int has no seconds method, the compiler searches for an implicit conversion that takes an Int and returns something that does have a seconds method, with the search constrained by the scope of your method call.

You have imported DurationInt into your scope. Since DurationInt is an implicit class with an Int parameter, its constructor defines an implicit Int => DurationInt conversion. DurationInt has a seconds method, so it satisfies all the search criteria. Therefore, the compiler rewrites your call as new DurationInt(20).seconds**.

*I mean this loosely. 20.seconds() is actually invalid because the seconds method has no parameter list and therefore the parens must be omitted on the method call.

**Actually, this isn't quite true because DurationInt is a value class, so the compiler will avoid wrapping the integer if possible.

Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
  • 91
    Any sufficiently advanced technology is indistinguishable from magic. – ripper234 Feb 27 '13 at 05:32
  • 4
    Fortunately most IDEs are capable of distinguishing it! Implicit conversions get used quite a lot in Scala. If you're just reading the text file, it could be confusing ("where does that method come from") but with appropriate tool support you should be able to find your way around, at which point Scala can be beautifully meaningful and concise. (eg, 20.seconds is much more readable than `new DurationInt(20).seconds()` so long as you know how it does it) – William Billingsley Feb 27 '13 at 06:56
  • 1
    If you do find yourself using implicits, always ask yourself if there is a way to achieve the same thing without their help. http://twitter.github.com/effectivescala/#Types and Generics-Implicits – oluies Feb 27 '13 at 08:13
  • 5
    Actually the `seconds` method is defined without parens, so calling it with parens is an error. – Frank S. Thomas Feb 27 '13 at 19:30
  • 2
    @Frank That's a good point. I didn't mean to suggest that you can write `20.seconds()` in Scala, only that the compiler is translating the call that way. It's worth pointing out that Scala *requires* you to omit parens if the corresponding method has no parameter list, as in this case. – Aaron Novstrup Feb 27 '13 at 19:44
7

The "magic" that's going on there is called "implicit conversion". You are importing the implicit conversions, and some of them handle the conversion between Int (and Double) to Duration. That's what's you are dealing with.

Bruno Reis
  • 37,201
  • 11
  • 119
  • 156
  • 3
    Any idea why importing `import scala.concurrent.duration._` resolves `20 seconds` but actually importing the `DurationConversions` Trait doesn't? _EDIT_: Just realized what they're actually importing is `DurationInt`. I'm guessing this is because you can't import the actual Trait? Only a concrete implementation of the Trait? – franklin May 19 '18 at 04:38