this is what the closure actually looks like if you're curious! https://gist.github.com/Archenoth/d189ce7b20ea922be3d3169f6b369fef
(i named it .clj because github doesn't recognize fennel)
basically, every tick, the coroutine to render a single line of dialogue resumes with a new time in scope, and based on that, renders a subset of text
after it renders a full line, it continues to a new yield loop to render the bouncy triangle at the bottom to note you can progress
and when the coroutine finally finishes, a new line takes its place
the magic is that resuming a coroutine just jumps right back to the place i yielded from, so i can break this coroutine into tiny pieces that get rendered every frame
meaning, i only ever need to interact with it in once place after making it; it's completely self-contained!
(i wonder how this compares to how scheme continuations work?)
coroutines function almost as a limmitted version of call/cc, gur limit is that they can only be resumed once for each yield
coroutines can actually be implemented with call/cc, something like
(def (m-coroutine yield-fn)
(show-dialog "* farts *")
(call/cc yield-fn)
(show-dialog "pardon me.")
(call/cc yield-fn))
(def co-resume (call/cc m-coroutine)
; * farts * is shown
(set! (call/cc co-resume))
; pardon me. is shown
@bx oh, interesting!
i remember thinking i grokked how this worked a while ago, but never had an opportunity to try it in practice
it didn't even occur to me that continuations could be continued as many times as you liked from the same spot rather than just pausing a function and resuming
@bx it's still a miracle to me that this exists, hahah
@thingywott Heheh, thanks :D
i find it kinda funny, cause after finish it i found out Factor also has call/cc and it just implements it completely differently (saving all gur stacks + current position then calling another func), where as I ended up writing in CPS style after reading about CHICKEN's internals
@bx oh dang! stack funtimes
@thingywott yeah, not having to support that with coroutines really eases up gur implementation (and makes it easier to stay light weight)
i like to think of call/cc as functional setjmp/longjmp from C, since call/cc doesn't actually do any mutation!
My implementation was done in Pico-8's Lua (which is much much easier than doing it in C :P)
https://git.tilde.town/bx/lis.p8