They're essentially callable, stateful, structured gotos. Difficult to understand for the uninitiated.

For example, generators. Also known as semicoroutines.

https://langdev.stackexchange.com/a/834

This:

  generator fib() {
      a, b = 1, 2
      while (a<100) {
          b, a = a, a+b
          yield a
      }
      yield a-1
  }
Becomes this:

  struct fibState {
      a,
      b,
      position
  }

  int fib(fibState state) {
      switch (fibState.postion) {
          case 0:
              fibState.a, fibState.b = 1,2
              while (a<100) {
                  fibState.b, fibState.a = fibState.a, fibState.a+fibState.b
                  // switching the context
                  fibState.position = 1;
                  return fibState.a;
          case 1:
              }

              fibState.position = 2;
              return fibState.a-1
          case 2:
              fibState.position = -1;
      }
  }
The ugly state machine example presented in the article is also a manual implementation of a generator. It's as palatable to the normal programmer as raw compiler output. Being written in C++ makes it even uglier and more complicated.

The programming language I made is a concrete example of what programming these things manually is like. I had to write every primitive as a state machine just like the one above.

https://www.matheusmoreira.com/articles/delimited-continuati...

What you've given is an example of how to implement a coroutine though.

Not of how to write a state machine based application without hiding the state machine behind abstractions.