I have a function f with a signature:
f(x, y, cache::Union{T1, T2})
where cache will be mutated (modified) when it’s of type T1 and not mutated when it’s of type T2. The type of cache is determined by the combinations of the types of x and y. Furthermore, the body (specific lines of code) of f does not change for different types of cache as f just passes cache to an inner function, fCore. So whether cache gets mutated is not “really” determined by f, but by cache itself.
Additionally, I would prefer not to split f into f1!(x, y, cache::T1) and f2(x, y, cache::T2) because f is used multiple times in a larger wrapper function h in which x and y do not compose a small finite number of type combinations. I think it’s too much of a hassle to manually write h with different versions of f in terms of f1! and f2 in its body, while the other parts are nearly identical.
So, after this somewhat convoluted introduction, my question is, should I redefine f’s signature to be something like f!(cache::Union{T1, T2}, x, y)?
It seems that there isn’t an apparent naming convention for a function when the mutability of an argument is directly dependent on itself rather than the function.
On the one hand, I can simply append ! to the end of the function’s name. Such a choice is under the assumption that as long as it CAN mutate the argument, it should be marked with !. On the other hand, we also have base functions like map, which does not have ! at the end but can indirectly mutate its arguments due to the lower-order function evaluated inside it, just like the relation between f and fCore in my case. To demonstrate what I meant by “indirectly”, here is an example (on Julia 1.11.2):
julia> unexpectedMutate! = x -> x .+= 1
#1 (generic function with 1 method)
julia> v = [[1], [2], [3]]
3-element Vector{Vector{Int64}}:
[1]
[2]
[3]
julia> map(unexpectedMutate!, v);
julia> v
3-element Vector{Vector{Int64}}:
[2]
[3]
[4]
If we think marking the argument name instead of the function name is a better solution for this type of edge case, I would love to see a special symbol dedicated to the arguments that will be (potentially) mutated, like how ! is used for a function. However, a naive solution of appending ! to the name of an optional argument currently (Julia 1.11.2) is an invalid syntax:
julia> foo1(a!=1) = (a!) + 1
ERROR: syntax: "(a != 1)" is not a valid function argument name around
Nevertheless, I’m interested in other people’s opinions on this edge case. Thank you!!