Why the difference between set-if and if...set?

The following example shows a (to me) unexpected difference between set-if and if…set. I expected the same behavior in both instances.

Could somebody explain to me what is happening here?

(Same results with versions from 0.10 to 0.14 rc.)

 #let ff(indent: true, body) = {
 set par(first-line-indent: 2em) if indent == true 
 set par(first-line-indent: 0em) if indent == false 
 body
}
#let gg(indent: true, body) = {
  if indent == true {
    set par(first-line-indent: 2em)
  } else {
    set par(first-line-indent: 0em)
  }
  body
}

== F
=== True
  #ff[Expect indent#parbreak()Indented]

=== False
  #ff(indent: false)[Expect no indent#parbreak()Not indented]

== G
=== True
  #gg[Expect indent#parbreak()No indent---why?]

=== False
  #gg(indent: false)[Expect no indent#parbreak()Not indented]

Results in:

set-if exists just for this use case, so set with if is the only correct way.

The reason is that set only has effect until the end of the current scope.

Where does the current scope end? At the next }, so both sets have no effect (apply to nothing).

It’s mentioned here in the docs Styling – Typst Documentation but it’s a point that’s worth bringing up explicitly like this.

With an example:

{
  set par(first-line-indent: 2em)
  /* The set applies to any document part located here */
}
// The above `set` has no effect on what follows here

It doesn’t matter if it’s a [] or {} or other delimited scope, the same rule applies.

4 Likes

We can rewrite the function gg in this way, and that’s also something that’s used sometimes, and then it will have the originally intended effect:

#let gg(indent: true, body) = {
  if indent {
    set par(first-line-indent: 2em)
    body
  } else {
    set par(first-line-indent: 0em)
    body
  }
}
1 Like

Is this necessary in Typst? In other languages you would just write

if indent {

No, it’s not necessary; I don’t think it’s necessary here. But typst is a dynamic language, and you could maybe have use for indent == true if indent can be either bool or none, for example.

2 Likes

There’s another simple way:

set par(
  first-line-indent: if indent { 2em } else { 0em },
)

I use this flavour when I customize many options of par but only first-line-indent depends on indent.

2 Likes