Overriding the constructor and argument parsing
Disabling typechecking
You can use typecheck: false
to generate an argument parser that doesn't check fields' types. This is useful to retain type information but disable checking if that's needed. The performance difference is likely to not be too significant, so that likely wouldn't be enough of a reason, unless too many advanced typesystem features are used.
Custom constructor
You can use construct: default-constructor => (..args) => value
to override the default constructor for your custom type. You should use construct:
rather than creating a wrapper function to ensure that data retrieval functions, such as e.data(func)
, still work.
Custom argument parsing
You can use parse-args: (default arg parser, fields: dictionary, typecheck: bool) => (args, include-required: bool) => dictionary with fields
to override the built-in argument parser. This is used both for the constructor and for set rules.
Here, args
is an arguments
and include-required: true
indicates the function is being called in the constructor, so required fields must be parsed and enforced.
However, include-required: false
indicates a call in set rules, so required fields must not be parsed and forbidden.
Argument sink
Here's how you'd use this to implement a positional argument sink:
#let sunk = e.element.declare(
"sunk",
display: it => {
(it.run)(it)
},
fields: (
field("values", e.types.array(stroke), required: true),
field("run", function, required: true, named: true),
field("color", color, default: red),
field("inner", content, default: [Hello!]),
),
parse-args: (default-parser, fields: none, typecheck: none) => (args, include-required: false) => {
let args = if include-required {
// Convert positional arguments into a single 'values' argument
let values = args.pos()
arguments(values, ..args.named())
} else if args.pos() == () {
args
} else {
assert(false, message: "element 'sunk': unexpected positional arguments\n hint: these can only be passed to the constructor")
}
default-parser(args, include-required: include-required)
},
prefix: ""
)
// Use 'run: func' as an example to test and ensure we received the correct fields
#sunk(
5pt, 10pt, black, 5pt + black,
run: it => assert.eq(it.values, (5pt, 10pt, black, 5pt + black).map(stroke))
)