Repository navigation
Documentation for modify confusion #371
Description
Activity
Documentation was originally added in a612079 by @rleshchinskiy
@infinity0 Without digging much into the exact proof why this works I will speculate destructive operation can be performed in place thanks to fusion, which is the only way to detect if it is safe to mutate immutable vector or not without relying on linear types.
Here is a rewrite rule that will let us modify a vector without copy, which depends on how it was constructed and if it is being used multiple times or not:
Lines 2259 to 2260 in 7ee4634
"clone/new [Vector]" forall p. clone (new p) = p So if a vector was constructed with a function that relies on
newthen it should be mutated in place without copy.Whether this works or not as promised at all times or at all can only be confirmed with testing, core inspection and investigating rule firings. Not sure if anything along those lines have been done for
modifyfunction.With all this in mind I do agree that documentation is a bit confusing and could use some improvement.
If I understand correctly, the fusion rules only express a subset of what safe situations are right? Namely, if the input Vector was itself just created by a call to
new. However this won't always be the case, especially if the user is using this API with no knowledge of the fusion-specific aspects of Vector - there are lots of other, more common, ways of creating a Vector (e.g. from List) without having used thenewfunction.So the documentation should be made clear regarding that - "Due to fusion RULES, the operation will be performed in place in a subset of the cases where it is safe to do so, namely if its input is statically known to the compiler to have been created with
new."There are plenty of rewrite rules that will change a pure function that you are using into a call to
new, so you don't have to manually callnewyourself in order to hit this optimization. For example the function you mentionedfromListwill in fact be converted to a call tonew:fromList xs = new (New.unstream (Bundle.fromList xs))
Documenting this will not make things more clear, because it is an optimization. Note that if you compile with
-O0then destructive operation will never be performed in place because non of the rewrite rules will fire.I don't think you should take it too seriously and think of this as an optimization that compiler might or might not do it for you. So, documenting that would be good in my opinion: "There is an optimization, which in some case will do a destructive modification safely in place without extra copy"
Yes fair enough, I didn't look into the details of how
fromListis implemented; however I think my wording was fairly precise, "statically known to the compiler to have been created withnew" - which covers the cases e.g. if the compiler doesn't know statically if it was created withfromListetc.I can understanding your point that documenting this detail about
newis too specific however, and simply telling the reader "in some cases [etc]" I agree would be sufficient to communicate the situation clearly (and would solve this issue).- added 3 commits that reference this issue
on Sep 2, 2023 Fixed by #466
I'm a bit confused about the documentation for
modifyin both Data.Vector and Data.Vector.Generic:It's unclear to me how we can possibly detect it is "safe" to perform the operation in-place given that, due to the type signature, the caller is free to pass the input again to another function. Indeed the implementation is just
modify p = new . New.modify p . clonewhich looks like it does an unconditional copy.