스폰서LobeHubLobeHub자세히 알아보기
dshfind

5강: 코이펙트: 계산이 환경에게 필요로 하는 것

한 줄 요약: 이펙트는 「프로그램이 세상에 무엇을 했는가」를 다루고, 코이펙트는 「세상이 프로그램에게 무엇에 의존할 것을 요구하는가」를 다룹니다. 코이펙트가 답하는 것은 「계산이 환경으로부터 무엇을 제공받아야 하는가」(접근해야 할 리소스, 갖춰야 할 능력, 의존해야 할 서비스)이며, 이펙트와는 방향이 반대인, 서로 쌍대인 개념입니다.

1. 쌍대로 여는 도입: 하나는 「내가 무엇을 바꿨는가」를 묻고, 다른 하나는 「내가 무엇을 필요로 하는가」를 묻습니다

먼저 두 가지 사고 실험을 합시다. 둘 다 여러분이 매일 겪는 일입니다.

배달 주문하기: 이것은 「이펙트」입니다. 여러분이 「주문하기」를 누륵면 세상은 정말로 바뀝니다. 가게가 조리를 시작하고, 라이더가 주문을 접수하고, 여러분의 지갑에서 돈이 빠져나가고, 길 위에 배달 상자가 하나 더 늘어납니다. 프로그램(이 주문 행위)이 세상에 영향을 미쳤고, 그 영향은 프로그램에서 세상으로 흐릅니다.

일하고 월급 받기: 이것은 「코이펙트」입니다. 여러분은 이제 막 입사한 프로그래머입니다. 자리, 컴퓨터, 네트워크, 데이터베이스 권한, 프로젝트 문서…… 어느 하나 여러분이 가져온 것이 없고, 전부 회사가 제공합니다. 이것들이 없으면 코드 한 줄도 쓸 수 없습니다. 월급은 말할 것도 없이 「세상이 여러분에게 주는 것」입니다. 여기서는 환경(회사)이 프로그램(여러분)에게 물건을 제공하는 것이며, 방향이 정확히 반대입니다.

이 두 예를 논문의 언어로 번역하면, 이번 강의의 핵심이 됩니다.

이펙트는 프로그램이 세상에 미치는 영향을 특징짓고, 코이펙트는 세상이 프로그램에 가하는 제약을 특징짓는다.

  • 배달 주문 = 프로그램이 세상을 바꿈 → 이펙트
  • 일하기 = 세상이 프로그램에게 리소스, 능력, 보수를 제공 → 코이펙트

그렇다면 「세상이 프로그램에 가하는 제약」은 구체적으로 어떤 모습일까요? 논문은 하나의 목록을 제시합니다. 그것은 계산이 자신의 환경에 요구하는 것을 기술하는데, 예를 들면 다음과 같습니다.

필요한 것프로그램에서의 모습
접근해야 할 리소스데이터베이스 연결, 파일, 메모리, 네트워크
갖춰야 할 능력어떤 권한, 하나의 키, 어떤 API를 호출할 자격
의존해야 할 서비스외부 시스템, 다른 플러그인이 제공하는 기능

타입 시스템 안에서 코이펙트는 이런 모습입니다. 일반적인 판단은 Γ ⊢ t : T(컨텍스트 Γ 아래에서 t의 타입은 T)입니다. 코이펙트 시스템은 판단을 Γcoeffect ⊢ t : T로 씁니다. 컨텍스트 Γ 자체가 「코이펙트 대수」의 원소로 주석이 달리고, 이 주석이 바로 「이 계산이 환경에 요구하는 것」입니다.

计算(组件/代码)环境(世界/上下文)我改了什么?余效应 · 需要什么给谁用?给我用效应修改世界余效应向世界索取

效应问「我改了什么」(对世界的影响);余效应问「我需要什么」(世界对我的约束)

위의 그림과 대조해 볼 때, 이 「쌍대」만 기억하면 충분합니다. 이펙트는 계산에서 세상을 가리키고(내가 무엇을 바꿨는가), 코이펙트는 세상에서 계산을 가리킵니다(내가 무엇을 필요로 하는가). 한쪽이 풍요롭게 하는 것은 타입이고, 다른 한쪽이 풍요롭게 하는 것은 컨텍스트입니다.

