-- Leo's gemini proxy

-- Connecting to michal_atlas.srht.site:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

Ambience


> This is written in Guile Scheme It’s quite similar to the Racket version which however has slightly uglier syntax so, meh


And goddamn there’s a lot of stuff I’d need to cover let’s say we’re all schemers who just happen to not know about these things


Imports


(use-modules (ice-9 control))

Imports shortened versions of prompt and abort.


(call-with-prompt 'tag (lambda () <body>) <handler>) => (% <body> <handler>)
(abort-to-prompt 'tag <args...>) => (abort <args...>)

Parameters


(define foo (make-parameter 0))

They are interesting things, skipping over some details it’s basically the scheme way to do dynamic scoping.


Basically the current value inside the parameter depends on the last parameterization that you’ve been call with. That’s an important part, that you’ve been called with, not that has been set there.


(define (bar)
    "A Function that just returns the parameter"
    (foo))

(parameterize ([foo 20]) (bar)) ;=> Will return 20

It basically acts as a let, that doesn’t really have to get the symbol all the way to the target, it’s sufficient that it’s seen globally, and the correct value will get through.


In Guile there’s more details under the keyword fluid, since that’s basically sorta the original name for this if I understand it correctly, but parameters kinda came and became very standard across Schemes so I used those this time.


Prompts


Prompts delimit continuations… that probably meant nothing to 99% of people, let me try again.


For this case it’s sufficient to know that from within a prompt, you may run a procedure named abort that let’s you play around with how that function should continue onwards.


% looks like a prompt so Guile has a macro to define prompts with it.


(% <body> <handler>)

(% (begin
    (abort 5)
    (display 6))
   (lambda (k num)
    (display num)))

The above code only prints 5. When you call abort it jumps out to the handler and passes whatever arguments abort got to it. You may notice that there’s an extra one named k. k is used to mark continuations, which basically means how the program will continue running from that point on, and is passed automatically to any handler as the first argument.


It’s invoked as a method and takes an argument that will be returned from the expression the evaluation is “paused” on. Since I didn’t use it, then the rest of the program after abort, namely the display just got thrown out. If I instead ran (k 7) inside the handler for example, then the return value of (abort 5) would be 7 (which yeah, is irrelevant here) and then evaluation would proceed to (display 6).


You could use k multiple times, or cons something onto it, or compose it, there’s a ton of magic, but we’re not looking at that today.


What I want to do today is wrap the evaluation of k in a parameterize:


(define (prompt-handler k)
  (% (k) ;; Evaluate this in prompt
     (lambda (k arg) ;; Handler
       (parameterize ([foo (+ arg (foo))]) ;; value of foo is (foo+arg)
                                           ;; in the body of parameterize
         (prompt-handler k))))) ;; Evaluate k in another prompt
                                ;; The handler is outside
                                ;; the original one

> I know I could use the default handler, but I’d have to throw more stuff in the abort call probably define a different function for that, whatevs there’s a million ways to do anything


k is also wrapped in a prompt-handler here so that the continuation doesn’t fail to abort when you invoke the continuation, since the handler isn’t under the given prompt. The built-in default handler actually deals with this and basically in the exact same way.


So now, what’s done is done, and when I run abort, the rest of the evaluation is wrapped in a parameterize which takes the number I have it and adds it to what was already in foo.


The Program


(prompt-handler
 (lambda ()
   (display (foo))
   (abort 4)
   (display (foo))
   (abort 6)
   (display (foo))))

Output: 0 4 10


So basically now, the top-level starts with foo set to 0, and when I get to (abort 4), I jump to the handler and wrap the rest of the evaluation in an environment so that the next display sees a 4, etc.


It’s quite a nice little trick, fun that helps with thinking about continuations, I seem to use them more and more often, the more comfortable I am with them.

-- Response ended

-- Page fetched on Sun May 12 07:18:21 2024