Forward properties: private API
Utility functions
PortfolioOptimisers.forward_nonnothing — Function
forward_nonnothing(v, _::Type{T}, pathstr, nodestr) -> Any
Guard one intermediate node of a @forward_properties nested path.
Return v unchanged when it is not nothing; otherwise throw a PropertyPathError naming the receiver type T, the full declared path pathstr, and the nodestr node that resolved to nothing. Called once per intermediate hop in the descent generated for a depth-≥2 locator.
Algorithm
visnothing: throw aPropertyPathErrorwhose message namespathstr, the typeTand the nodenodestr.vis anything else: returnv.
The guard runs on the intermediate hops only, so a path whose last hop gives nothing returns that nothing rather than raising. That is deliberate: an absent leaf is a value, and an absent intermediate is a path that cannot be walked.
Related
PortfolioOptimisers.forward_flatten_path — Function
forward_flatten_path(expr) -> Vector{Symbol}
Flatten a @forward_properties locator into its path of field symbols.
A bare identifier a becomes [:a]; a dotted expression a.b.c becomes [:a, :b, :c]. Any other expression raises an error.
Algorithm
expris aSymbol: return the one-element vector holding it.expris anExprwith head:.and two arguments:- Read
leaf, its second argument, and unwrap aQuoteNodeto its value. leafis not aSymbol: raise an error naming the leaf.- Call this function again on the first argument, and append
leafto the result.
- Read
expris anything else: raise an error naming the expression.
The recursion of step 2.3 is what makes the path any depth: a.b.c parses as (a.b).c, so the walk descends to the bare name and rebuilds the path from the left.
Related
PortfolioOptimisers.forward_walk_expr — Function
forward_walk_expr(
path,
struct_name,
broadcast::Bool
) -> Expr
Build the expression that descends a @forward_properties path (a vector of field symbols) on the receiver x, returning the value at the path.
A depth-1 path is a single getfield. A depth-≥2 path descends hop by hop, guarding every intermediate with forward_nonnothing (keyed on the receiver type struct_name) so a nothing node throws a path-naming PropertyPathError. When broadcast is true, the final hop maps over the penultimate value if it is an AbstractVector (the scalar-or-vector solution case), otherwise it is a plain access.
Algorithm
- The path holds one name: return
getfield(x, name)and stop.getfieldis used rather thangetproperty, so the generatedBase.getpropertynever re-enters itself. - Build
pathstr, the whole path joined by dots, for the error message. - Start
stmtswith__v = getfield(x, first_name). - For each further hop
kof the path:- Push
__v = forward_nonnothing(__v, struct_name, pathstr, nodestr), wherenodestrnames the part of the path walked so far. kis the last hop andbroadcastistrue: push an assignment that reads the leaf withgetproperty.when__vis anAbstractVector, and withgetpropertyotherwise.- Otherwise: push
__v = getproperty(__v, leaf).
- Push
- Push
__vas the value of the block. - Return the statements wrapped in a
letblock, so__vnever escapes into the caller.
Step 4.1 runs before every hop after the first, so the guard covers each intermediate exactly once and never the leaf. Only the leaf of step 4.2 broadcasts, so an intermediate vector is still a path error rather than a silent map.
Related