Provide overloads for IncrementalValue[s]Provider.Combine accepting more than 2 providers
- Dominant language
- C#
- Stars
- 20.7k
- Forks
- 4.3k
- PR merge metrics
- PR metrics pending
Description
## Background and Motivation
In .NET MAUI we're leveraging incremental source generators (https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md), define our providers as simple as they can, then `Combine()` a bunch of them at the last possible moment. As soon as you chain more than 2 Combine calls, you end up with a tuple of tuples (of tuples) return type: `(p1,p2), p3), p4)` which are hard to work with and make sense of as all the parts are either named Left or Right.
We often flatten the tuples for easier consumption:
```csharp
var sourceProvider = provider1
.Combine(provider2)
.Combine(provider3)
.Combine(provider4)
.Select(static (t, _) => (t.Left.Left.Left, t.Left.Left.Right, t.Left.Right, t.Right))
.WithTrackingName("provider5");
```
(and even while typing this, I'm unsure if this is correct. let's say it is)
That flattening syntax is atrocious, hard to read, hard to understand, hard to write, and hard to debug (nested tuples of Left and Right). I'm actually very afraid of modifying those lines, unless I have time, a piece of paper, and a pencil (why don't you use Copilot, Stephane ?).
The doc states "Combine is the most powerful, but also most complicated transformation" (https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md). Well, it shouldn't be complicated.
## Proposed API
To solve that, I wrote a few extensions methods that accepts more than 2 providers for combining, and I think they'd be beneficial to anyone writing an incremental source generator slightly more complex than the basic sample.
```diff
namespace Microsoft.CodeAnalysis
{
public static class IncrementalValueProviderExtensions
{
+ public static IncrementalValuesProvider<(TItem1, TItem2, TItem3)> Combine(this IncrementalValuesProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3);
+
+ public static IncrementalValuesProvider<(TItem1, TItem2, TItem3, TItem4)> Combine(this IncrementalValuesProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3, IncrementalValueProvider provider4);
+
+ //goes up to reasonable amount
+
+ public static IncrementalValueProvider<(TItem1, TItem2, TItem3)> Combine(this IncrementalValueProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3);
+
+ public static IncrementalValueProvider<(TItem1, TItem2, TItem3, TItem4)> Combine(this IncrementalValueProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3, IncrementalValueProvider provider4);
+
+ //goes up to reasonable amount
}
}
```
A 'reasonable amount' would be at least overloads taking up to 5 providers. to be aligned with the design choices of System.Tuple (https://learn.microsoft.com/en-us/dotnet/api/system.tuple-8?view=net-9.0) and ValueTuple it could go up to 8, being named provider1 to 7, and then a Rest)
## Usage Examples
The example given earlier now becomes:
```csharp
var sourceProvider = provider1.Combine(provider2, provider3, provider4)
.WithTrackingName("provider5");
```
and that tuple is easy to deconstruct in RegisterSourceOutput's action:
```csharp
var (item1, item2, item3, item4) = provider;
```
## Alternative Designs
/
## Risks
Those are a set of extensions methods, I have no idea what could go wrong....
Contributor guide
Assessment
This issue has not been assessed yet.