이펙트(effect)코이펙트(coeffect)
묻는 질문내가 무엇을 바꿨는가?내가 무엇을 필요로 하는가?
방향계산 → 세상세상 → 계산
특징짓는 것프로그램이 세상에 미치는 영향세상이 프로그램에 가하는 제약
풍요롭게 하는 것타입컨텍스트
일상의 예배달 주문: 주문 후 세상이 바뀐다일하기: 회사가 자리, 권한, 월급을 준다

2. 코모나드적 코이펙트: 프로그램은 「환경에 잠겨 있다」

지난 강의에서 우리는 모나드, 즉 「값을 상자에 넣는」 디자인 패턴을 만났습니다. 이번 강의의 쌍대는 **코모나드(comonad)**입니다. 모나드는 부수효과를 상자에 넣지만, 코모나드는 프로그램이 원래부터 컨텍스트(환경)에 잠겨 있다고 가정하고, 매 단계에서 환경으로부터 무언가를 「꺼낼」 수 있습니다.

논문에서 가장 직관적인 예는 환경 코모나드로, 다음과 같이 씁니다.

D(X) = E × X

이 공식을 두려워할 필요는 없습니다. 그것이 말하는 것은 사실 이것입니다. 모든 계산 X는 환경 E를 몸에 지니고 함께 달린다는 것입니다. 스마트폰 앱이 항상 「현재 시각」과 「현재 위치」라는 두 개의 환경 값을 갖고 다니며, 어느 단계에서든 그것들을 읽을 수 있는 것과 같습니다.

코모나드에는 두 가지 기본 동작이 있고, 직관적으로는 다음과 같습니다.

  • ε(꺼내기): 컨텍스트에서 현재 값을 추출합니다——「컨텍스트에서 현재 값을 꺼낸다」. 프로그램이 환경을 읽고 싶다고요? 손을 뻗으면 바로 있습니다.
  • δ(복제): 컨텍스트를 한 부 복사해서 중첩된 안쪽 계산에 걸려줍니다——「중첩된 접근을 위해 컨텍스트를 복제한다」. 안쪽 함수가 환경을 읽고 싶다고요? 환경이 그것을 따라갑니다.

논문은 또한 스트림 코모나드를 언급하는데, D(X) = ℕ → X로 씁니다. ℕ은 자연수(시점이라고 이해핼 수 있습니다)로, 어떤 시각을 주면 하나의 값을 반환합니다. 그것이 특징짓는 것은 시계열 데이터에 대한 의존입니다. 예를 들어 시계를 따라 움직여야 하는 컴포넌트나, 데이터 스트림을 따라 움직이는 컴포넌트 같은 것입니다.

🐟 비유를 들어봅시다. 모나드 안의 프로그램은 밀봉된 상자 같아서 세상을 밖에 닫아 두지만, 코모나드 안의 프로그램은 물고기 같아서 시시각각 물(환경)에 잠겨 있고, 숨을 쉴 때마다(계산의 매 단계마다) 물에서 산소를 얻습니다. 물고기가 어디로 헤엄쳐 가든 물은 따라갑니다. 이것이 「컨텍스트 의존 계산」입니다.

그래서 「코모나드적 코이펙트」의 한 줄 버전은 이렇습니다. 프로그램의 결과는 그것이 놓인 환경에 의존하며, 환경은 필요한 것을 스스로 걸려준다.

3. 그레이디드 코이펙트: 「얼마나 필요한가」에 무게 라벨을 붙이다

「프로그램이 환경에 의존한다」고만 말하는 것으로는 부족합니다. 얼마나 의존하는가도 중요합니다. 어떤 변수가 0번 쓰이는 것과 100번 쓰이는 것은 리소스 소모 면에서 하늘과 땅 차이입니다.

그레이디드 코이펙트(graded coeffects)의 방법은 모든 「요구」에 무게 라벨을 붙이는 것입니다. 논문에서 이 라벨 체계는 하나의 **반환(semiring)**이며, 다음과 같이 씁니다.

S = (S, ≤, +, ×, 0, 1)

기호에 겁먹지 마세요. 그냥 「덧셈, 뺄셈, 곱셈, 나눗셈이 가능한 계량 시스템」으로 이해하면 됩니다. 라벨에는 네 가지 의미가 있고, 아주 일상적입니다.

