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 = 20
Expected 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.
The description and tooltip of
Seq.initare as follows (emphasis mine):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:
Expected behavior
If the values from the generator were indeed saved, the
initializerfunction would not be called again. However, theSeq.lastreturns 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 theIEnumerator<_>.Currentdirectly. This saving itself is done in aLazy<_>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.