The most general signature an extension method can have is as follows:
leftTyParams
leadingUsing
leftParam
(in an explicit term clause)trailingUsing
def
keyword)rightTyParams
rightParam
(in an explicit term clause)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)
[U]
(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.
This expansion also introduces some inconsistencies when calling the extension methods in non infix form. The user needs to invert the order of the arguments at call site manually. For instance:
extension [T](x: T)
def *:(xs: List[T]): List[T] = ...
y.*:(ys) // error when following the parameter definition order
ys.*:(y)
*:(y)(ys) // error when following the parameter definition order
*:(ys)(y)
Another limitation of this representation is that it is impossible to pass the type parameters of the def
explicitly, (unless called in prefix form). For instance:
extension (x: Int)
def *:[T](xs: List[T]): List[T] = ...
xs.*:[Int](1) // error when trying to set T explicitly
The expansion of right-associative extension methods also affects the order in which contextual parameters can be passed explicitly.
Group extension can also behave unintuitively, in general all extension in a group are extension on the receiver. Except if one of these extensions is a right-associative extension method, in which case that one is an extension on the type of its argument. For instance:
extension (a: Int)
def :+(b: Long): Long = ... // extension on Int
def +:(b: Long): Long = ... // extension on Long
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4