라벨의미비유
0미사용: 이 변수는 처음부터 끝까지 한 번도 쓰이지 않았다전원을 켠 적이 없는 가전제품
1선형 사용: 정확히 한 번 쓰였다일회용 식기, 쓰고 나면 버린다
n유계 사용: 최대 n번까지 쓰인다횟수가 정해진 체험권
무제한 사용: 몇 번이든 쓰고 싶은 대로 쓸 수 있다무제한 데이터 요금제

두 가지 연산은 두 가지 「결합 방식」에 대응합니다.

  • ×(곱셈): 순차 결합——A를 먼저 끝내고 B를 하면, 무게는 곱해집니다. 예를 들어 「파일을 한 번 읽고, 다시 한 번 읽는다」는 1 × 1입니다.
  • +(덧셈): 병렬 결합——A와 B가 동시에 진행되면, 무게는 더해집니다.

💡 비유를 들어봅시다. 집에 수도·전기 계량기를 달아 둔 것과 같습니다. 「이 코드는 이 파일을 최대 n번까지 읽는다」라고 선언하면, 시스템은 실행하기 전에 초과가 없는지 검사할 수 있습니다. 두 작업이 동시에 돌아가면, 시스템은 그 사용량을 합산해 총 결산을 합니다.

이 「계량 시스템」이 있으면, 논문에 따륵면 하나의 통일된 대수적 프레임워크 안에서 이전에는 할 수 없던 몇 가지 일을 할 수 있습니다.

  1. 정밀한 리소스 추적——이 변수가 도대체 몇 번 쓰였는지, 조회하면 바로 압니다.
  2. 민감도 분석——누구의 입력이 누구의 출력에 영향을 미치고, 그 영향이 얼마나 큰지 한눈에 압니다.
  3. 정보 흐름 제어——민감한 데이터가 안전하지 않은 곳으로 흘러갈 수 있는지, 전 과정을 검사할 수 있습니다.

한마디로, 그레이디드 코이펙트 = 코이펙트 + 사용량 장부이며, 「무엇이 필요한가」를 「얼마나 필요한가」로 업그레이드하는 것입니다.

4. 정적에서 동적으로: 컴포저빌리티와의 관계

이제 이 논문이 진정으로 관심을 갖는 문제로 돌아갑시다. 동적 컴포저빌리티입니다(기억하시나요? 컴포넌트는 장착했다가 떼어낼 수 있고, 떼어낸 뒤 흔적이 남지 않는다). 논문의 2.3절은 이펙트와 코이펙트를 컴포저빌리티의 두 차원에 각각 대응시킵니다.

  • 시간적 컴포저빌리티 → 상태를 가진 이펙트. 시간적 컴포저빌리티는 「컴포넌트가 공유 환경에 가한 변경이, 컴포넌트 언인스톨 시 가역적이어야 한다」를 요구합니다. 환경을 진정으로 지속적으로 바꾸는 것은 상태를 가진 이펙트(파일 쓰기, 데이터베이스 변경)입니다. 이런 변경을 가역적으로 만들려면, 그 변환이 반드시 역변환이 존재해야 합니다(취소할 수 있어야 합니다).
  • 공간적 컴포저빌리티 → 코이펙트. 공간적 컴포저빌리티는 「컴포넌트 간의 의존을 선언하고, 리액티브한 방식으로 관리한다」를 요구합니다. 이러한 의존은 바로 코이펙트가 포착하는 내용입니다. 의존을 관리한다는 것은, 환경이 실제로 제공하는 것에 근거하여 각 의존을 하나씩 해석해 내는 것입니다.
컴포저빌리티 차원묻는 질문대응하는 개념
시간적 컴포저빌리티언인스톨 후에 환경이 복원될 수 있는가?상태를 가진 이펙트(변경에는 역변환이 있어야 함)
공간적 컴포저빌리티컴포넌트는 무엇에 의존하는가? 누가 제공하는가?코이펙트(의존 선언 + 리액티브한 해석)

그렇다면 문제입니다. 고전적인 이펙트 시스템, 코이펙트 시스템이 동적 조합을 곧바로 뒷받침할 수 있을까요?

논문의 답은 단호합니다. 없습니다. 그것들은 정적인 도구이기 때문입니다.

  • 이펙트는 어휘적으로 고정된 스코프 안에서 추적되고, 컴파일 시점의 핸들러에 의해 제거됩니다.
  • 코이펙트 주석은 실행 전에 이미 확정된 컨텍스트에 근거해 검증됩니다.

