Skip to content

Commit 651bcf8

Browse files
docs(datamodel): add docstrings for macros and utilities
1 parent f265dae commit 651bcf8

1 file changed

Lines changed: 126 additions & 61 deletions

File tree

src/datamodel/macros.jl

Lines changed: 126 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,49 @@
1+
"""
2+
$(TYPEDSIGNATURES)
3+
4+
Determines the promoted numeric element type for convenience constructors of component `C`. The promotion is computed across the values of `coercive_fields(C)`, extracted from the normalized `NamedTuple` `ntv` produced by [`validate!`](@ref). This ensures all numeric fields that participate in calculations share a common element type (e.g., `Float64`, `Measurement{Float64}`).
5+
6+
# Arguments
7+
8+
- `::Type{C}`: Component type \\[dimensionless\\].
9+
- `ntv`: Normalized `NamedTuple` returned by `validate!` \\[dimensionless\\].
10+
- `_order::Tuple`: Ignored by this method; present for arity symmetry with `_coerced_args` \\[dimensionless\\].
11+
12+
# Returns
13+
14+
- The promoted numeric element type \\[dimensionless\\].
15+
16+
# Examples
17+
18+
```julia
19+
Tp = $(FUNCTIONNAME)(Tubular, (radius_in=0.01, radius_ext=0.02, material_props=mat, temperature=20.0), ())
20+
```
21+
"""
122
@inline _promotion_T(::Type{C}, ntv, _order::Tuple) where {C} =
223
resolve_T((getfield(ntv, k) for k in coercive_fields(C))...)
324

25+
"""
26+
$(TYPEDSIGNATURES)
27+
28+
Builds the positional argument tuple to feed the **typed core** constructor, coercing only the fields returned by `coercive_fields(C)` to type `Tp`. Non‑coercive fields (e.g., integer flags) are passed through unchanged. Field order is controlled by `order` (a tuple of symbols), typically `(required_fields(C)..., keyword_fields(C)...)`.
29+
30+
# Arguments
31+
32+
- `::Type{C}`: Component type \\[dimensionless\\].
33+
- `ntv`: Normalized `NamedTuple` returned by `validate!` \\[dimensionless\\].
34+
- `Tp`: Target element type for numeric coercion \\[dimensionless\\].
35+
- `order::Tuple`: Field order used to assemble the positional tuple \\[dimensionless\\].
36+
37+
# Returns
38+
39+
- A `Tuple` of arguments in the requested order, with coercions applied where configured.
40+
41+
# Examples
42+
43+
```julia
44+
args = $(FUNCTIONNAME)(Tubular, ntv, Float64, (:radius_in, :radius_ext, :material_props, :temperature))
45+
```
46+
"""
447
@inline _coerced_args(::Type{C}, ntv, Tp, order::Tuple) where {C} =
548
tuple((
649
let k = s, v = getfield(ntv, s)
@@ -9,14 +52,95 @@
952
for s in order
1053
)...)
1154

12-
# materialize tuple of symbols/defaults from tuple literal or const name
55+
"""
56+
$(TYPEDSIGNATURES)
57+
58+
Utility for the constructor macro to *materialize* input tuples from either:
59+
60+
- A tuple literal expression (e.g., `(:a, :b, :c)`), or
61+
- A bound constant tuple name (e.g., `_REQ_TUBULAR`).
62+
63+
Used to keep macro call sites short while allowing both styles.
64+
65+
# Arguments
66+
67+
- `mod`: Module where constants are resolved \\[dimensionless\\].
68+
- `x`: Expression or symbol representing a tuple \\[dimensionless\\].
69+
70+
# Returns
71+
72+
- A standard Julia `Tuple` (of symbols or defaults).
73+
74+
# Errors
75+
76+
- `ErrorException` if `x` is neither a tuple literal nor a bound constant name.
77+
78+
# Examples
79+
80+
```julia
81+
syms = $(FUNCTIONNAME)(@__MODULE__, :( :a, :b ))
82+
syms = $(FUNCTIONNAME)(@__MODULE__, :_REQ_TUBULAR)
83+
```
84+
"""
1385
_ctor_materialize(mod, x) = x === :(()) ? () :
1486
x isa Expr && x.head === :tuple ? x.args :
1587
x isa Symbol ? Base.eval(mod, x) :
1688
error("@_ctor: expected tuple literal or const tuple, got $(x)")
1789

