meetings/working-groups/params-improvements/PI-2022-10-25.md
This was the first meeting of the params improvements working group.
We discussed extending params support to collection types other than arrays.
params IEnumerable<T>, IList<T>, etc.params List<T>, ImmutableArray<T>, etc.params Span<T>, ReadOnlySpan<T>params IEnumerable<T>, List<T>, etc.Perhaps the most common request is support for params IEnumerable<T>.
This is potentially useful for scenarios where the params method simply iterates over the collection, and where callers might have an IEnumerable<T> instance rather than a T[] and are calling the method with normal form.
However, when callers use expanded form, a params IEnumerable<T> may not provide any advantage over params T[] to the caller or callee.
If we extend params to support IEnumerable<T>, we should consider extending params support to other interface types and concrete collection types as well.
If so, what are the requirements of supported types?
The collection literals working group is already defining similar requirements for constructible collection types. Those types can be interface types or concrete class, struct, and ref struct types.
The collection literals proposal specifies how the corresponding collection instances for constructible collection types are constructed and populated.
Any additional params types should match the collection literals approach.
That is, for any particular params type, the code generated for WriteLine(fmt, a, b, c) should be equivalent to the code generated for WriteLine(fmt, [a, b, c]).
That said, the difference between an expanded form call (with implicit collection) and a call using a collection literal is just two characters, [], so the advantage of params over collection literals seems small.
Given that, we concluded we should not extend params to arbitrary interfaces or collection types.
params ReadOnlySpan<T>, Span<T>ReadOnlySpan<T> has a couple of advantages over other potential params types.
T[] and Span<T> are implicitly convertible to ReadOnlySpan<T>.That means params ReadOnlySpan<T> is a reasonable alternative to params T[] in new APIs.
And for existing APIs, adding a params ReadOnlySpan<T> overload could reduce allocations for current callers of a params T[] overload without changing the calling code.
The potential perf benefit, and the opportunity to replace params T[] with params ReadOnlySpan<T> seem sufficiently important that we should support params ReadOnlySpan<T> even if collection literals provide similar optimizations for span literals - that is, even if the difference between using params and using a collection literal is simply [].
Span<T> has the same benefits as ReadOnlySpan<T>, but Span<T> is only preferable to ReadOnlySpan<T> if the params method modifies the span.
But modifying a params span is not a pattern we should encourage - in part because modifications are not visible to callers where the compiler generated the Span<T> instance.
In short, we concluded we should support params ReadOnlySpan<T> but not params Span<T>.
params ReadOnlySpan<T> should be implicitly scoped so the compiler can reuse the implicitly generated ReadOnlySpan<T> instance across multiple calls in the calling method.
Should we also support params scoped T[], to allowing sharing the array across callers?
Overload resolution should prefer an overload with params ReadOnlySpan<T> over an overload with params T[], to ensure call sites get benefit from reduced allocations.
That means adding a params ReadOnlySpan<T> overload to an API with a params T[] overload is a source breaking change.
Extend params support to ReadOnlySpan<T> only.
params ReadOnlySpan<T> parameters should be implicitly scoped.
Overload resolution should prefer params ReadOnlySpan<T> over params T[].
Consider support for params scoped T[] as an open question.