이 두 가지는 「세상은 컴파일 시점에 확정된다」라는 가정 아래에서는 성립하지만, 동적 조합의 세상은 그렇지 않습니다.

정적 시스템의 가정동적 조합의 현실
스코프는 어휘적으로 박혀 있다플러그인은 배포된 후에야 로드되므로, 고정 스코프로는 그것들을 가둘 수 없다
컨텍스트는 컴파일 시점에 확정할 수 있다의존은 런타임 설정에서 생겨날 수 있어, 컴파일 시점에는 도저히 예견할 수 없다

⚠️ 그래서 논문은 여기서 관점을 전환합니다. 더 많은 주석을 추가해 정적 타입 시스템을 확장하는 대신, 이펙트와 코이펙트의 개념적 구조를 구체화(reify)하여 런타임이 그것들을 직접 조작할 수 있게 합니다. 그리하여 이 시스템들이 정적인 경우에 제공하던 보장을, 런타임에 동적으로 세워 올리는 것입니다.

이것이 「컴파일 시점의 증명」에서 「런타임의 메커니즘」으로의 결정적인 도약입니다. 어떻게 구체화하고, 어떻게 런타임이 조작하게 할 것인지는 논문 제3절의 내용이니, 다음 강의에서 만납시다.

핵심 정리

이번 강의는 정보가 적지 않지만, 이 다섯 문장만 기억하면 충분합니다.

  1. 쌍대: 이펙트는 「내가 무엇을 바꿨는가」를 묻고(프로그램이 세상에 미치는 영향), 코이펙트는 「내가 무엇을 필요로 하는가」를 묻습니다(세상이 프로그램에 가하는 제약). 배달 주문은 이펙트이고, 일하고 월급 받기는 코이펙트입니다.
  2. 코이펙트가 주석을 다는 것은 컨텍스트: Γcoeffect ⊢ t : T에서 컨텍스트는 코이펙트 대수의 원소로 주석이 달리며, 계산이 환경에 요구하는 것——접근해야 할 리소스, 갖춰야 할 능력, 의존해야 할 서비스——을 기술합니다.
  3. 코모나드 = 환경에 잠긴 계산: 환경 코모나드 D(X) = E × X는 모든 계산이 환경을 지니고 달린다는 것을 나타냅니다. ε는 환경에서 현재 값을 꺼내고, δ는 환경을 복제해 중첩된 계산에 걸려줍니다.
  4. 그레이디드 코이펙트 = 사용량 장부: 반환 S = (S, ≤, +, ×, 0, 1)의 라벨이 사용을 정량화합니다——0은 미사용, 1은 선형, n은 유계, ∞는 무제한. ×는 순차 결합, +는 병렬 결합으로, 리소스 추적, 민감도 분석, 정보 흐름 제어를 뒷받침합니다.
  5. 정적에서 동적으로: 시간적 컴포저빌리티는 상태를 가진 이펙트(가역적 변경)에 대응하고, 공간적 컴포저빌리티는 코이펙트(의존)에 대응합니다. 정적 시스템은 런타임에 로드되는 플러그인을 가둘 수 없으므로, 논문은 이펙트/코이펙트를 런타임이 직접 조작할 수 있는 메커니즘으로 끌어올립니다.

🚀 다음 강의에서는 논문 제3절의 첫 번째 주인공인 롤백 가능한 이펙트에 들어갑니다. 「장착할 수 있고, 떼어낼 수 있다」. 이펙트가 어떻게 런타임에 진정으로 취소될 수 있을까요? 그것이 바로 시간적 컴포저빌리티에 대한 답입니다.

자가 테스트 · 코이펙트

답을 모두 선택한 후 「답안 제출」을 클릭하면 정답 여부와 해설을 확인할 수 있습니다.

1. 코이펙트가 답하는 핵심 질문은 무엇입니까?
2. 이펙트와 코이펙트의 쌍대 관계에 대해 옳은 설명은 무엇입니까?
3. 「일하고 월급 받기」라는 일상의 예는 어느 개념에 더 가깝습니까?
4. 공간적 컴포저빌리티에 대응하는 것은 다음 중 어느 개념입니까?