# Current behavior of fill and the like..?

`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.

``````julia> a = fill([1 2; 3 4], 3)
3-element Vector{Matrix{Int64}}:
[1 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]

julia> a[1][1] = 123;

julia> a
3-element Vector{Matrix{Int64}}:
[123 2; 3 4]
[123 2; 3 4]
[123 2; 3 4]
``````

`repeat`, albeit a different function with a different goal, can also have the same behavior.

``````julia> b = repeat([[1 2; 3 4]], 3)
3-element Vector{Matrix{Int64}}:
[1 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]

julia> b[1][1] = 123;

julia> b
3-element Vector{Matrix{Int64}}:
[123 2; 3 4]
[123 2; 3 4]
[123 2; 3 4]
``````

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?

``````julia> a = map(copy, fill([1 2; 3 4], 3))
3-element Vector{Matrix{Int64}}:
[1 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]

julia> a[1][1] = 123
123

julia> a
3-element Vector{Matrix{Int64}}:
[123 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]
``````

Yes, one can use `map` of course, but it is not better than a comprehension anyway.

``````julia> a = [[1 2; 3 4] for i=1:3]

3-element Vector{Matrix{Int64}}:
[1 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]
julia> a[1][1] = 123;

julia> a
3-element Vector{Matrix{Int64}}:
[123 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]
``````

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.

4 Likes

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.

2 Likes

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.

`repmat` doesnât make arrays of arrays, it just repeats the values of the input array into a larger array:

``````>> repmat(rand(2,3), 2, 2)
ans =
0.2785    0.9575    0.1576    0.2785    0.9575    0.1576
0.5469    0.9649    0.9706    0.5469    0.9649    0.9706
0.2785    0.9575    0.1576    0.2785    0.9575    0.1576
0.5469    0.9649    0.9706    0.5469    0.9649    0.9706
``````

Iâm actually not aware of any functionality like `fill` in Matlab. In fact, you cannot even have arrays of arrays, except with the special âcell arrayâ type.

The way Matlab arrays work arenât really comparable to Juliaâs arrays, I think.

`repmat` is similar to Juliaâs `repeat` function, though:

``````julia> repeat(rand(2,3), 2,2)
4Ă6 Matrix{Float64}:
0.135393  0.416019  0.238718  0.135393  0.416019  0.238718
0.765527  0.546136  0.629576  0.765527  0.546136  0.629576
0.135393  0.416019  0.238718  0.135393  0.416019  0.238718
0.765527  0.546136  0.629576  0.765527  0.546136  0.629576
``````

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):

``````expand([1 2; 3 4], 3)
3-element Vector{Matrix{Int64}}:
[1 2; 3 4]
[1 2; 3 4]
[1 2; 3 4]
``````

Another difficulty is that the argument is parsed in the scope of the caller. Thus, what

``````fill(rand(2), 10)
``````

Should return? Any alternative would be confusing (the current behavior is confusing but consistent noneless)

Maybe there should be a `fillcopy` function?

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.

3 Likes

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.
• Add `@fill`.

At least a result of the above linked Github issue is that the documentation for `fill` will be improved in Julia version 1.8.

I donât follow. Bring back what functionality?

As far as I know, Matlabâs `repmat` functionality exists in Juliaâs `repeat`. I donât think there is any parallel to what you are looking for in Matlab, nor in previous Julia functions.

Thanks for pointing to the related issue. From a fast scim through the discussion there, it seems providing a `@fill` maco that means `[<expr> for _ = 1:n]` might be a viable solution.

1 Like

I meant re-using the name `repmat` since it carries the postfix `mat` to now work with arrays, similar to what was suggested by @CameronBieganekâs 3 options above (`fillf`).

Yes, similar to this.

But after I scimmed through that issue I tend to agree on a macro `@fill`.

Hereâs a link to the recommendation to not use `deepcopy` (aside from interactive use):

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.