Selectors
Documenter.Selectors — ModuleAn extensible code selection interface.
The Selectors module provides an extensible way to write code that has to dispatch on different predicates without hardcoding the control flow into a single chain of if statements.
In the following example a selector for a simple condition is implemented and the generated selector code is described:
abstract type MySelector <: Selectors.AbstractSelector end
# The different cases we want to test.
abstract type One <: MySelector end
abstract type NotOne <: MySelector end
# The order in which to test the cases.
Selectors.order(::Type{One}) = 0.0
Selectors.order(::Type{NotOne}) = 1.0
# The predicate to test against.
Selectors.matcher(::Type{One}, x) = x === 1
Selectors.matcher(::Type{NotOne}, x) = x !== 1
# What to do when a test is successful.
Selectors.runner(::Type{One}, x) = println("found one")
Selectors.runner(::Type{NotOne}, x) = println("not found")
# Test our selector with some numbers.
for i in 0:5
Selectors.dispatch(MySelector, i)
endSelectors.dispatch(Selector, i) will behave equivalent to the following:
function dispatch(::Type{MySelector}, i::Int)
if matcher(One, i)
runner(One, i)
elseif matcher(NotOne, i)
runner(NotOne, i)
end
endand further to
function dispatch(::Type{MySelector}, i::Int)
if i === 1
println("found one")
elseif i !== 1
println("not found")
end
endThe module provides the following interface for creating selectors:
Documenter.Selectors.AbstractSelector — TypeRoot selector type. Each user-defined selector must subtype from this, i.e.
abstract type MySelector <: Selectors.AbstractSelector end
abstract type First <: MySelector end
abstract type Second <: MySelector endDocumenter.Selectors.disable — MethodDisable a particular case in a selector so that it is never used.
Selectors.disable(::Type{Debug}) = trueDocumenter.Selectors.dispatch — MethodCall Selectors.runner(T, args...) where T is a subtype of MySelector for which matcher(T, args...) is true.
Selectors.dispatch(MySelector, args...)Documenter.Selectors.leaf_subtypes — MethodReturn a list of all subtypes of T which do not have further subtypes.
The returned list includes subtypes of subtypes, and it does not distinguish between concrete types (i.e. types which are guaranteed not to have subtypes) and abstract types (which may or may not have subtypes).
Documenter.Selectors.matcher — FunctionDefine the matching test for each case in a selector, i.e.
Selectors.matcher(::Type{First}, x) = x == 1
Selectors.matcher(::Type{Second}, x) = trueNote that the return type must be Bool.
To match against multiple cases use the Selectors.strict function.
Documenter.Selectors.order — FunctionDefine the precedence of each case in a selector, i.e.
Selectors.order(::Type{First}) = 1.0
Selectors.order(::Type{Second}) = 2.0Note that the return type must be Float64. Defining multiple case types to have the same order will result in undefined behaviour.
Documenter.Selectors.runner — FunctionDefine the code that will run when a particular Selectors.matcher test returns true, i.e.
Selectors.runner(::Type{First}, x) = println("`x` is equal to `1`.")
Selectors.runner(::Type{Second}, x) = println("`x` is not equal to `1`.")Documenter.Selectors.strict — MethodDefine whether a selector case will "fallthrough" or not when successfully matched against. By default matching is strict and does not fallthrough to subsequent selector cases.
# Adding a debugging selector case.
abstract type Debug <: MySelector end
# Insert prior to all other cases.
Selectors.order(::Type{Debug}) = 0.0
# Fallthrough to the next case on success.
Selectors.strict(::Type{Debug}) = false
# We always match, regardless of the value of `x`.
Selectors.matcher(::Type{Debug}, x) = true
# Print some debugging info.
Selectors.runner(::Type{Debug}, x) = @show x