Laurenz Mädje once explained the design in Better documentation for show vs show-set details · Issue #4591 · typst/typst · GitHub in 2024.
(The following is not an exact quotation and has been severely reformatted.)
- show-set example:
show x: set x(..)
- show-function example:
show x: it => { set x(..); it }
The decision that show-set behaves differently from was a trade-off made in v0.11.
Problem of show-function
When there are multiple show-function rules, they need to be evaluated in some order. This means an element goes through various stages of styling and there isn’t one canonical representation of it. This has various bad consequences.
A show rule that reconstructs a new objects should only be used as a last resort because it ruins the semantics of the document. (For instance, when you reconstruct a heading, it shows up twice in the outline.)
That’s also the reason why we decided to not do anything about show rules applying recursively when doing reconstruction. It is a problematic idiom and we don’t want to encourage it. Sometimes there is simply no other option, but it can also indicate that the thing one wants to do is problematic in the first place.
Simpleness of show-set
In contrast, with show-set Typst can statically resolve all rules at once when “preparing” the element. The resulting finished element is canonical and also what you get from query
.
So in general:
-
To just set defaults, a set / show-set rule is the best bet.
-
If additional logic is needed, try a show-where-set rule.
-
If even more complex, use show-function rule as a last resort.
As for my earliest example
show-set
#show heading: set text(purple)
#show heading: set text(green)
= Green
is approximately
#heading({
set text(purple)
set text(green)
[Green]
})
Therefore, the last set text
takes precedence.
show-function
#show heading: it => { set text(purple); it }
#show heading: it => { set text(green); it }
= Purple
turns into
#show heading: it => { set text(purple); it }
#{
set text(green)
heading[Purple]
}
and then
#{
set text(green)
{
set text(purple)
heading[Purple]
}
}
Therefore, the first set text
takes precedence.
A visible and intuitive example
show-set
#show list: set block(stroke: purple)
#show list: set block(stroke: green)
- List
- Nested
- List
- Recursive!
show-function
#show list: block.with(stroke: purple)
#show list: block.with(stroke: green)
// or equivalently
// #show list: it => block(stroke: purple, it)
// #show list: it => block(stroke: green, it)
- List
- Nested
- List
- Recursive!
show-function (more complex)
#show list: it => {
set block(stroke: purple)
grid(columns: 2, gutter: 1em, it, [Purple])
}
#show list: it => {
set block(stroke: green)
grid(columns: 2, gutter: 1em, it, [Green])
}
- List
- Nested
- List
- Recursive!
Full code
Prepend #set block(inset: 0.5em)
to all examples.
Ref: typst/tests/suite/styling/show.typ at 9a6268050fb769e18c4889fa5f59d4150e8878d6 · typst/typst · GitHub