Extension methods lets you add new methods to an existing type without having to sub-class it or recompile the original type. For example, you can add a square method to the Int class:

scala> 4.square
val res0: Int = 16

Even though we did not sub-class the Int class or recompile it we were still able to a method to it. Extension methods when used correctly can provide better APIs.

Extension methods in Scala 2

In Scala 2 extension methods were implemented using the implicit class approach:

object RichExtensions {
  implicit class RichInt(value: Int) {
    def square = value * value
  }
}

And by having the RichInt in scope we can do something like below:

scala> import RichExtensions._

scala> 4.square
val res0: Int = 16

Here is how this works, when the compiler does not find a method square for class Int it does not fail. It, instead, looks for an implicit class in scope which takes Int as it’s constructor parameter and has a method named sqaure. If such implicit class is found, in our case RichInt, the compiler converts the type to the implicit class.

Extension methods in Dotty (Scala 3)

In Dotty the syntax method is much more cleaner and has less boiler plate. Here is the same square method written in Dotty

object RichExtensions {
  def (value: Int) square: Int = value * value
}

And after having the square method in scope we use it as below:

scala> import RichExtensions._

scala> 4.square
val res0: Int = 16

Let’s try to understand the syntax for creating an extension method in Dotty.

  • We start the function as usual with def followed by the class for which we are creating the extension method. In our case
def (value: Int) ...`

We have also assinged a variable name value this is useful in accessing the variable inside the body.

  • Next is similar to any other function in Scala. We write the name of the method square, it can also accept arguments. And finally the return type.
def (value: Int) square: Int = ...
  • Finally the body of the function and this is also similar to normal functions. By giving our class a name we can access it easily in our function body. scala def (value: Int) square: Int = value * value

What if the class already has the method we are trying to extend

If the class already has a method we are trying to extend, our extension method won’t be called instead the method already defined will be used. To see this in action let’s define a + (add) method for Int.

object RichExtensions {
  def (value: Int) square: Int = value * value
  //also and example of extension method with parameters.
  def (a: Int) + (b: Int) = a * b //multiplying so that we know that our + method is being called
}
scala> import RichExtensions._

scala> 4 + 38
val res0: Int = 42

Well our method + wasn’t called and instead the + defined for Int was used. It would have been helpful it Dotty complained in such scenarios.

Conclusion

As can be seen, the extension methods in Dotty is much more concise. Extension methods are really helpful and because of them other concepts such typeclass are boilercode free and consice in Dotty.