Edit this page on GitHub

Right-Associative Extension Methods: Details

The most general signature an extension method can have is as follows:

  • An optional type clause leftTyParams
  • A possibly empty list of using clauses leadingUsing
  • A single parameter leftParam (in an explicit term clause)
  • A possibly empty list of using clauses trailingUsing
  • A name (preceded by the def keyword)
  • An optional type clause rightTyParams
  • An optional single parameter rightParam (in an explicit term clause)
  • Any number of any clauses rest

For example:

extension [T]                               // <-- leftTyParams
            (using a: A, b: B)(using c: C)    // <-- leadingUsing
            (x: X)                            // <-- leftParam
            (using d: D)                      // <-- trailingUsing
    def +:: [U]                               // <-- rightTyParams
            (y: Y)                            // <-- rightParam
            (using e: E)(z: Z)                // <-- rest

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 explicit term parameter (in other words, rightParam is present). 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 the inverse parameter swap. More precisely, if rightParam is present, the total parameter sequence of the extension method's expansion is:

    leftTyParams  leadingUsing  rightTyParams  rightParam  leftParam  trailingUsing  rest

In other words, we swap leftParams trailingUsing with rightTyParam rightParam.

For instance, the +:: method above would become

<extension> def +:: [T]
                      (using a: A, b: B)(using c: C)
                      (y: Y)
                      (x: X)
                      (using d: D)
                      (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.