-
Notifications
You must be signed in to change notification settings - Fork 833
Description
The description and tooltip of Seq.init are as follows (emphasis mine):
Generates a new sequence which, when iterated, will return successive elements by calling the given function, up to the given count. Each element is saved after its initialization. The function is passed the index of the item being generated.
This suggests that for any sequence, the initializer is called only once and that successive access would use the saved value. I was inspecting the code and couldn't find how this was done and it turns out, it isn't. Not really, at least.
Repro steps
Basically, just this:
> let mutable i = 0;;
val mutable i: int = 0
> let s = Seq.init 10 (fun x -> i <- i + 1; i);;
val s: seq<int>
> Seq.last s;;
val it: int = 10
> Seq.last s;;
val it: int = 20
> i;;
val it: int = 20Expected behavior
If the values from the generator were indeed saved, the initializer function would not be called again. However, the Seq.last returns different values, which means the mutable int I used gets updated again.
Actual behavior
The values aren't saved. At least not in the way I'd expect. They are saved momentarily for a call to Current, in such cases where you would use the IEnumerator<_>.Current directly. This saving itself is done in a Lazy<_> type to prevent problems if multiple threads read the value.
Known workarounds
Just ensure you don't write an expensive function as generator.
Related information
It's very well possible that this is "expected", and it is likely being used with side effects in practice. I'm bringing this up to discuss a better description for this function, as the way it is currently written, it confused me and possibly others.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status