In my opinion, I am still fresh to Julia, it would be ontological mismatch to juxtapose “required_action” with “perform”, because “required_action_to_take” is semantically different from “performing_required_action_to_take”.
So if we have perform_a(b) and perform(b, a), the latter of which looks like your kind of generalization, then perform_a signals that _a is an invariant and will not be used for dispatch at all.
My understanding of Julia so far is that this is useful to make internal, unexported functions more readable for humans: “read_file(path)” or “parse_from_left(text)”.
When a has state that’s important as in your “required_action_to_take”, then I see the value of your style. But to me it would look like over-engineering to make everything a type for dispatch, e.g. parse(text, :left). Note that the snake casing has just moved to the “object name” because we would probably have parse(text, :from_left).
If your style degenerates to something like
perform(args…, method)
It starts looking a bit like single dispatch to me, where method is the object owning the function.
That was just an example. I tried to convey the message that it is always possible to take a long function name and rethink a design where the args and kwargs become fields of a Method struct in CamelCase. The number of verbs is reduced to a more intuitive/clean API in my personal experience.
Of course people are free to mix code styles as they wish. I am more picky with that because of the number of features provided by the packages I maintain. It would be a nightmare to memorize hundreds of function names that are not explicitly connected in a type hierarchy of methods.
To add a note, the usage of !-postfix in function names is sometimes unintuitive. Julia suggests using !s only when the function itself modifies its arguments. However, what if the function is a function factory that returns another function? For instance, Fix{N} does not have !, but it allows mutating functions to be passed to it. In the case of anonymous usage of such functions, i.e. without storing the resulting function in a global const, it could be misleading.
I don’t see the issue here? If you don’t assign the anonymous function to a variable, your original ! function is still directly visible. If you do assign it to a variable (possibly local or non-const), you can just add a ! to the name.
julia> f!(x, s) = x .+= s; x = rand(2)
2-element Vector{Float64}:
0.8772536942598435
0.34042814275432287
julia> Base.Fix{2}(f!, 1)(x); x # Mutates, as there's an !
2-element Vector{Float64}:
1.8772536942598435
1.3404281427543228
julia> g! = Base.Fix{2}(f!, 1);
julia> g!(x); x # g! clearly mutates
2-element Vector{Float64}:
2.8772536942598435
2.3404281427543228