I have packages `A`

, `B`

, `C`

, where both `B`

and `C`

depend on `A`

. In order to make some function `f`

available to both `B`

and `C`

at the same time, I first define it in `A`

and then overload it in `B`

and `C`

with some local methods, e.g.

```
module A
function f end
export f
end
module B
using A
...
A.f(x::SomeTypeInB) = ...
end
module C
using A
...
A.f(x::SomeTypeInC) = ...
end
```

So that I can then write `using A, B, C`

and use `f`

, dispatching without issues.

Can I use a similar approach to define recipe functions (using RecipesBase) common to both `B`

and `C`

? Iâ€™ve tried without success.

##
Additional info

For reference, atm I define recipes via

```
@userplot COOLPLOT
@recipe function f(h::COOLPLOT)
if h.args[1] isa SomeTypeInPackage
...
else
error("Got $(typeof(h.args[1])) instead of SomeTypeInPackage.")
end
end
```

which exports `coolplot`

and `coolplot!`

automatically.

You can just define a `user recipe`

Recipes Â· Plots on the Union of your types (or their abstract supertype if they have one).

Although I want these recipes to share the same name, the actual codes for plotting `SomeTypeInB`

and `SomeTypeInC`

are quite different. I need to be able to differentiate between these types inside `B`

and `C`

respectively.

Do they need to have a particular name? Maybe can you be a litte more concrete about how the plots should look and what the types are?

Iâ€™d like to define a common recipe `convergence`

(and `convergence!`

) that shows the standard converge plot (error vs iteration) of a fixed-point problem using some custom algorithms defined in `B`

and `C`

.

In other words, I have `SolutionUsingAlgorithmB`

and `SolutionUsingAlgorithmC`

(which in practice are two different `struct`

s of common supertype `<: AbstractSolution`

, where `AbstractSolution`

is defined in `A`

), defined in each respective package.

I would like to define `convergence(solution::SolutionUsingAlgorithmB)`

and `convergence(solution::SolutionUsingAlgorithmC)`

using `RecipesBase`

alone. In practice, each method of `convergence`

must be different (because `SolutionUsingAlgorithmB`

and `SolutionUsingAlgorithmC`

are very different), so I cannot simply make use of `convergence(solution::AbstractSolution)`

.

Ah, I see. I think. Itâ€™s a little tricky because Plots already uses dispatch to have the recipe names work.

The easiest is probably to have a function inside the recipe that generates the data to be plotted from your solution, and have that function dispatch on the type of solution. So that the recipe itself only sets up what is visible to the user plotting (which should be the same).

2 Likes

Yes, that would do it! However, using this approach, how do I go about passing different Plotsâ€™ stylistic arguments, e.g. `markersize --> 4`

, from such a function that lives inside the recipe to the recipe itself?

EDIT: Looking at this beautiful daschwâ€™s post, I realised that it is simply a matter of `push`

ing to `plotattributes`

as follows:

```
module A
...
function _convergence end
@userplot CONVERGENCE
@recipe f(h::CONVERGENCE) = _convergence(h.args[1], plotattributes)
end
module B
using A
...
function A._convergence(solution::SolutionUsingAlgorithmB, plotattributes)
push!(plotattributes, :framestyle => :box, :gridalpha => 0.2, ...)
...
return solution.plottablefield
end
```

and similarly for `C`

.

For future reference, based on @mkborregaardâ€™s comment, I found a better solution that involves wrapping `SolutionUsingAlgorithmB`

and `SolutionUsingAlgorithmC`

inside a constructor `_Convergence`

defined in `A`

:

```
module A
...
struct _Convergence{solution_T}
solution::solution_T
end
@userplot CONVERGENCE
@recipe function f(h::CONVERGENCE)
return _Convergence(h.args[1])
end
end
module B
using A
...
@recipe function f(wrappedobject::NSDEBase._Convergence{<:SolutionUsingAlgorithmB})
solution = wrappedobject.solution
...
end
end
```

and similarly for `C`

. This solution has the non-trivial advantage of allowing for the usage of the usual `RecipesBase`

tricks (for loops of `@series`

, etc) inside the relevant recipes defined in `B`

and `C`

, which otherwise couldnâ€™t be used (that easily) with the previous approach based on `_convergence`

.

My idea was just something along the lines of

```
using RecipesBase
create_xy(x::A_type) = ...; x, y
create_xy(x::B_type) = ...; x, y
@userplot CoolPlot
@recipe function f(h::CoolPlot)
markersize --> 4
seriestype := :scatter
create_xy(h.args[1])
end
```