Timeline for Is this method pure?
Current License: CC BY-SA 3.0
17 events
| when toggle format | what | by | license | comment | |
|---|---|---|---|---|---|
| Feb 22, 2021 at 4:15 | comment | added | jmoreno | There’s no reason to assume that GetEnumerator is a pure function, which means that the method may not be pure. For instance, the first could call result in an iterator that was first in last out, and a subsequent call could be last in first out, ordered by a particular property, change which property is being used to order the results, etc. More commonly, there’s no guarantee that a subsequent call is allowed, an IEnumerable may thrown on successive calls to GetEnumerator. | |
| Apr 12, 2017 at 7:31 | history | edited | CommunityBot | replaced http://programmers.stackexchange.com/ with https://softwareengineering.stackexchange.com/ | |
| Aug 21, 2014 at 13:08 | comment | added | Eric Lippert | @jk: Indeed -- that is the thrust of my final paragraph. Arguing about whether this method is pure or not misses the point; don't write this method in the first place. It creates a sequence which produces both side effects and values every time it is enumerated, and that's unexpected. | |
| Aug 21, 2014 at 6:17 | comment | added | jk. | it is pure, in a very similar way to how IO in haskell is pure, its a pure function that produces a sequence of action aka an imperative program. the difference here is that in haskell the sequence of actions can only be executed by the runtime, where as here you can execute it as many times as you like. So Eric is right it is a pure function, but also a unsafe function and should therefore be avoided | |
| Aug 20, 2014 at 22:17 | comment | added | Eric Lippert | @ThomasEding: Again, the questions that should be asked here regarding purity are questions like: can I safely memoize this method? and can I safely call this method on multiple threads? and so on. Clearly you can call Apply safely on multiple threads, clearly you can memoize the result; it's a pure function. Apply(...).GetEnumerator().MoveNext() is not a pure function; it mutates the enumerator. | |
| Aug 20, 2014 at 22:13 | comment | added | Eric Lippert | @ThomasEding: You are missing something; that's not how iterators work. The ApplyIterator method returns immediately. No code in the body of ApplyIterator is run until the first call to MoveNext on the returned object's enumerator. Now that you know that, you can deduce the answer to this puzzle: blogs.msdn.com/b/ericlippert/archive/2007/09/05/… The answer is here: blogs.msdn.com/b/ericlippert/archive/2007/09/06/… | |
| Aug 20, 2014 at 21:31 | comment | added | Thomas Eding | @EricLippert: I agree with your simplified example in the above comment. But unless I'm missing something, ApplyIterator calls action before it has a chance to yield, assuming the collection is non-empty. And since Apply calls ApplyIterator, that single call to action will be performed. | |
| Aug 20, 2014 at 20:10 | comment | added | Eric Lippert | @ThomasEding: The method doesn't call action, so the purity of action is irrelevant. I know it looks like it calls action, but this method is a syntactic sugar for two methods, one which returns an enumerator, and one which is the MoveNext of the enumerator. The former is clearly pure, and the latter clearly is not. Look at it this way: would you say that IEnumerable ApplyIterator(whatever) { return new MyIterator(whatever); } is pure? Because that's the function that this really is. | |
| Aug 20, 2014 at 19:28 | comment | added | Thomas Eding | -1: I cannot believe you claim the function is pure. If action modifies item (it can), it's end of story. Now if you are able to restrict action to be pure, then you're onto something. (I'm no C# expert, but I don't believe its type system is powerful enough to denote such a restriction.) | |
| Aug 20, 2014 at 18:18 | history | edited | gnat | CC BY-SA 3.0 | links to referred posts |
| Jun 13, 2014 at 5:34 | comment | added | Eric Lippert | @robertharvey no, they are low level implementation details. Whether a method is pure from my perspective has nothing whatsoever to do with the psychological feelings that developers have about a method. Its entirely about what optimizations a compiler or runtime is allowed to make. Can it memoize the method? Can it execute it lazily? Can the method be split into little pieces and parallelized to multiple threads? And so on. | |
| Jun 12, 2014 at 23:41 | comment | added | Robert Harvey | This answer muddies the waters more than it illuminates. While everything you say is unquestionably true, the principles of immutability and purity are high-level programming language principles, not low-level implementation details. Programmers working at a functional level are interested in how their code behaves at the functional level, not whether or not its inner workings are pure. They're almost certainly not pure under the hood if you go low enough. We all generally run these things on Von Neumann architecture, which is most certainly not pure. | |
| Jun 6, 2014 at 18:48 | comment | added | Eric Lippert | @ThomasLevesque: My advice is to never ever do that. A query should answer a question, not mutate a sequence; that's why they're called queries. Mutating the sequence as it is queried is extraordinarily dangerous. Consider for example what happens if such a query is then subjected to multiple calls to Any() over time; the action will be performed again and again, but only on the first item! A sequence should be a sequence of values; if you want a sequence of actions then make an IEnumerable<Action>. | |
| Jun 6, 2014 at 18:07 | comment | added | Thomas Levesque | I guess this method falls in the same bag as the ForEach extension method, which is intentionally not part of Linq because its goal is to produce side effects... | |
| Jun 6, 2014 at 18:05 | comment | added | Thomas Levesque | Thanks Eric! I followed roughly the same reasoning; I do think the method is pure (even though I accepted an answer that said otherwise), but I didn't apply the attribute, because it would be misleading. I also considered not writing this method at all, but sometimes I need to mutate the items "on the fly", before applying more operators to the sequence, e.g. items.Apply(something).Where(...).GroupBy(...).... I could use a loop instead, but it would be less convenient. | |
| Jun 6, 2014 at 16:31 | history | edited | Eric Lippert | CC BY-SA 3.0 | added 172 characters in body |
| Jun 6, 2014 at 16:26 | history | answered | Eric Lippert | CC BY-SA 3.0 |