Design Q: Order of arguments: input data and algorithm type

Hey all,

(wasn’t sure in which category to post it)

We are doing a lot of refactoring across many JuliaDynamics packages at the moment. A question that always comes up in the refactoring concerns the order of arguments of typically 2-argument functions. They way we’ve set up most of our systems is to have a collection of types that decide what algorithm will be used to compute the “quantity” we care about. And we have the following set up:

abstract type Supertype end

struct Type1 <: Supertype end
struct Type2 <: Supertype end

quantity(x, t::Type1) = ...
quantity(x, t::Type2) = ...

Notice that the return type of quantity is concrete irrespecitively of Type. E.g., it will always be a Vector. The types decide what algorithm will be used to fill the values of the vector. A concrete example would be our probabilities(x, est::ProbabilitiesEstimator) which extracts probabilities from data using one of 15 different approaches.

My big design question is about the argument order of quantity: Which should be first? The input data x or the type deciding the algorithm t? The [Julia Style Guide]( Style Guide · The Julia Language) doesn’t resolve this because t doesn’t decide the output type. What is the majority of the community doing? We in JuliaDynamics use the quantity(x, t) signature, but we recently realized it doesn’t lent itself to generalizations of multiple inputs, such as quantity(x, y, t). (I.e., if multi-input is possible, quantity(t, args...) makes for an easier to learn interface).

4 Likes

I would tend to put the type first, for the multi-args reason among others. This also seems to be what Base does in methods such as convert(T, x) and reinterpret(type, A)

The first argument you provide is valid, but the second calling the the design in Base doesnt’ fit here, because this targets the case where the type decides the return type, as listed in the Julia style guide.

But actually Base follows the design of type first in the implementation of rand functions, where the RNG type is used first.

Irrespective of what order you come down on: I would probably have quantity(args…; estimator) as the only public method. That is, with a keyword argument, and leave the positional version as an internal implementation detail to exploit multiple dispatch. That’s what I do e.g. in QuantumPropagators.jl/propagate.jl at 84ab059936bec838e75c1857cbb146c30e22f78f · JuliaQuantumControl/QuantumPropagators.jl · GitHub

1 Like

That’s arguably only because rand(rng, ...) mutates rng, and should possibly be called rand!(rng, ...)