1890
using MacroTools: postwalk
91+
"""
92+
$(TYPEDSIGNATURES)
93+
94+
Generates a weakly‑typed convenience constructor for a component `T`. The generated method:
95+
96+
1. Accepts exactly the positional fields listed in `REQ`.
97+
2. Accepts keyword arguments listed in `OPT` with defaults `DEFS`.
98+
3. Calls `validate!(T, ...)` forwarding **variables** (not defaults),
99+
4. Computes the promotion type via `_promotion_T(T, ntv, order)`,
100+
5. Coerces only `coercive_fields(T)` via `_coerced_args(T, ntv, Tp, order)`,
101+
6. Delegates to the numeric core `T(...)` with the coerced positional tuple.
102+
103+
`REQ`, `OPT`, and `DEFS` can be provided as tuple literals or as names of bound constant tuples. `order` is implicitly `(REQ..., OPT...)`.
19104
105+
# Arguments
106+
107+
- `T`: Component type (bare name) \\[dimensionless\\].
108+
- `REQ`: Tuple of required positional field names \\[dimensionless\\].
109+
- `OPT`: Tuple of optional keyword field names \\[dimensionless\\]. Defaults to `()`.
110+
- `DEFS`: Tuple of default values matching `OPT` \\[dimensionless\\]. Defaults to `()`.
111+
112+
# Returns
113+
114+
- A method definition for the weakly‑typed constructor.
115+
116+
# Examples
117+
118+
```julia
119+
const _REQ_TUBULAR = (:radius_in, :radius_ext, :material_props)
120+
const _OPT_TUBULAR = (:temperature,)
121+
const _DEFS_TUBULAR = (T₀,)
122+
123+
@_ctor Tubular _REQ_TUBULAR _OPT_TUBULAR _DEFS_TUBULAR
124+
125+
# Expands roughly to:
126+
# function Tubular(radius_in, radius_ext, material_props; temperature=T₀)
127+
# ntv = validate!(Tubular, radius_in, radius_ext, material_props; temperature=temperature)
128+
# Tp = _promotion_T(Tubular, ntv, (:radius_in, :radius_ext, :material_props, :temperature))
129+
# args = _coerced_args(Tubular, ntv, Tp, (:radius_in, :radius_ext, :material_props, :temperature))
130+
# return Tubular(args...)
131+
# end
132+
```
133+
134+
# Notes
135+
136+
- Defaults supplied in `DEFS` are **escaped** into the method signature (evaluated at macro expansion time).
137+
- Forwarding into `validate!` always uses *variables* (e.g., `temperature=temperature`), never literal defaults.
138+
- The macro is hygiene‑aware; identifiers `validate!`, `_promotion_T`, `_coerced_args`, and the type name are properly escaped.
139+
140+
# Errors
141+
142+
- `ErrorException` if `length(OPT) != length(DEFS)`.
143+
"""
20144
macro _ctor(T, REQ, OPT=:(()), DEFS=:(()))
21145
mod = __module__
22146
req = Symbol.(_ctor_materialize(mod, REQ))
@@ -55,63 +179,4 @@ macro _ctor(T, REQ, OPT=:(()), DEFS=:(()))
55179
node isa Symbol && (node in free) ? esc(node) : node
56180
end
57181
return ex2
58-
end
59-
60-
# macro _ctor(T, REQ, OPT=:(()), DEFS=:(()))
61-
# # NOTE: macros run in caller's module
62-
# mod = __module__
63-
64-
# req_syms_any = _ctor_materialize(mod, REQ)
65-
# opt_syms_any = _ctor_materialize(mod, OPT)
66-
# def_vals_any = _ctor_materialize(mod, DEFS)
67-
68-
# req_syms = Symbol.(req_syms_any)
69-
# opt_syms = Symbol.(opt_syms_any)
70-
# defs = def_vals_any
71-
72-
# length(opt_syms) == length(defs) || error("@_ctor: OPT and DEFS length mismatch")
73-
74-
# # === Build function signature ===
75-
# # positional args: plain Symbols (no esc here)
76-
# pos_args = req_syms
77-
78-
# # keyword defaults: name = (escaped default expr)
79-
# kw_defs = Any[]
80-
# for i in eachindex(opt_syms)
81-
# push!(kw_defs, Expr(:kw, opt_syms[i], esc(defs[i])))
82-
# end
83-
84-
# # validate! kwargs: name = name (pass-through)
85-
# call_kws = Any[]
86-
# for s in opt_syms
87-
# push!(call_kws, Expr(:kw, s, s))
88-
# end
89-
90-
# # order used for promotion/coercion: REQ ++ OPT
91-
# order_tuple = Expr(:tuple, (QuoteNode.(vcat(req_syms, opt_syms))...))
92-
93-
# # build function head: function T(pos...; kw=defaults...)
94-
# head = isempty(kw_defs) ?
95-
# Expr(:call, esc(T), pos_args...) :
96-
# Expr(:call, esc(T), pos_args..., Expr(:parameters, kw_defs...))
97-
98-
# # ntv = validate!(T, pos...; kw...)
99-
# validate_call = isempty(call_kws) ?
100-
# Expr(:call, :validate!, esc(T), pos_args...) :
101-
# Expr(:call, :validate!, esc(T), pos_args..., Expr(:parameters, call_kws...))
102-
103-
# promote_call = Expr(:call, :_promotion_T, esc(T), :ntv, order_tuple)
104-
# coerced_call = Expr(:call, :_coerced_args, :ntv, :Tp, order_tuple)
105-
106-
# # use a gensym so we can splat the coerced tuple
107-
# args_sym = gensym(:args)
108-
109-
# body = quote
110-
# ntv = $validate_call
111-
# Tp = $promote_call
112-
# local $args_sym = $coerced_call
113-
# return $(esc(T))($args_sym...)
114-
# end
115-
116-
# return esc(Expr(:function, head, body))
117-
# end
182+
end

0 commit comments

Comments
 (0)