#lang scribble/doc
@(require scribble/struct scribble/racket "mz.rkt")

@(define (cont n)
   (make-element variable-color
                 (list "C" (make-element 'subscript (list (format "~a" n))))))

@title[#:tag "contmarks"]{Continuation Marks}

See @secref["mark-model"] and @secref["prompt-model"] for
general information about continuation marks.

The list of continuation marks for a key @racket[_k] and a continuation
@racket[_C] that extends @cont[0] is defined as follows:

@itemize[

 @item{If @racket[_C] is an empty continuation, then the mark list is
 @racket[null].}

 @item{If @racket[_C]'s first frame contains a mark @racket[_m] for @racket[_k],
 then the mark list for @racket[_C] is @racket[(cons _m _lst)],
 where @racket[_lst] is the mark list for @racket[_k] in @cont[0].}

 @item{If @racket[_C]'s first frame does not contain a mark keyed by
 @racket[_k], then the mark list for @racket[_C] is the mark list for
 @cont[0].}

]

The @racket[with-continuation-mark] form installs a mark on the first
frame of the current continuation (see @secref["wcm"]).  Procedures
such as @racket[current-continuation-marks] allow inspection of marks.

Whenever Racket creates an exception record for a primitive exception,
it fills the @racket[continuation-marks] field with the value of
@racket[(current-continuation-marks)], thus providing a snapshot of
the continuation marks at the time of the exception.

When a continuation procedure returned by
@racket[call-with-current-continuation] or
@racket[call-with-composable-continuation] is invoked, it restores the
captured continuation, and also restores the marks in the
continuation's frames to the marks that were present when
@racket[call-with-current-continuation] or
@racket[call-with-composable-continuation] was invoked.

@defproc[(continuation-marks [cont (or/c continuation? thread? #f)]
                             [prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
         continuation-mark-set?]{

Returns an opaque value containing the set of continuation marks for
all keys in the continuation @racket[cont] (or the current
continuation of @racket[cont] if it is a thread) up to the prompt
tagged by @racket[prompt-tag]. If @racket[cont] is @racket[#f], the
resulting set of continuation marks is empty. If @racket[cont] is an escape
continuation (see @secref["prompt-model"]), then the current
continuation must extend @racket[cont], or the
@exnraise[exn:fail:contract]. If @racket[cont] was not captured with
respect to @racket[prompt-tag] and does not include a prompt for
@racket[prompt-tag], the @exnraise[exn:fail:contract]. If
@racket[cont] is a dead thread, the result is an empty set of
continuation marks.}

@defproc[(current-continuation-marks [prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
         continuation-mark-set?]{

Returns an opaque value containing the set of continuation marks for
all keys in the current continuation up to @racket[prompt-tag]. In
other words, it produces the same value as

@racketblock[
(call-with-current-continuation
  (lambda (k) 
    (continuation-marks k prompt-tag))
  prompt-tag)
]}

@defproc[(continuation-mark-set->list
          [mark-set (or/c continuation-mark-set? #f)]
          [key-v any/c]
          [prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
         list?]{
Returns a newly-created list containing the marks for @racket[key-v]
in @racket[mark-set], which is a set of marks returned by
@racket[current-continuation-marks] or @racket[#f] as a shorthand for
@racket[(current-continuation-marks prompt-tag)]. The result list is truncated at
the first point, if any, where continuation frames were originally
separated by a prompt tagged with @racket[prompt-tag]. Producing the result
takes time proportional to the size of the continuation reflected by
@racket[mark-set].

@history[#:changed "8.0.0.1" @elem{Changed to allow @racket[mark-set] as @racket[#f].}]}


@defproc[(continuation-mark-set->list*
          [mark-set (or/c continuation-mark-set? #f)]
          [key-list (listof any/c)]
          [none-v any/c #f]
          [prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
         (listof vector?)]{
Returns a newly-created list containing vectors of marks in
@racket[mark-set] for the keys in @racket[key-list], up to
@racket[prompt-tag], where a @racket[#f] value for @racket[mark-set]
is equivalent to @racket[(current-continuation-marks prompt-tag)].
The length of each vector in the result list is
the same as the length of @racket[key-list], and a value in a
particular vector position is the value for the corresponding key in
@racket[key-list]. Values for multiple keys appear in a single vector
only when the marks are for the same continuation frame in
@racket[mark-set]. The @racket[none-v] argument is used for vector
elements to indicate the lack of a value. Producing the result
takes time proportional to the size of the continuation reflected by
@racket[mark-set] times the length of @racket[key-list].

@history[#:changed "8.0.0.1" @elem{Changed to allow @racket[mark-set] as @racket[#f].}]}


@defproc[(continuation-mark-set->iterator
          [mark-set (or/c continuation-mark-set? #f)]
          [key-list (listof any/c)]
          [none-v any/c #f]
          [prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
         (-> (values (or/c vector? #f) procedure?))]{

Like @racket[continuation-mark-set->list*], but instead of returning a
list of values, returns a functional iterator in the form of a
procedure that returns one element of the would-be list and a new
iterator function for the rest of the would-be list. An iterator
procedure returns @racket[#f] instead of a vector when no more
elements are available; in that case, the returned iterator
procedure is like the called one, producing no further values.
The time required for each step is proportional to the length of
@racket[key-list] times the size of the segment of the continuation
reflected by @racket[mark-set] between frames that have keys in
@racket[key-list].

@history[#:added "7.5.0.7"
         #:changed "8.0.0.1" @elem{Changed to allow @racket[mark-set] as @racket[#f].}]}


@defproc[(continuation-mark-set-first 
          [mark-set (or/c continuation-mark-set? #f)]
          [key-v any/c]
          [none-v any/c #f]
          [prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
         any]{
Returns the first element of the list that would be returned by
@racket[(continuation-mark-set->list (or mark-set
(current-continuation-marks prompt-tag)) key-v prompt-tag)], or
@racket[none-v] if the result would be the empty list.

The result
is produced in (amortized) constant time. Typically, this
result can be computed more quickly using
@racket[continuation-mark-set-first] than using
@racket[continuation-mark-set->list] or by using
@racket[continuation-mark-set->iterator] and iterating just once.

Although @racket[#f] and @racket[(current-continuation-marks
prompt-tag)] are equivalent for @racket[mark-set], providing @racket[#f]
as @racket[mark-set] can enable shortcuts that make it even faster.}


@defproc[(call-with-immediate-continuation-mark
          [key-v any/c]
          [proc (any/c . -> . any)]
          [default-v any/c #f])
         any]{

Calls @racket[proc] with the value associated with @racket[key-v] in
the first frame of the current continuation (i.e., a value that would
be replaced if the call to
@racket[call-with-immediate-continuation-mark] were replaced with a
@racket[with-continuation-mark] form using @racket[key-v] as the key
expression). If no such value exists in the first frame,
@racket[default-v] is passed to @racket[proc]. The @racket[proc] is
called in tail position with respect to the
@racket[call-with-immediate-continuation-mark] call.

This function could be implemented with a combination of
@racket[with-continuation-mark], @racket[current-continuation-marks],
and @racket[continuation-mark-set->list*], as shown below, but
@racket[call-with-immediate-continuation-mark] is implemented more
efficiently; it inspects only the first frame of the current
continuation.

@racketblock[
(code:comment "Equivalent, but inefficient:")
(define (call-with-immediate-continuation-mark key-v proc [default-v #f])
  (define private-key (gensym))
  (with-continuation-mark
   private-key #t
   (let ([vecs (continuation-mark-set->list* (current-continuation-marks)
                                             (list key-v private-key)
                                             default-v)])
     (proc (vector-ref (car vecs) 0)))))
]}


@defproc*[([(make-continuation-mark-key) continuation-mark-key?]
           [(make-continuation-mark-key [sym symbol?]) continuation-mark-key?])]{
Creates a continuation mark key that is not @racket[equal?] to the result
of any other value (including prior and future results from
@racket[make-continuation-mark-key]). The continuation mark key can be used
as the key argument for @racket[with-continuation-mark] or accessor procedures
like @racket[continuation-mark-set-first]. The mark key can be chaperoned
or impersonated, unlike other values that are used as the mark key.

The optional @racket[sym] argument, if provided, is used when printing
the continuation mark.}


@defproc[(continuation-mark-key? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a mark key created by
@racket[make-continuation-mark-key], @racket[#f] otherwise.}


@defproc[(continuation-mark-set? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a mark set created by
@racket[continuation-marks] or @racket[current-continuation-marks],
@racket[#f] otherwise.}


@defproc[(continuation-mark-set->context [mark-set continuation-mark-set?])
          list?]{

Returns a list representing an approximate ``@index["stack
dump"]{@as-index{stack trace}}'' for @racket[mark-set]'s
continuation. The list contains pairs, where the @racket[car] of each
pair contains either @racket[#f] or a symbol for a procedure name, and
the @racket[cdr] of each pair contains either @racket[#f] or a
@racket[srcloc] value for the procedure's source location (see
@secref["linecol"]); the @racket[car] and @racket[cdr] are never both
@racket[#f].

Conceptually, the stack-trace list is the result of
@racket[continuation-mark-set->list] with @racket[mark-set] and
Racket's private key for procedure-call marks. The implementation may
be different, however, and the results may merely approximate the
correct answer. Thus, while the result may contain useful hints to
humans about the context of an expression, it is not reliable enough
for programmatic use.

A stack trace is extracted from an exception and displayed by the
default error display handler (see
@racket[error-display-handler]) for exceptions other than
@racket[exn:fail:user] (see @racket[raise-user-error] in
@secref["errorproc"]).}

@examples[
(define (extract-current-continuation-marks key)
  (continuation-mark-set->list
   (current-continuation-marks)
   key))

(with-continuation-mark 'key 'mark
  (extract-current-continuation-marks 'key))

(with-continuation-mark 'key1 'mark1
  (with-continuation-mark 'key2 'mark2
    (list
     (extract-current-continuation-marks 'key1)
     (extract-current-continuation-marks 'key2))))

(with-continuation-mark 'key 'mark1 
  (with-continuation-mark 'key 'mark2 (code:comment @#,t{replaces previous mark})
    (extract-current-continuation-marks 'key)))

(with-continuation-mark 'key 'mark1 
  (list (code:comment @#,t{continuation extended to evaluate the argument})
   (with-continuation-mark 'key 'mark2 
      (extract-current-continuation-marks 'key))))

(let loop ([n 1000])
  (if (zero? n) 
      (extract-current-continuation-marks 'key) 
      (with-continuation-mark 'key n
        (loop (sub1 n)))))
]
