Can a function know if it called with a "!" in the name?

I’m writing a specialized plotting function (let’s call it A(), say) that does quite a lot of logic, and then calls plot() to generate a new plot. Is there a way that I can make a paired function (let’s call it A!(), say) without repeating all the logic in the second version? The only difference would be that A() would call plot() and A!() would call plot!(), with the same logic before the plot call and the same arguments in the plot call.

I’m guessing that this sort of thing comes up quite a bit. Is the best practice to put the logic that goes before the plot calls into a new function and have both A() and A!() call that?

Many thanks for any advice.

Dan Kelley

Hi dankelley, I think so.

B() = begin
	A()
	plot()
end

B!() = begin
	A()
	plot!()
end

If you can’t separate the logic that come before the call to plot you can use a macro instead (just saying, it’s not the best solutions)

const PLOT_FUNC = (:plot, :plot!)
const FUNCS = (:A, :A!)

for i in Base.OneTo(2)
    func = FUNCS[i]
    plot = PLOT_FUNC[i]
    eval(quote
        function $func(args...)
            # function body
            $plot(args...)
        end
     end )
end

It’s just a way to achieve what you want, just a proposition

Thanks. I’ll go with this, since I’m too much of a newbie to use macros, as another helpful person suggested.

Thanks. I’ll bookmark this for future reference.

In addition to factoring out the common code to a separate function, like @karei suggested, another common solution would be, to include an additional argument (possibly with default value) should_mutate::Bool=false and branch only for the call that differs.

Of course you could also pass the function (plot or plot!) as argument, but this might be overkill for two choices.

1 Like

I’d like to supplement a code example for the answer above.

“Manual dispatch”:

function A(a,b;should_mutate::Bool=false)
	...
	if should_mutate
		plot!()
	else
		plot()
	end
end
1 Like

Can you define the mutating method first and define the non-mutating one based on it? This is a very common pattern for mutating. Something like:

function A!(p, arg1, arg2) 
  #logic logic
  return(plot!(p, etc))
end

function A(arg1, arg2)
  p = plot(somearguments) #maybe create the axis or something initial
  A!(p, arg1, arg2)
end
4 Likes

Yes of course. In fact this is a very common pattern.
E.g. in LinearAlgebra.jl you’ll find a lot code like

function eigen!(matrix)
    # implementation
end

function eigen(matrix)
    return eigen!(copy(matrix))
end
7 Likes

But then you’re not following the convention that mutating functions ought to have a name ending with `!`

True, in fact it’s more geared towards two different functions in general.