#Problem updating and returning value of counter

1 messages · Page 1 of 1 (latest)

pearl plank
#

I'm trying to create a counter to label paragraphs.

First, I create a counter like this:

#let gParagraphCounter = counter("para-counter")

Then, I create a couple of functions to work with the counter.

#let ParLabelReset() =  {
    gParagraphCounter.update(42)
}

#let ParLabel() = context {
    gParagraphCounter.step()
    let num = gParagraphCounter.get().last()
    return str(num) + ")"
}

Then, the usage is supposed to be:

// At the beginning of the document/chapter.
// Reset the paragraph counter.
#ParLabelReset()

// ... later in the document
#ParLabel() This is some text in paragraph 1.


#ParLabel() This is some text in paragraph 2.

The problem:
The value returned by #ParLabel() is always 42. The gParagraphCounter.step() doesn't seem to have an effect.

How can I have the #ParLabel() update the value of the counter, prior to returning the updated value?

tranquil kestrel
#

basically it's doing something like

#let ParLabel() = [#context-provider(ctx => {
  gParagraphCounter.step()
  let num = gParagraphCounter.get(ctx).last()
  return str(num) + ")"
})]
#

the ctx was already there when you stepped so it's not picked up

#

oh

#

theres actually two problems

#

thats one of them

#

the second one is the fact that you're using return

#

and so you're discarding the marker element returned by .step()

#

a counter step only takes effect if you place what it returns in the document

#

(it does not return none!)

#

so

#

to fix both things, remove return and move context to the inside

#
#let ParLabel() = {
  gParagraphCounter.step()
  // note: no return
  context { 
    let num = gParagraphCounter.get().last()
    [#num)]
  }
}
#

then it will return gParagraphCounter.step() + context { ... } automatically (both the step and the context will be placed)

pearl plank
#

Whenever I think I'm finally understanding the state management in Typst, I trigger a new usecase that shows me that I don't fully get it.
I'm not gonna lie, I only understood about 40% of what you wrote. But, I still count it as a win:

  1. I do understand state management a bit more that yesterday.
  2. The labeling function is working.
  3. With the body of code that I'm building, which utilizes stateful objects, eventually it will all click.
#

Thank you very much for the help.

tranquil kestrel
#

but what is the location where you run counter.get()? a location is always tied to an element in the document, e.g. when you place a heading, you can refer to where that heading appears in the document, so thats an example of a location

a location is not something like x=29345 y=203 - so that's an important distinction

So what context does here is: context { } creates an element which must be placed in the document so that it gains a location
it then provides that location to .get() so it knows: query all steps until that location ("here")

pearl plank
#

So, in this case, "location" is like a position in a graph?

tranquil kestrel
#

yeah, in the element tree, so to speak

pearl plank
#

ah, I see. That does further my understanding a bit.

#

What is the difference between putting context outside the first {, and inside?

#

Well, besides the fact that outside encompases the .step() call

tranquil kestrel
#

the main difference is that it changes the location , when it's outside the location is "before" the .step() call since the context element is its parent in the tree, when it's inside it's "after" since it's a sibling that was joined afterwards

#

so when it's inside .get()'s query for everything "before" will include that .step(), when it's outside it won't

#

so if you add a bunch of step() inside a context block, a .get() after them inside the same context block will return the same value as if the .step() werent there

#

precisely because the context element always gets information from before the block started

pearl plank
#

oh, this is a key sentence: "the context element always gets information from before the block started"

#

That's very illuminating.

tranquil kestrel
#

yeah, maybe it helps to think of it as a "freeze"

#

note that you could keep both contexts - outside and inside - if you wanted, but the outside one would only apply to .step() if you do that (the inside one would update its own context regardless), which wouldnt change much cuz .step() doesnt use context for anything

pearl plank
#

If i may make a suggestion. I've read to the "context" and "counter" pages a few times. I don't recall reading that info about the computing of value like a "freeze" kinda thing. I think it would be helpful to have a section about that.

#

I think it would be illuminating to have that details.

#

I would even mention about how it relates to the element tree, as you said.

#

The context is a thing that is technical, and I don't think it's possible to just explain it in a high level, and to leave the low level details out. I think those low-level details cannot be escaped.

#

Just my 2 cents.

tranquil kestrel
#

yeah, i agree the docs need improvement

pearl plank
#

I think the docs are pretty good, overall. Just that this knowledge placed in that context page would be additionally helpful.

tranquil kestrel
#

yeah

#

im not sure but i think there isnt an issue for this yet, so you could make one, i think you can just copy paste what you said here (maybe adjust a bit if needed) and it should be good

pearl plank
#

Make an issue in the Github, you mean?

tranquil kestrel
pearl plank
#

ok. I will do that.