MoreLINQ
MoreLINQ copied to clipboard
Rename Pipe to ViaAction/Cause/Visit
The Pipe
operator sounds too general and vague. Any set of LINQ operations chained together could be seen as a pipeline or pipe formation/composition. Pipe
also does not imply any side effect. Consequently, consider renaming Pipe
to ViaAction
. ViaAction
clearly states two things: (1) the operator is pass-through, as in via, and (2) it implies an action, which for the majority case is understood to be a side-effect.
Originally reported on Google Code with ID 10
Reported by @atifaziz on 2009-02-17 21:05:47
Should you choose to accept, a patch is attached to make this painless. :)
Reported by @atifaziz on 2009-02-17 21:09:39
- Attachment: ViaAction.patch
I think Pipe
is ok. The purpose of a pipe is to pass something through to what comes after, so I think it is just as expressive as "via". It's not perfect, as it evokes the Unix idea of a "pipeline", which is technically very similar to what most of the LINQ operators already do: allow function chaining.
But if we are going to consider alternatives, I'm fond of Visit
, because it evokes the Visitor Pattern: http://en.wikipedia.org/wiki/Visitor_pattern
Reported by @cammerman on 2009-02-17 21:14:08
The Unix idea of a pipeline is exactly what I was thinking of, but I'm not convinced by any of the names suggested so far. Keep thinking, everyone :)
Reported by @jskeet on 2009-02-17 21:27:45
How about Cause
? Cause
implies action and effect. It also reads nice:
"foo bar".Split().Cause(Console.WriteLine).Consume();
Reported by @atifaziz on 2009-02-17 22:28:06
At a glance I would expect Cause
to be synonymous to ForEach
. I think it's crucial that the pass-through behavior of this method be reflected in the name.
Reported by @cammerman on 2009-02-17 22:34:13
ForEach
is well-understood to be a terminal. There should no confusion in anyone's mind what ForEach
does otherwise it means we have introduced an element of surprise.
I agree, though, that Cause
may not imply continuation but we should strive to have only two terminals: ForEach
and Exhaust
/Consume
. This way, we can be relaxed about how many behaviors does a single word embody. And come to think of it, ForEach
is Cause
followed by Exhaust
/Consume
. 😉
I've updated the issue summary to reflect the current options.
Reported by @atifaziz on 2009-02-17 23:21:47
Speaking of Exhaust
, should we have that as a "normal" method? I've currently just got it as a test helper, but it might not be a bad thing to include for completeness.
Another option: Observe
- I get to take an action when I see something, but not interfere with the fact that it's being passed along the pipe.
Reported by @jskeet on 2009-02-17 23:25:47
Speaking of Exhaust, should we have that as a "normal" method?
Yep, posted as Issue #11.
Reported by @atifaziz on 2009-02-17 23:43:36
Observe sounds interesting until you consider the "action" part. If you consider the signature in pseudo code, it reads odd:
Observe(source, action)
Likewise, it just doesn't read right when put in usage:
"foo bar".Split().Observe(Console.WriteLine).Consume();
Although you could say the Console
is "observing" each string as it passes through, the WriteLine
bit disturbs or feels extra. It has a whole different feel when you change Observe
to Notify
and use Console
like this:
"foo bar".Split().Notify(Console).Consume();
This reads natural and implies an imperative action but it doesn't get us anywhere.
Reported by @atifaziz on 2009-02-17 23:57:47
Why not call it Tee
in reminiscence of the UNIX command that IMHO most closely fits this action? May be too obscure though. An alternative might be Fork
although this somehow seems to imply that two enumerables are returned.
Reported by konrad.rudolph
on 2009-02-18 09:39:49
I did wonder about Tee
. That's more likely to be reasonable in Push LINQ though, where it may mean something slightly different (if it actually turns out to be needed; multicast events probably make it pointless).
Keep the ideas coming though... I'm going to blog on the difficulty of naming quite soon.
Reported by @jskeet on 2009-02-18 09:44:35
Fork
although this somehow seems to imply that two enumerables are returned.
And adding to the frustration, Python does with Tee
what you mean here with Fork
: http://docs.python.org/3.0/library/itertools.html#itertools.tee
For someone who works in both camps, it can be frustrating to have one tee causing side-effects while the other not.
I would reserve Fork
for parallelism and Tee
for how Python does it. It would be a shame to loose both names to side-effects when they don't necessarily have to imply it.
Reported by @atifaziz on 2009-02-18 10:05:25
Any judgment/criticism/etc. of my Visit
suggestion? Another possibility may be Apply
, as in "apply this method to each element".
Reported by @cammerman on 2009-02-19 16:05:41
Would Process
not be a valid name for Pipe
? It implies that a transformation occurs and that there is an output.
Reported by jeffmagic
on 2009-03-10 21:54:45
'Scuse me for butting in out of the blue, but how about just SideEffect
instead of Pipe
, because that is clearly what it is made for. In this sense, Trace
is just a special case of SideEffect
. I wouldn't have categorised ForEach
as SideEffects
, because it implicitly consumes the IEnumerable
, which is hardly a side effect, in my book.
Reported by ben.simkins
on 2009-03-25 11:40:30
PS (and afterwards I'll shut up), Pipe
looks like this to me (nothing to do with Linq):
//pipe an action or function behind a function result
public static void Pipe<T>(this T val, Action<T> action) where T : class
{
if (val != null) action(val);
}
public static R Pipe<T, R>(this T val, Func<T, R> func)
where T : class
where R : class
{
return val != null ? func(val) : null;
}
(CF: http://stackoverflow.com/questions/336775/pipe-forwards-in-c)
Reported by ben.simkins
on 2009-03-26 06:37:16
I think ForEach
would have been good, and its side effect behavior would be consistent with ToList().ForEach()
. But then the name would not imply that it returns the transform, which List<T>.ForEach()
does not. Confusion ensues.
I worry slightly about the user experience here. Nowhere else in the LINQ space is the concept of lazy evaluation explicit. I don't think the majority of LINQ users knows about it. Therefore, terms like ForceSelect
, or likewise, will confuse people.
I don't like Pipe
really, speaking from process automation, because a pipe is a passive thing. You PUT one thing in one end and this causes something to come out the other end. In that regard, maybe it should be PUMP, but then it feels like we're losing connection with out math/academic roots. Losing our monads, so to speak.
Reported by tormod.steinsholt
on 2012-06-16 11:56:54
I'd be really worried about any MoreLINQ users who didn't understand lazy evaluation already, to be honest.
How about we make it really, really explicit what this operator is for? WithSideEffect
:
var query = input.Where(...).WithSideEffect(Console.WriteLine);
The With
suggests it's still returning something, and it's clear that the purpose is to add a side-effect to the pipeline.
Reported by @jskeet on 2012-06-16 16:50:02
The eager execution nature of the operator, which is the motivation behind it, would still not be explicit. Select
also has side effects.
I may be (very) colored from my work in process automation industry, but ideally it should be something suggesting that it is an active element (actor) in the pipeline. WithSideEffects
suggests that something extra (side effects) happens when stuff is pulled through it.
Reported by tormod.steinsholt
on 2012-06-17 09:12:33
Perhaps we can settle on this one with Do
as Interactive Extensions (Ix) does? FWIW, at least it will provide some consistency (at the risk of issue #60 expanding for projects using MoreLinq and Ix).
Reported by @atifaziz on 2012-06-17 09:53:49
@tormod
: The Pipe
operator doesn't execute eagerly. It is lazy - the motivation isn't to make it execute eagerly, it's to add a side-effect. Your statement of:
WithSideEffects
suggests that something extra (side effects) happens when stuff is pulled through it.
... is exactly correct. That's what the operator does. Look at the implementation!
Reported by @jskeet on 2012-06-17 11:19:16
@atifaziz: I would suggest that projects using both MoreLINQ and Ix are going to have a lot of duplication already. Personally I wouldn't want to use the two together -I expect it would cause more harm than good.
"Do" feels very woolly to me, and suggests more eagerness. I'm still keen on WithSideEffects
- especially as Tormod understood exactly what that meant even though he expected something else. That suggests it's a good name, IMO :)
Reported by @jskeet on 2012-06-17 11:21:07
Oh, I am sorry. I have now looked at the code and I agree that "Do" seems to be the correct choice. I am not saying that this is necessarily what we would be arriving at in this discussion, but this is certainly what people will be looking for, and will immediately understand, after they have started to use Rx (or Ix). To answer my own question. The thing that sets it apart from Select
is that it accepts an action as argument and returns the element (not a transformation of the element).
Reported by tormod.steinsholt
on 2012-06-26 10:34:32
Is this project still alive? If so, and feedback is still appreciated, can I suggest something along the lines of OnEnumerate
, WhenEnumerated
, or if you like it really verbose WhenEnumeratedPerform
? IMO it makes it clear that this is lazy (i.e. performed when it is enumerated) and the signature makes it clear it is a side effect action, and is chainable.
Reported by MartijnHoekstra
on 2013-11-18 12:14:23
What about DoYield
?
var avg = new[] {1, 2, 3}.DoYield(x => Console.WriteLine(x)).Average()
@JamesFaix I think it would be reasonable/sensible at this stage to borrow Do
from Rx here and move on.