States

This section is outdated. It may be still useful, but it is strongly recommended to study new context system (using the reference).

Before we start something practical, it is important to understand states in general.

Here is a good explanation of why do we need them: Official Reference about states. It is highly recommended to read it first.

So instead of

#let x = 0
#let compute(expr) = {
  // eval evaluates string as Typst code
  x = eval(
    expr.replace("x", str(x))
  )
  [New value is #x.]
}

#compute("10") \
#compute("x + 3") \
#compute("x * 2") \
#compute("x - 5")

DOES NOT COMPILE: Variables from outside the function are read-only and cannot be modified

You should write

#let s = state("x", 0)
#let compute(expr) = [
  #s.update(x =>
    eval(expr.replace("x", str(x)))
  )
  New value is #s.display().
]

#compute("10") \
#compute("x + 3") \
#compute("x * 2") \
#compute("x - 5")

The computations will be made _in order_ they are located in the document:

#let more = [
  #compute("x * 2") \
  #compute("x - 5")
]

#compute("10") \
#compute("x + 3") \
#more
Rendered image

Operations with states

Creating new state

#let x = state("state-id")
#let y = state("state-id", 2)

#x, #y

#x.display() \
#y.display(n => "State is " + str(n))
Rendered image

Update

Updating is a content that tells that in this place of document the state should be updated.

#let x = state("x", 0)
#x.display() \
#let _ = x.update(3)
// nothing happens, we don't put `update` into the document flow
#x.display() \
#repr(x.update(3)) \ // this is how that content looks \
#x.update(3)
#x.display() // Finally!
Rendered image

ID collision

TLDR; Never allow colliding states.

States are described by their id-s, if they are the same, the code will break.

So, if you write functions or loops that are used several times, be careful!

#let f(x) = {
  // return new state…
  // …but their id-s are the same!
  // so it will always be the same state!
  let y = state("x", 0)
  y.update(y => y + x)
  y.display()
}

#let a = f(2)
#let b = f(3)

#a, #b \
#repr(a), #repr(b)
Rendered image

However, this may seem okay:

// locations in code are different!
#let x = state("state-id")
#let y = state("state-id", 2)

#x, #y
Rendered image

But in fact, it isn't:

#let x = state("state-id")
#let y = state("state-id", 2)

#x.display(), #y.display()

#x.update(3)

#x.display(), #y.display()
Rendered image