The most general form of leading parameters of an extension method is as follows:
- A possibly empty list of using clauses
leadingUsing
- A single parameter
extensionParam
- A possibly empty list of using clauses
trailingUsing
This is then followed by def
, the method name, and possibly further parameters otherParams
. An example is:
extension (using a: A, b: B)(using c: C) // <-- leadingUsing
(x: X) // <-- extensionParam
(using d: D) // <-- trailingUsing
def +:: (y: Y)(using e: E)(z: Z) // <-- otherParams
An extension method is treated as a right-associative operator (as in SLS §6.12.3) if it has a name ending in :
and is immediately followed by a single parameter. In the example above, that parameter is (y: Y)
.
The Scala compiler pre-processes a right-associative infix operation such as x +: xs
to xs.+:(x)
if x
is a pure expression or a call-by-name parameter and to val y = x; xs.+:(y)
otherwise. This is necessary since a regular right-associative infix method is defined in the class of its right operand. To make up for this swap, the expansion of right-associative extension methods performs an analogous parameter swap. More precisely, if otherParams
consists of a single parameter rightParam
followed by remaining
, the total parameter sequence of the extension method’s expansion is:
leadingUsing rightParam trailingUsing extensionParam remaining
For instance, the +::
method above would become
<extension> def +:: (using a: A, b: B)(using c: C)
(y: Y)
(using d: D)
(x: X)
(using e: E)(z: Z)
This expansion has to be kept in mind when writing right-associative extension methods with inter-parameter dependencies.
An overall simpler design could be obtained if right-associative operators could only be defined as extension methods, and would be disallowed as normal methods. In that case neither arguments nor parameters would have to be swapped. Future versions of Scala should strive to achieve this simplification.