Am Do., 21. Feb. 2019 um 10:38 Uhr schrieb Marc Nieper-Wißkirchen <xxxxxx@gmail.com>:
>
> Am Do., 21. Feb. 2019 um 10:17 Uhr schrieb Alex Shinn <xxxxxx@gmail.com>:

[...]

> > Mostly to have and compare the alternate implementation strategy,
> > and I suspect it may be less hairy.
>
> I will let you know when I will have found the time to implement (show
> columnar).

In (chibi show columnar) first-class continuations are only used in "call-with-output-generator". The reason why first-class continuations are used there is the same, why "make-coroutine-generator" of SRFI 121 depends on first-class continuation. Therefore, it may be a good idea, not to use first-class continuations in (chibi show columnar) but to let the (lower level) SRFI 121 do all the dirty work (which may or may not be implemented with first-class continuations).

And indeed it works out quite elegantly!

First of all, let us define the following monadic procedure (which should become a part of SRFI 165 in its next draft):

(define (bind/forked computation . proc*)
  (apply bind
     (local environment-copy computation)
     proc*))

"bind/forked" combines "bind" (from SRFI 165) and "forked" (from SRFI 159/165): (bind/forked computation proc1 ...) is like (bind computation proc1 ...), with the difference that bind/forked executes computation in a copy of the environment.

The following formatter just updates the state variables "row" and "col" (and could be used by the default output). (Note that I am using "make-computation" to create a formatter so that I can use "string-for-each", which is in R7RS-small.)

(define (hidden str)
  (fn ((string-width string-width))
    (make-computation
     (lambda (format)
       (string-for-each
        (lambda (ch)
          (if (char=? ch #\newline)
              (format (fn ((%row row))
                (with! (row (+ %row 1))
                       (col 0))))
              (format (fn ((%col col))
                (with! (col (+ %col (string-width (string ch)))))))))
        str)))))

The interesting procedure is the following (and is basically doing what "call-with-output-generator" is doing in the reference implementation:

(define (with-output-generator fmt)
  (make-computation
   (lambda (format)
     (make-coroutine-generator
      (lambda (yield)
        (format (with ((output
                       (lambda (str)
                         (yield str)
                         (hidden str))))
                  (bind/forked fmt))))))))

"(with-output-generator fmt)" returns a formatter that, when executed, yields a generator. (Here, I am using the extensions from SRFI 165 and that a formatter may have a non-trivial monadic value.)

"(call-with-output-generator producer consumer)" would then be "(bind (with-output-generator producer) consumer)", using the SRFI 165 primitive "bind".

[...]

Marc