I have the following extension method:
public static IEnumerable<T> Apply<T>(
[NotNull] this IEnumerable<T> source,
[NotNull] Action<T> action)
where T : class
{
source.CheckArgumentNull("source");
action.CheckArgumentNull("action");
return source.ApplyIterator(action);
}
private static IEnumerable<T> ApplyIterator<T>(this IEnumerable<T> source, Action<T> action)
where T : class
{
foreach (var item in source)
{
action(item);
yield return item;
}
}
It just applies an action to each item of the sequence before returning it.
I was wondering if I should apply the Pure
attribute (from Resharper annotations) to this method, and I can see arguments for and against it.
Pros:
- strictly speaking, it is pure; just calling it on a sequence doesn't alter the sequence (it returns a new sequence) or make any observable state change
- calling it without using the result is clearly a mistake, since it has no effect unless the sequence is enumerated, so I'd like Resharper to warn me if I do that.
Cons:
- even though the
Apply
method itself is pure, enumerating the resulting sequence will make observable state changes (which is the point of the method). For instance,items.Apply(i => i.Count++)
will change the values of the items every time it's enumerated. So applying the Pure attribute is probably misleading…
What do you think? Should I apply the attribute or not?
Best Answer
No it is not pure, because it has side effect. Concretely it is calling
action
on each item. Also, it is not threadsafe.The major property of pure functions is that it can be called any number of times and it never does anything else than return same value. Which is not your case. Also, being pure means you don't use anything else than the input parameters. This means it can be called from any thread at any time and not cause any unexpected behavior. Again, that is not case of your function.
Also, you might be mistaken on one thing: function purity is not question of pros or cons. Even single doubt, that it can have side effect, is enough to make it not pure.
Eric Lippert raises a good point. I'm going to use http://msdn.microsoft.com/en-us/library/dd264808(v=vs.110).aspx as part of my counter-argument. Especially line
Lets say we create method like this:
First, this assumes that
GetEnumerator
is pure too (I can't really find any source on that). If it is, then according to above rule, we can annotate this method with [Pure], because it only modifies instance that was created within the body itself. After that we can compose this and theApplyIterator
, which should result in pure function, right?No. This composition is not pure, even when both
Count
andApplyIterator
are pure. But I might be building this argument on wrong premise. I think that the idea that instances created within the method are exempt from the purity rule is either wrong or at least not specific enough.