연속 마크
연속 마크
연속 마크(continuation mark)에 대한 일반적인 정보는 연속 프레임과 마크, 프롬프트·구획 연속·배리어 문서를 참고해요.
출처: Racket Reference
본문
키 k와 연속 C(그리고 C가 확장한 C0)에 대한 연속 마크 목록은 다음과 같이 정의돼요:
C가 빈 연속이면 마크 목록은null이에요.C의 첫 프레임이k에 대한 마크m을 담고 있으면,C에 대한 마크 목록은(cons m lst)인데, 여기서lst는C0에서k에 대한 마크 목록이에요.C의 첫 프레임이k로 키가 정해진 마크를 담고 있지 않으면,C에 대한 마크 목록은C0의 마크 목록이에요.
with-continuation-mark 형식은 현재 연속의 첫 프레임에 마크를 설치해요(연속 마크: with-continuation-mark 참고). current-continuation-marks 같은 프로시저는 마크를 조사할 수 있게 해줘요.
Racket이 프리미티브 예외에 대한 예외 레코드를 만들 때마다, (current-continuation-marks)의 값으로 continuation-marks 필드를 채워 예외 시점의 연속 마크 스냅샷을 제공해요.
call-with-current-continuation이나 call-with-composable-continuation이 돌려준 연속 프로시저가 호출될 때, 그것은 포착된 연속을 복원하면서 call-with-current-continuation이나 call-with-composable-continuation을 호출할 당시에 있었던 연속 프레임의 마크들도 복원해요.
procedure
(continuation-marks cont [prompt-tag]) → continuation-mark-set?
cont: (or/c continuation? thread? #f)prompt-tag: continuation-prompt-tag? = (default-continuation-prompt-tag)
연속 cont(또는 cont가 스레드라면 그 스레드의 현재 연속)에서 prompt-tag라는 태그의 프롬프트까지, 모든 키에 대한 연속 마크 집합을 담은 불투명한 값을 돌려줘요. cont가 #f이면 결과 연속 마크 집합은 비어 있어요. cont가 탈출 연속이면(구획 연속·탈출 연속과 배리어 문서 참고), 현재 연속이 cont를 확장해야 하며, 그렇지 않으면 exn:fail:contract 예외가 발생해요. cont가 prompt-tag에 대하여 포착되지 않았고 prompt-tag에 대한 프롬프트를 포함하지 않으면 exn:fail:contract 예외가 발생해요. cont가 죽은 스레드이면 결과는 빈 연속 마크 집합이에요.
procedure
(current-continuation-marks [prompt-tag]) → continuation-mark-set?
prompt-tag: continuation-prompt-tag? = (default-continuation-prompt-tag)
prompt-tag까지의 현재 연속에서 모든 키에 대한 연속 마크 집합을 담은 불투명한 값을 돌려줘요. 즉 다음 표현식과 같은 값을 만들어내요.
(call-with-current-continuation
(lambda (k)
(continuation-marks k prompt-tag))
prompt-tag)
procedure
(continuation-mark-set->list mark-set key-v [prompt-tag]) → list?
mark-set: (or/c continuation-mark-set? #f)key-v: any/cprompt-tag: continuation-prompt-tag? = (default-continuation-prompt-tag)
mark-set에서 key-v에 대한 마크들을 담은 새로 만든 목록을 돌려줘요. mark-set은 current-continuation-marks가 돌려준 마크 집합이거나, (current-continuation-marks prompt-tag)의 축약형으로서의 #f예요. 결과 목록은 원래 연속 프레임들이 prompt-tag라는 태그의 프롬프트로 분리되었던 첫 지점에서 잘려요. 결과를 만드는 시간은 mark-set이 반영하는 연속의 크기에 비례해요.
procedure
(continuation-mark-set->list* mark-set key-list [none-v prompt-tag]) → (listof vector?)
mark-set: (or/c continuation-mark-set? #f)key-list: (listof any/c)none-v: any/c = #fprompt-tag: continuation-prompt-tag? = (default-continuation-prompt-tag)
mark-set에서 key-list의 키들에 대한 마크 벡터들을 담은 새로 만든 목록을 prompt-tag까지 돌려줘요. 여기서 mark-set의 #f 값은 (current-continuation-marks prompt-tag)와 동등해요. 결과 목록의 각 벡터 길이는 key-list의 길이와 같고, 특정 벡터 위치의 값은 key-list의 대응 키에 대한 값이에요. 여러 키에 대한 값들이 단일 벡터에 나타나는 것은 오직 그 마크들이 mark-set의 같은 연속 프레임에 대한 것일 때예요. none-v 인자는 값이 없음을 나타내는 벡터 요소로 쓰여요. 결과를 만드는 시간은 mark-set이 반영하는 연속의 크기 곱하기 key-list의 길이에 비례해요.
procedure
(continuation-mark-set->iterator mark-set key-list [none-v prompt-tag])
→ (-> (values (or/c vector? #f) procedure?))
mark-set: (or/c continuation-mark-set? #f)key-list: (listof any/c)none-v: any/c = #fprompt-tag: continuation-prompt-tag? = (default-continuation-prompt-tag)
continuation-mark-set->list*과 비슷하지만, 값 목록을 돌려주는 대신 값 목록의 한 요소와 나머지 목록을 위한 새 반복자 함수를 돌려주는 함수 형태의 함수형 반복자(funvtional iterator)를 돌려줘요. 더 이상 요소가 없으면 반복자 함수는 벡터 대신 #f를 돌려주고, 그 경우 반환된 반복자 함수는 호출된 것과 같아서 더 이상 값을 만들어내지 않아요. 각 단계에 필요한 시간은 key-list의 길이 곱하기 mark-set이 반영하는 연속에서 key-list의 키를 가진 프레임들 사이 세그먼트 크기에 비례해요.
procedure
(continuation-mark-set-first mark-set key-v [none-v prompt-tag]) → any
mark-set: (or/c continuation-mark-set? #f)key-v: any/cnone-v: any/c = #fprompt-tag: continuation-prompt-tag? = (default-continuation-prompt-tag)
(continuation-mark-set->list (or mark-set (current-continuation-marks prompt-tag)) key-v prompt-tag)가 돌려줄 목록의 첫 요소를 돌려주거나, 결과가 빈 목록이면 none-v를 돌려줘요.
결과는 (상각) 상수 시간에 만들어져요. 보통 continuation-mark-set->list나 continuation-mark-set->iterator를 한 번 반복하는 것보다 continuation-mark-set-first로 더 빨리 계산할 수 있어요.
mark-set에 대해 #f와 (current-continuation-marks prompt-tag)가 동등하지만, mark-set으로 #f를 주면 더 빨라지는 지름길을 사용할 수 있어요.
procedure
(call-with-immediate-continuation-mark key-v proc [default-v]) → any
key-v: any/cproc: (any/c . -> . any)default-v: any/c = #f
현재 연속의 첫 프레임에서 key-v와 연관된 값으로 proc을 호출해요(즉, call-with-immediate-continuation-mark의 호출을 key-v를 키 표현식으로 쓰는 with-continuation-mark 형식으로 바꾸면 대체될 값이에요). 첫 프레임에 그런 값이 없으면 default-v가 proc에 전달돼요. proc은 call-with-immediate-continuation-mark 호출에 대해 꼬리 위치에서 호출돼요.
이 함수는 아래처럼 with-continuation-mark, current-continuation-marks, continuation-mark-set->list*의 조합으로 구현할 수 있지만, call-with-immediate-continuation-mark는 현재 연속의 첫 프레임만 검사하기 때문에 더 효율적으로 구현돼요.
; 동등하지만 비효율적인 구현:
(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)))))
procedure
(make-continuation-mark-key) → continuation-mark-key?
(make-continuation-mark-key sym) → continuation-mark-key?
sym: symbol?
다른 어떤 값과도 equal?하지 않은 연속 마크 키를 만들어요(make-continuation-mark-key의 이전·이후 결과 포함). 연속 마크 키는 with-continuation-mark의 키 인자나 continuation-mark-set-first 같은 접근자 프로시저의 키 인자로 쓸 수 있어요. 마크 키는 마크 키로 쓰이는 다른 값들과 달리 체퍼론(chaperone)이나 임퍼서네이터(impersonator)로 감쌀 수 있어요.
선택적 sym 인자가 주어지면 연속 마크를 출력할 때 사용돼요.
procedure
(continuation-mark-key? v) → boolean?
v: any/c
v가 make-continuation-mark-key가 만든 마크 키이면 #t를, 그렇지 않으면 #f를 돌려줘요.
procedure
(continuation-mark-set? v) → boolean?
v: any/c
v가 continuation-marks나 current-continuation-marks가 만든 마크 집합이면 #t를, 그렇지 않으면 #f를 돌려줘요.
procedure
(continuation-mark-set->context mark-set [realms?]) → list?
mark-set: continuation-mark-set?realms?: any/c = #f
mark-set의 연속에 대한 대략적인 "스택 트레이스"를 나타내는 목록을 돌려줘요. realms?가 #f이면 목록은 페어들을 담는데, 각 페어의 car는 #f나 프로시저 이름 기호를 담고 cdr는 #f나 프로시저의 소스 위치 srcloc 값을 담아요(위치·줄·열 세기 문서 참고). car와 cdr가 둘 다 #f인 경우는 없어요. realms?가 참이면 목록은 3-요소 벡터들을 담는데, 처음 두 요소는 페어의 값들과 같고 세 번째 요소는 영역(리럄) 기호예요.
개념적으로 스택 트레이스 목록은 Racket의 프로시저 호출 마크용 개인 키로 continuation-mark-set->list를 호출한 결과예요. 다만 구현은 다를 수 있고 결과는 단지 정답에 근사할 뿐일 수 있어요. 따라서 결과가 표현식의 문맥에 대한 유용한 힌트를 사람에게 줄 수는 있어도, 프로그래밍 방식으로 쓰기에는 충분히 신뢰할 수 없어요.
스택 트레이스는 예외에서 추출되어, exn:fail:user가 아닌 예외(예외 발생 문서의 raise-user-error 참고)에 대해 기본 오류 표시 핸들러(error-display-handler 참고)가 표시해요.
예시:
> (define (extract-current-continuation-marks key)
(continuation-mark-set->list
(current-continuation-marks)
key))
> (with-continuation-mark 'key 'mark
(extract-current-continuation-marks 'key))
'(mark)
> (with-continuation-mark 'key1 'mark1
(with-continuation-mark 'key2 'mark2
(list
(extract-current-continuation-marks 'key1)
(extract-current-continuation-marks 'key2))))
'((mark1) (mark2))
> (with-continuation-mark 'key 'mark1
(with-continuation-mark 'key 'mark2 ; replaces previous mark
(extract-current-continuation-marks 'key)))
'(mark2)
> (with-continuation-mark 'key 'mark1
(list ; continuation extended to evaluate the argument
(with-continuation-mark 'key 'mark2
(extract-current-continuation-marks 'key))))
'((mark2 mark1))
> (let loop ([n 1000])
(if (zero? n)
(extract-current-continuation-marks 'key)
(with-continuation-mark 'key n
(loop (sub1 n)))))
'(1)