fill is very useful for creating arrays having the same vaule at all locations. However, if this vaule is a mutable object, fill will place that very same object at all locations. This behavior can be confusing, especially for beginners, when one tries to mutate a single location, it will silently modify all locations similarly.
IMO, this makes fill and repeat less useful in practice. Also, I’m not sure why Julia doesn’t copy the value at all locations as MATLAB’s repmat does? Of course, for immutable structures this issue doesn’t exist. I know one can use array comprehensions to create such arrays but it’s not as intuitive/compact. Any ideas?
This is a fundamental difference between Julia and Matlab. Matlab copies everything silently under the hood (function arguments, = assignments, etc.). Julia never does this, so fill is just consistent with the way everything else in Julia works. Changing fill to automatically call copy (or should it be deepcopy?) would make it totally inconsistent with the rest of Julia, and I think that’s a bad thing.
On the other hand, I agree that this is a very common trap for new users. It’s so common that I wonder if it would be better to rename fill to something else for Julia 2.0 as a way to gently discourage new Julia users from expecting it to behave just like it does in Matlab. It’s a useful function in Julia, but it’s not useful for the thing that most Matlab users seem to expect it to be useful for.
Adding a keyword argument like fill(x, dims; copy = false) would be semver-compliant for inclusion in a v1.x release so long as it doesn’t change default behavior, right? And putting the default behavior right in the function signature might do a better job of warning new users than expecting them to read the full docstring.
Why it should? Do you want it to check if the value is mutable and take this decision for the programmer? The short answer is that it is confusing to many programmers (Java behaves the same way, as it is just how it is expected to behave when you have the correct memory model in mind) and it would be terrible for performance if done silently.
I admit that the current behavior of not copying is consistent with Julia’s design; arrays are not copied by default. But as you said, this is a common trap for many new users, and there should be a way to prevent this misunderstanding. On the same time, the functionality of expanding a vector into given dims is very useful in practice, see how many questions in this discourse about making the same mistake of referring to the same object at all locations.
Historically, repmat was superceded by repeat in 2018 so that now repeat works for both strings and arrays besides scalars. I think now we should have a means to prevent usage of fill with arrays and at the same time provide a convenience method that works for 2D and nD arrays. Something similar to this would be very useful (maybe repmat back or expand or any more expressive name):
Sure, MATLAB doesn’t even have array of arrays, but since Julia has that, it makes sense to have that functionality to work for array of arrays in Julia. repmat was deprecated by Jeff in 2018 in favor of repeat, we might think of bringing it back for filling with arrays or choose a better name for a new method, say expand, multicopy, copydims, etc.
Having a copy = false keyword argument is not ideal, because if you set copy = true you’ll still have the same problem with arrays of arrays of arrays. So what you really want is a deepcopy. But I believe I’ve heard it said that deepcopy in Julia is not very well defined, or shouldn’t exist, or something like that. Can’t find a link now.
So the only viable options are:
Use map or a comprehension.
Add fillf which takes a function as the first argument.
At least a result of the above linked Github issue is that the documentation for fill will be improved in Julia version 1.8.
Not really agree with Jeff on this one, but I think I understand the perspective he is coming from. In package code, you really rarely will really want to generically deep copy something. However, I do not see why this hinders better naming of the functions (the PR’s goal). I mean, this seems like an excessively “training wheels”/“protecting the programmers from themselves” take that is uncommon to Julia design: to avoid giving a function a better name just because it may make them discover a slower function that is not what they need 90% of the time.