I would like to get comments on a certain point of API design for a package I am pondering over. Specifically, for an MWE, I have a generic function exposed by the API, the user defines custom types and how to “update” them with some change.
For some user-defined types, updating in place would make sense, while for others (fully immutable all the way down, eg containing StaticArray
s) one would make a new one. I want to design an interface that accommodates both.
Option 1: single function interface
The interface exposed by the package could look like this
"Subtypes define [`updatestate!`](@ref)."
abstract type AbstractState end
"""
state′ = updatestate!(state::AbstractState, Δ)
Return `state` updated with `Δ`, possibly (but not necessarily) modifying the
first argument. It is up to the implementation whether `state′` and `state`
share structure. Functions in this package guarantee never to use assume that
`state` is unmodified after a call.
"""
function updatestate! end
Then someone using the package defines a custom type, and implements the required interface:
# some implementation - defined by the user for a specific type
struct ConcreteState{T <: AbstractVector} <: AbstractState
x::T
end
# fallback, eg for StaticVector
updatestate!(state::ConcreteState, Δ) = ConcreteState(state.x .+ Δ)
# but Vector's we can modify
updatestate!(state::ConcreteState{<:Vector}, Δ) = (state.x .+= Δ; state)
Option 2: traits
Instead, user types could define a trait that describes whether they update in place (default: not). Depending on this, one would call updatestate
or updatestate!
.
Any other options? Comments? Examples I could look at?