Time and time again, I’ve wanted to be able to write code like a op b op c op d
, and had to resort to something like
extension [T <: Tuple](l1: List[T])
@targetName("zip2")
infix def zip1[U](l2: List[U]) = (l1 zip l2).map( (e1, e2) => e1 :* e2)
extension [T](l1: List[T])
infix def zip1[U](l2: List[U]) = l1 zip l2
(It turns out lazyZip
already does did, but its implementation is similarly frightening)
My (still early) idea is to add “Infix Varargs”, a construct where the signature of zip1
above would look something like:
infix def zip1[Ts](ls: List[T]*): List[Ts]
//or
extension [Ts](ls: List[T]*)
infix def zip1: List[Ts]
As you can see, 1) I don’t yet have a definitive syntax and 2) for polymorphism to be really useful, we need some sort of type vararg (I used Ts
and T
here)
Nevertheless, once we do have a syntax, we can cleanly define:
- lazyZip
- an operator
~
for making tuples - a python-like
<
:0 < x < 10
* - a c-style
<<
for string concatenation (not that we need one) - an optimized (multi) matrix multiplication, which chooses the order of multiplications to minimize computation
*More restricted than the python one: Would not allow for 0 <= x < 10
, 0 < x > y
or 0 < x == y in l
This is not only good for library developers, also for library users, as the intent will be much clearer
The semantics would be as follows:
In case we have a call of the form
a op b op c op d
, an infix vararg op
would be applicable if and only if
op(a,b,c,d)
typechecks
We do not check for op(a,b,c)
, the run is always either fully infix vararg, or fully not
To this end, if a op b
is a non infix-vararg call, no infix vararg method will be applicable on the rest of the run
Overloading resolution works as usual (using the above applicability criterion), with the following special case:
If both an infix vararg and a normal method are applicable, it is considered ambiguous, even if one is more specific than the other
We might want to disallow or further discourage defining infix vararg and normal methods with the same name
To allow library maintainers to change their binary methods into infix vararg methods, while maintaining binary/tasty compatibility, we should generate a specialized method for the two argument case, either always or if requested
Infix varargs method may not have names ending in :
, as they are inherently neither left- nor right- associative
Feedback:
- Would a construct like that be useful to you ? (Examples welcome)
- Does the spec above make sense for a construct like this (too restrictive, not enough, different spec fits better, etc)
- Is this feature worthwhile? (Given it can already be implemented, just more messily)
Syntax at the definition site is extremely important, but as there is not a clear proposal yet, I encourage you to focus on the other aspects for now