본문 바로가기

프로그래밍 기술 노트/Functional Study

Delimited Continuations 가 대체 뭔데?

본 포스팅은 후속문 자체의 정의와 설명에 관한 내용이 아니므로

후속문의 개념은 너무나도 잘 쓰여진 아래 링크 참조 (거의 유일한 한글자료이자 아주 상세하게 쓰여진 자료)
Computat ergo est: 후속문(Continuation) : 제1부. 개념과 call/cc (guruma.github.io)

 

Delimited Continuations 이 무엇인지 파악하기 위하여 Continuations(후속문) 을 개념적인 Level 에서 훑어 보자

 

CPS (Continuations Passing Style)

흔히 callback 이라고 부르는 방식이다.

Continuations (후속 작업을) Passing(넘기는) Style(스타일)

 

First-Class Continuation

후속문 자체를 1급 시민으로 다룰수 있으면  그 언어는 First-Class Continuation 을 지원한다고 볼수있다.

 

그럼 여기서 거의 모든 언어가 CPS 를 지원하니까 전부 First-Class Continuation 인가? 하면 아니다.

우리는 Function을 1급 시민으로 다룬것 뿐이다. 후속문 그 자체를 다루지는 않았다.

 

Continuation

후속문은 다음에 할 작업이다.

  • If문은 A/B 후속문중 하나만 선택하는것이다.
  • Error 발생 - Catch 는 아래 있는 모든 후속문을 무시하고 Catch 안의 내용을 후속문으로 선택하는것이다
  • For 문은 같은 후속문을 반복해서 선택하는것이다.
  • Break 는 반복작업할 후속문은 무시하고 For문 밖의 후속문을 선택하는것이다.

즉 CPS 는 Function 이 끝나면 호출될 다음 후속문을 Function으로 넘긴것뿐이다.

 

후속문을 다룬다는건 무엇을 의미하는가?

제어 흐름방식 그자체를 다룬다는것을 의미한다.

 

예를 들어보면 다음과 같은 언어가 존재한다고 해보자 이름은 흠.. NoAbort Lang 이다

  • For/ForEach 문이 존재함
  • If 문이 존재함
  • Break 문 없음
  • Return 문 없음

위와 같은 언어에서

Int 형 List 를 받고 List 를 처음부터 끝까지 순회하면서 출력하는데, 순회하다가 10 이라는 Item 을 만나면 즉시 종료하는 함수를 짠다고 해보자

다른 언어라면 이렇게 짤수있다.

for(var item in items) {
    if (item == 10) {
        return // 혹은 break
    }
    println(item)
}

 

하지만 우리 NoAbort Lang 언어에서는 break 도 없고 return 도 없다.

따라서 저런 전통적인 방법으로 구현하는게 불가능하다. 

하지만 만약 NoAbort Lang 이 First-Class Continuation 인 언어라면?

개발자가 Break나 Return 에 해당하는 Continuation 제어 구조를 직접 구현해서 중간에 abort 시키는것이 가능하다.

// 대충 이런식..
with-current-continuation (return) {
for(var item in items) {
    if (item == 10) {
        return // 혹은 break
    }
    println(item)
}
}

 

call/cc

First-Class Continuation 인 경우 거의 대부분 위 코드의 with-current-contiunation 같은 게 존재한다.

그걸 거의 모든 경우 call/cc 라고 한다 (call/with-current-contiunation)

call/cc 를 이용하여 후속문을 First-Class Continuation 로 다룰수있는것

 

Context

후속문을 컨트롤할때 중요한것은 Context 이다. 간단하게는 Call Stack 이라고 보면 되고

기본적으로는 Call Stack Context 는 1개 뿐이다.

함수 콜이 되면 Call Stack 에 해당 함수에 대한 Layer 가 쌓이는것 뿐이다.

Error 가 발생하고 Catch 가 되는 경우, 스택 되감기를 통하여 Catch 까지 타고 올라가고 Catch 안의 내용이 다음 후속문으로 선택되는것이다. 즉 

  • (A - B) : A 함수에서 B 호출
  • (A - B - C) : B 함수에서 C 호출
  • (A - B - C) : C 함수에서 Exception 발생
  • (A - B) : 스택 되감기중
  • (A) : A 에서 Catch 
  • (A) : Catch 할때에는 C 의 Context 는 몰?루?, 아는것은 오로지 넘겨받은 Exception e 뿐

이것이 일반적인 프로그램이 돌아가는 구조이다.

 

반면 후속문을 call/cc 로 컨트롤하게 되는 경우는 다르다.

call/cc 를 이용하면 해당 후속문에 해당하는 Context 로 완전 대체 된다.

  • (A - B) : A 함수에서 B 호출
  • (A - B - C) : B 함수에서 call/cc 로 후속문 선택
  • (I' - J' - K'): 선택된 후속문의 Context 로 완전 대체

여기까지가... 기존에 학습한 Continuation 내용이다.

 

Delimited Continuation

구분된 후속문이 대체 뭔데?

language agnostic - Difference between delimited and undelimited continuations - Stack Overflow

scheme - What exactly is a "continuation prompt?" - Stack Overflow

간단하게 말하자면, Context 가 완전 대체 되는것이 아니라, 내 원하는 만큼만 구분되서 (Delimited) 컨트롤할수 있는 후속문을 의미한다.

즉 Context 를 일부만 캡쳐한다

  • (A - B) : A 함수에서 B 호출
  • (A - B - C) : B 함수에서 Delimited Continuation 선택
  • (A - B - I' - J'):  구분된 Context 만 대체 (혹은 사용)

예를 들어 코루틴은 일반적으로 진입점이 여러개인 서브루틴이고, yield로 기존 중지점부터 시작한다 (=> 코루틴별 Context 가 있음)

  • (A - B) : A 함수에서 B 호출
  • (A - B - C1.1) : B 함수에서 C1 코루틴 선택
  • (A - B):  C1 Yield
  • (A - B - C2.1):  C2 코루틴 선택
  • (A - B):  C2 Yield
  • (A - B - C1.1 - C1.2) : B 함수에서 C1 코루틴 선택
728x90