[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Continuation sets and order-independency
From: |
Mark H Weaver |
Subject: |
Re: Continuation sets and order-independency |
Date: |
Mon, 09 Jan 2012 09:45:03 -0500 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.0.92 (gnu/linux) |
Hi David,
David Kastrup <address@hidden> writes:
> Noah Lavine <address@hidden> writes:
>
>> Okay, let me see if this is right now.
>>
>> In the expression
>>
>> (list (call-with-current-continuation func) (+ 4 14)),
>>
>> you want the addition to be done before the
>> call-with-current-continuation, as opposed to being part of the
>> continuation.
>>
>> Right?
>
> This is part of it, yes. If you think as "(list expr1 expr2 ...)" as
>
> (call-with-values (parallel expr1 expr2 ...) list)
>
> and each of the expr might suspend its thread (and record the thread
> somewhere for waking up), then you have parallel execution that
> continues until all paths have reached completion or suspension.
>
> Now instead of parallel execution, it would be ok to have the
> serialization of call/cc but still continue in all paths to completion
> or suspension.
>
> Of course, a single call/cc just stores away a single continuation, and
> whatever continuation while evaluating "list" was reached, one can't
> prod Scheme into taking up another branch without letting that
> continuation continue, so obviously something is missing in this
> picture.
I'm still not entirely clear on what you want, but perhaps you're
looking for delimited continuations, a.k.a composable continuations,
partial continuations, or _prompts_. They are not yet part of standard
Scheme, but some advanced implementations have them, including Guile
2.0. (They are not available in Guile 1.8).
http://www.gnu.org/software/guile/manual/html_node/Prompts.html
I've attached a small example module that might do what you want.
Here's an example session, starting with the highest-level syntax:
scheme@(guile-user)> ,use (mhw suspendable)
scheme@(guile-user)> (%% list 1 2 3 (begin (suspend) 4) 5)
$1 = ((completed 1) (completed 2) (completed 3) (suspended
#<partial-continuation 10541cd0>) (completed 5))
scheme@(guile-user)> (map final-values $1)
$2 = (1 2 3 4 5)
scheme@(guile-user)>
This is based on some lower-level primitives: (suspendable <expr> ...)
evaluates the expressions and returns a <suspension>.
A <suspension> is a simple list structure:
(completed . <return-values>), or
(suspended <partial-continuation> . <args>)
The latter is returned if (suspend <arg> ...) is called within the
dynamic extent of the `suspendable' form.
A suspension can be resumed as follows:
(resume <suspension>)
`resume' returns another <suspension>, which is not necessarily
completed, because `suspend' may be called multiple times. To
repeatedly resume a suspension until it completes, and return its values
nicely, use:
(final-values <suspension>)
For example:
scheme@(guile-user)> (suspendable (begin (for-each suspend '(1 2 3)) 'done))
$3 = (suspended #<partial-continuation 1055eec0> 1)
scheme@(guile-user)> (resume $3)
$4 = (suspended #<partial-continuation 10564db0> 2)
scheme@(guile-user)> (resume $4)
$5 = (suspended #<partial-continuation 1058bc60> 3)
scheme@(guile-user)> (resume $5)
$6 = (completed done)
scheme@(guile-user)> (suspendable (begin (for-each suspend '(1 2 3)) 'done))
$7 = (suspended #<partial-continuation 1059a230> 1)
scheme@(guile-user)> (final-values $7)
$8 = done
Basically what the high-level `%%' syntax does is to call a procedure,
but automatically evaluate all of its operands as parallel suspendable
computations. The suspensions are passed to the procedure, which may
resume them (or ask for their final-values) as desired.
(Actually, the operator is evaluated in the same way as the operands, in
the tradition of Scheme.)
For example, (%% list 1 2 3 (begin (suspend) 4) 5) expands to:
(call-with-values
(lambda () (parallel (suspendable list)
(suspendable 1)
(suspendable 2)
(suspendable 3)
(suspendable (begin (suspend) 4))
(suspendable 5)))
(lambda (proc . args)
(apply (final-values proc) args)))
Is this what you're looking for, or something close to it?
Regards,
Mark
(define-module (mhw suspendable)
#:use-module (ice-9 threads)
#:export (start-suspendable-thunk
completed? suspended?
suspend resume final-values)
#:export-syntax (suspendable %%))
(define suspend-tag (make-prompt-tag "suspend"))
(define (start-suspendable-thunk thunk)
(resume-suspendable-thunk
(lambda ()
(call-with-values thunk
(lambda results
`(completed ,@results))))))
(define (resume-suspendable-thunk thunk)
(call-with-prompt
suspend-tag
thunk
(lambda (resume . args)
`(suspended ,resume ,@args))))
(define-syntax-rule (suspendable e0 e ...)
(start-suspendable-thunk (lambda () e0 e ...)))
(define (completed? s) (and (pair? s) (eq? (car s) 'completed)))
(define (suspended? s) (and (pair? s) (eq? (car s) 'suspended)))
(define (suspend . args)
(apply abort-to-prompt suspend-tag args))
(define (resume s)
(cond ((suspended? s) (resume-suspendable-thunk (cadr s)))
((completed? s) s)
(else (error "resume: invalid argument"))))
(define (final-values s)
(cond ((suspended? s) (final-values (resume s)))
((completed? s) (apply values (cdr s)))
(else (error "finalize: invalid argument"))))
(define-syntax-rule (%% op operand ...)
(call-with-values
(lambda () (parallel (suspendable op)
(suspendable operand) ...))
(lambda (proc . args)
(apply (final-values proc) args))))