ardalis / ardalis/Ardalis.Extensions

Make ForEach return an Iterator, like `Tap` in other languages

Open
#59 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
162
Forks
37
PR merge metrics
No merged PRs in 30d

Description

At the moment, our implementation of `ForEach` can only be used as the only or last step in a LINQ operations pipeline as it forces the execution of the chain of operations and returns void.

Just because `ForEach` allows us to perform side effects doesn't mean that it needs to take us out of the Iterator pipeline. I'm thinking of the `tap` operator that exists in many other languages and libraries as an example of allowing the "streaming" to continue.

https://rxjs.dev/api/operators/tap
https://hexdocs.pm/elixir/main/Kernel.html#tap/2

This would allow for things like this:

```cs
var blueWhalesWeight = whales
.Where(w => w.Color == "blue")
.ForEach(w => Console.WriteLine(w.Name))
.Select(w => w.Weight)
.ToList();
```

This would also mean that it is no longer possible to just do:

```cs
whales
.Select(w.Color == "blue")
.ForEach(w => Console.WriteLine(w.Name));
```

I guess at the end of the day it depends on our philosophy regarding Iterators. There is a reason why many languages choose to not implement a ForEach on their Iterator and that is because it doesn't fit very well with the abstraction. Iterators tend to encourage a more "functional" approach by creating a pipeline of operations where data comes in on one end, goes through a set of pure functions, and comes out at the other end transformed. The ForEach kind of breaks that abstraction in two ways:

1) the very intent of the method is to make side effects
2) it doesn't return anything

Languages have compromised with this by introducing the `tap` operation, which allows for side effects, but keeps the flow of data going.

So, I guess I'm proposing that we either 1) make `ForEach` into a `Tap` or, if we prefer to leave `ForEach` the way it is, that we introduce a `Tap` extension method.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by locating the existing ForEach extension and reviewing its return type and usages. Resolve whether the project should change ForEach to return the iterator or add a separate Tap method; done means the chosen API supports the shown chained pipeline while preserving the intended behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.