I am aware of some related discussion (Have a function field in a structure, using another field in the structure and Alternative to function as field in struct), but the discussion there is insufficient for my case. Therefore, it is probably useful to start a separate thread here.
If, for example, I need to define a type AtomicOrbital as
struct AtomicOrbital
    n::Int64
    l::Int64
    m::Int64
    radial_function::Function
end
where the radial_function should be specified by the user to describe the radial part of the atomic orbital. The definition above is not completely satisfactory, since Function is an abstract type and such definition will hurt performance. However, storing the radial_function inside the struct seems to be the most reasonable way to organize data.
To eliminate the abstract type, the above type can be defined as
struct AtomicOrbital{T<:Function}
    n::Int64
    l::Int64
    m::Int64
    radial_function::T
end
However, I am not completely comfortable with the above definition. One reason is that this definition does not seem to be logical to me. r -> exp(-r) and r -> exp(-2r) lead to two different concrete types, which is logically weird. Another reason is that I am not sure whether this approach will put too much pressure on the Julia compiler.
Another way to improve the performance is to define an abstract type
abstract type AbstractAtomicOrbital end
function get_radial end
and ask the user to defined custom types as
struct MyAtomicOrbital <: AbstractAtomicOrbital
    n::Int64
    l::Int64
    m::Int64
end
function get_radial(orbital, r)
    return exp(-r)
end
However, with this approach, I partially lose control of how AtomicOrbital is defined. If, for some reason, I want to define
function transform_orbital(orbital::AbstractAtomicOrbital)
    # do something to define new orbital
    return new_orbital
end
to transform one atomic orbital into another (e.g., make the radial function decay quicker). I have no idea how  transform_orbital should be defined, since I don’t have information how MyAtomicOrbital is defined by the user.
So how do people generally handle this kind of situation, which I believe is not that rare in the real world?
Another related question: why Julia does not have something like Function{Float64,Float64} to specify the input and output of a function? It seems to solve the above issue.