가변·별칭 상태의 해악을 짚고, OOP와 순수 함수형/모나드의 시도, Rust의 수명·차용 규칙과 구조적 동시성, 값 의미론과 Hylo까지 대안을 비교한다.
고급 언어에서 프로그래머는 실행 중인 자신의 프로그램을 갱신하는 위험한 능력을 박탈당한다. 더 가치 있게도, 그는 자신의 기계를 여러 개의 독립된 변수, 배열, 파일 등으로 분할할 수 있는 능력을 갖는다. 이들 중 어떤 것을 갱신하려면 대입문의 왼쪽에 그 이름을 명시해야 하므로, 변경의 대상이 되는 기계의 부분이 무엇인지 즉시 분명해진다. 마지막으로, 고급 언어는 모든 변수가 서로 분리되어 있음을 보장할 수 있으며, 그 중 하나를 갱신하더라도 다른 어떤 변수에도 영향을 미칠 수 없음을 보장할 수 있다.
불행히도, 이러한 이점들 중 많은 부분이 ALGOL 60과 다른 언어들의 프로시저와 매개변수 설계에서는 유지되지 않았다. 그러나 이런 사소한 결함을 고치기는커녕, 많은 언어 설계자들은 참조(reference), 포인터(pointer), 간접 주소(indirect address)라는 개념을 언어에 대입 가능한 데이터 항목으로 도입함으로써 이러한 결함을 언어 전체로 확장하는 길을 택했다. 이것은 즉시 고급 언어에 기계어의 가장 악명 높은 혼동 중 하나, 즉 주소와 그 내용 간의 혼동을 야기한다. 일부 언어는 더더욱 혼란스러운 자동 강제(coercion) 규칙으로 이를 해결하려 한다. 설상가상으로, 기계어에서와 마찬가지로 포인터를 통한 간접 대입은 어떤 저장소 위치든 업데이트할 수 있으므로, 피해는 더 이상 대입 대상(target)으로 명시된 변수에 한정되지 않는다…
다른 모든 값(정수, 문자열, 배열, 파일 등)과 달리, 참조는 프로그램의 특정 실행(run)과 독립적인 의미를 갖지 않는다. 그것들은 데이터로 입력될 수도 없고, 결과로 출력될 수도 없다. 데이터나 데이터에 대한 참조를 파일이나 보조 저장소에 저장해야 한다면, 문제는 엄청나게 복잡해진다. 그리고 많은 기계에서 이것들은 성능에 놀랄 만한 오버헤드를 야기한다. 예컨대 명령어 파이프라인, 데이터 선판독(lookahead), 슬레이브 스토어, 심지어 페이징 시스템까지 막아버릴 것이다. 참조는 점프와 같아서, 데이터 구조의 한 부분에서 다른 부분으로 난폭하게 이끈다. 고급 언어에 그것들을 도입한 것은 우리가 결코 회복하지 못할 수도 있는 퇴보였다.
— C.A.R. Hoare, Hints on programming-language design 1974
소프트웨어 개발 실천에 있어 얼마나 난처한 일인가. 참조라는 주제에 관해 우리는 지난 반세기 동안 토니 경이 옳았음을 입증하는 거대한 실물 증거를 만들어냈다. 널 포인터는 그의 수십억 달러짜리 실수였을지 몰라도, 포인터 일반의 문제에 대한 그의 발언을 무시하기로 한 결정은 다른 모든 이들이 저지른 수조 달러짜리 실수였다.
토니 호어가 “참조는 점프와 같다”고 말했을 때 지적하고자 했던 것은 가변이면서 별칭된 상태(mutable, aliased state)의 문제였다. 어떤 언어가 두 변수가 같은 메모리 위치를 가리키도록 별칭(alias)을 만들 수 있게 하고, 또 실행이 진행되며 변수에 값을 대입할 수 있게 한다면, 시스템 구성요소의 거동을 지역적으로(reason locally) 추론하는 능력은 심각하게 저해된다. 사용자가 실수로 가변 별칭 상태를 만들지 못하도록 하는 것은 사용자가 올바르게 작동하는 시스템을 쉽게 구축하도록 하는 데 결정적으로 중요하다.
이런 취지의 글을 여기저기 악명 높은 인터넷 포럼에 쓰면, 때때로 가장 불신에 찬 반응을 받는다. 가변 별칭 상태 없이는 아무 일도 해낼 수 없다고! 이는 프로그램에서 GOTO 연산자의 사용을 줄여야 한다고 제안되었을 때 일부 프로그래머가 보였던 반응과 매우 비슷하다. 토니 호어가 옳았다는 또 다른 증거다. 참조는 점프와 같다. 이런 불신은 나를 별로 흔들지 못한다. 이것에 대해서는 내가 옳다는 확신이 있기 때문이다.
Rust(러스트)의 전설은 바로 이 문제를 해결하기 위한 타입 시스템을 실전에 내놓았다는 데서 비롯한다. Rust에서 참조는 가변이거나 별칭될 수는 있지만, 동시에 둘 다일 수는 없다. 수명(lifetime) 변수와 차용(빌림) 분석 전체가 이를 보장하도록 설계되었다. 하지만 Rust가 개발된 것은 고작 10년 전이다. 그 전 40년 동안 우리는 무엇을 하고 있었을까?
이 문제를 해결하려는 첫 번째 주요 시도는 객체지향이었다. 객체는 코드와 데이터를 함께 결합하고, 객체지향에 관해 내세운 약속 중 하나는 변화하는 상태를 “캡슐화”하는 추상화를 만들 수 있게 해준다는 것이었다. 처음에는 이것이 유망한 해법처럼 보였음이 분명하다. 토니 호어 자신도 1972년 편저 Structured Programming 에서 이를 지지했는데, 이 책은 에츠허르 디익스트라의 유명한 텍스트, 기본 타입 검사에 관한 호어 자신의 글, 그리고 최초의 객체지향 언어인 심룰라(Simula)에 관한 O.J. 달의 노트를 묶은 것이다( PDF는 여기 ). 호어가 서문에서 쓴 말은 이 기법에 대한 그의 낙관을 보여준다.
세 번째 모노그래프[Simula에 관한]는 앞선 두 편을 종합하여, 데이터 설계와 프로그램 설계 사이의 밀접한 이론적·실천적 연관성을 펼쳐 보인다. 이는 많은 프로그래머들에게는 생소할 수도 있는, 프로그램과 데이터 구조화에 유용한 추가 기법들을 소개한다.
하지만 문제는, 캡슐화된 별칭 가변 참조도 여전히 별칭 가변 참조라는 점이다. 객체는 자신의 상태를 갱신하는 메서드를 가지며, 그 객체는 또 다른 객체들에 대한 별칭 참조들로 구성되고, 수시로 그 메서드들을 호출한다. 그래서 조 암스트롱의 다음과 같은 말이 나온다.
객체지향 언어의 문제는 그들이 온갖 암묵적 환경을 주렁주렁 달고 다닌다는 거야. 너는 바나나를 원했지만, 네가 손에 넣은 건 바나나를 들고 있는 고릴라와 정글 전체였지.
한 별칭 참조에서 메서드를 호출해 객체를 갱신할 수 있는 동안, 다른 별칭 참조는 그 메서드에 의해 위배되는 불변식에 의존하고 있을 수도 있다. 그러면 일명 “먼 거리의 유령 같은 작용(spooky action at a distance)”이 발생한다.
소프트웨어 개발이야 기껏해야 준과학적이니, 객체지향 추상이 목표 달성에 실패했다고 해서 그것이 지배적 실천이 되는 것을 막지는 못했다. 하지만 그 한계는 학계에서 일찍이 인식되었고, 이는 순수 함수형 프로그래밍에 대한 관심의 성장으로 이어졌다. 이 접근은 언어에서 변이를 아예 제거함으로써 공유 가변 상태의 문제를 해결했다. 상태를 얼마든지 별칭할 수는 있지만, 대입이 없다면 상태를 결코 변경할 수 없다. 이는 호출-시점-평가(call-by-need, 지연 평가)를 가능케 했고, 값이 실제로 필요할 때만 함수를 호출하게 했다(무한 함수를 멋진 의사코드로 시연하는 데 특히 유용했다). 또한 변이가 없으므로 많은 경쟁 상태가 자동으로 제거되어, 동시성 문맥에서 실제로 가치가 있었다.
그러나 많은 해 동안, 함수형 언어 연구자들은 실제로 IO를 수행하고 자신 바깥의 시스템과 상호작용하는 전체 프로그램을 어떻게 작성할지 애를 먹었다. 초기 시도는 지연 스트림에 기반했는데, 프로그램을 입력 스트림에서 출력 스트림으로 가는 함수로 모델링했다. 바로 이 시기에 유명한 Structure and Interpretation of Computer Programs (SICP)가 쓰였다. 그 3장 “모듈성, 객체, 상태”는 상태 있는 객체에 기반한 시스템과 상태 없는 스트림에 기반한 시스템을 구성해 비교·대조한다. 이 텍스트는 컴퓨터 프로그램 작성자로서 나의 성장에도 큰 영향을 미쳤다.
장 말미에서, SICP는 비록 스트림이 프로그램을 상태 없는, 무한히 전개되는 스트림의 순수 함수로 모델링하더라도, 프로그래머가 두 스트림을 합치려 할 때 상태의 문제가 다시금 떠오른다고 지적한다.
이 정식화의 문제는 ‘merge(병합)’라는 개념에 있다. 피터와 폴의 요청을 하나씩 번갈아 가져오는 방식으로 두 스트림을 병합하는 것으로는 충분치 않다. 폴이 아주 드물게만 계정에 접근한다고 하자. 우리가 피터에게, 폴이 접근할 때까지 기다렸다가 두 번째 거래를 제출하라고 강요할 수는 없다. 병합이 어떻게 구현되든, 이는 피터와 폴이 지각하는 ‘실시간’에 의해 제약되는 어떤 방식으로든 두 거래 스트림을 인터리브(interleave)해야 한다. 다시 말해, 피터와 폴이 만나면 그들은 어떤 거래는 만남 이전에 처리되었고, 다른 거래는 만남 이후에 처리되었다는 데 동의할 수 있어야 한다. 이는 정확히 3.4.1절에서 우리가 다뤄야 했던 동일한 제약으로, 상태를 가진 객체들의 동시 처리에서 ‘올바른’ 사건의 순서를 보장하기 위해 명시적 동기화가 필요함을 발견했었다. 따라서 함수형 스타일을 지지하려는 시도에서, 서로 다른 행위자로부터의 입력을 병합할 필요가 함수형 스타일이 제거하고자 했던 바로 그 문제를 다시 도입한다.
우리는 이 장을, 우리가 모델링하려는 현실 세계에 대한 인식과 구조가 일치하는 계산 모델을 구축하려는 목표로 시작했다. 우리는 세계를 상태를 가진, 분리되고 시간에 묶인 상호작용하는 객체들의 집합으로 모델링할 수도 있고, 시간도 상태도 없는 단일한 통일체로 모델링할 수도 있다. 각 관점은 강력한 장점을 지니지만, 어느 하나만으로는 완전히 만족스럽지 않다. 위대한 통일은 아직 나타나지 않았다.
SICP의 저자들만이 지연 스트림 추상의 한계를 인식한 것이 아니었다. 무엇보다도 사이먼 페이턴-존스와 필립 와들러는 IO에 대한 대안적 메커니즘인 IO 모나드를 발표했다. 지연 스트림과 달리, 모나딕 IO 인터페이스는 합성 가능성이 훨씬 좋았고, 주요 순수 함수형 언어인 Haskell 주변 커뮤니티에서 큰 열의로 채택되었다. 궁극적으로 “부수 효과”를 표현하기 위해 모나딕 인터페이스를 사용하는 개념은 다른 방식에도 적용되었는데, 예를 들어 가변 상태에 대한 갱신의 시퀀싱을 명시적으로 표현하는 State 모나드가 그렇다.
모나드를 채택함으로써, 순수 함수형 프로그래밍 커뮤니티는 마침내 공유 가변 상태의 문제를 방지하는 언어로 실용적인 시스템을 구축할 수 있게 되었다. 이 돌파구가 곧바로 순수 함수형과 모나딕 상태/IO 관리의 세계 제패로 이어질 것이라고 상상할 수도 있겠다. 하지만 그렇게 되지는 않았다. 왜 그런지에 대해 몇 가지 이유를 제시할 수 있다.
한 가지 이유는 이게 정말로 별 문제 아니라는 것이다. 누가 가변 별칭 상태 따위를 신경 쓰느냐는 것이다. 앞서 언급한 나의 비평가들이 내놓을 답이 바로 이것이다. 이에 나는 이렇게 답할 수밖에 없다. 당신은 매일 사용하는 소프트웨어의 품질에 만족하는가? 시스템 내부를 모르는 사용자 입장에서도 분명히 드러나는, 상태 관리 문제에서 비롯된 이해할 수 없는 버그나 형편없는 성능을 전혀 겪지 않는가? 그렇다면 당신의 경험은 내 것과 매우 다르다.
또 다른, 더 그럴듯한 답은 좋든 싫든 아무도 모나드가 뭔지, 그것을 가지고 무엇을 해야 하는지 이해하지 못한다는 것이다. 어느 정도는 모나드 옹호자들의 책임이다. 그들은 비입문자에게 설계를 더 분명히 설명하려 하기보다는, 진정한 신자 말고는 아무도 설득하지 못하는 범주론의 경이로운 힘을 숭배하는 사이비 집단 같은 태도로 빠져들었다. 또 어느 정도는, 재귀와 고차 함수의 매혹적인 우아함에도 불구하고, 대부분의 사람들에게는 상태를 갱신하는 코드보다 이런 코드가 실제로 더 이해하기 어렵다고 주장할 수도 있다. 게다가, 본질적으로 명령형·상태 기반의 기계 위에서 순수 함수형 언어를 구현하는 성능 비용은 차치하고도 말이다.
바로 이 마지막 측면(고성능 달성)이 물론 Rust의 설계를 동기부여했다. 역사는 다소 우회적이다. 초기 Rust 설계의 성능 목표는 최종적으로 1.0으로 선적된 Rust의 성능 목표와 달랐지만, 공유 가변 상태의 사용을 제한한다는 약속은 언제나 있었다. 그레이던에게서, 이 글 서두에 인용한 토니 호어의 에세이가 Rust의 방향에 영향을 준 논문 모음집에 들어 있었다는 말을 들었다. Rust가 궁극적으로 발전시킨 시스템—수명, 차용, 소유권 등—은 Mozilla가 Rust의 초점을, Mozilla에서 주로 쓰이던 언어인 C++을 대체할 언어로 전환하도록 요구한 데서 비롯되었다. 컴파일 타임에 공유 가변 상태를 금지할 수 있다면, 데이터 레이스를 방지하고 “RAII”를 구현하며 가비지 컬렉션 사용을 피하기가 훨씬 쉬워진다는 사실이 드러난 것이다.
이 역사에서의 하나의 특이점은, Rust가 수명을 타입 검사하는 시스템을 채택했지만 여전히 충분한 양의 타입 미지정 IO 부수효과를 가진 불순(impure) 언어라는 점이다. 또 하나의 특이점은, Rust가 RefCell과 Mutex 같은 이름으로 런타임 검사 기반의 별칭 가변 상태에 대한 견고한 설비를 갖추었다는 점이다. 이 타입들은 상태가 갱신될 때마다 원자적으로 배타적 접근을 보장하면서도, Rust의 타입 시스템과 통합되어 공유 상태를 가능케 한다. 하지만 이런 것들의 존재는 Rust가 이와 관련한 모든 버그를 방지하지는 못한다는 뜻이기도 하다. 예컨대 RefCell의 경우 런타임 패닉이 여전히 발생할 수 있고, Mutex의 경우 교착 상태의 가능성이 남는다.
여기에서 Rust 역사상 가장 불운한 에피소드 중 하나, 일명 leakpocalypse(리크포칼립스)를 잠시 돌아볼 가치가 있다. 1.0 이전, Rust의 표준 라이브러리에는 다음과 같은 흥미로운 함수가 있었다. 이는 두 번째 스레드를 생성하되, 그 스레드가 자신을 생성한 스레드의 스택에 있는 값들에 대한 참조를 가져갈 수 있도록 허용했다.
fn scoped<'a, T>(f: impl FnOnce() -> T + Send + 'a) -> JoinGuard<'a, T>
이 API는 JoinGuard의 소멸자에서 블록함으로써 작동했는데, 이렇게 해서 이 스레드가 다른 스레드가 빌린 값을, Rust의 가변/별칭 상태에 대한 보장을 위배하는 방식으로 접근하지 못하도록 했다. 문제는 Rust의 타입 시스템이 JoinGuard의 소멸자가 반드시 호출된다는 것을 보장하지 않는다는 점이었다. 따라서 JoinGuard를 “누수”시켜 소멸자를 실행하지 않는 것이 완전히 가능했고, 이는 사용자가 Rust의 보장을 위배하도록 만들었다. 더 나쁜 점은, 이 API의 건전성(soundness)에 난 구멍이 Rust 1.0 릴리스 예정일 약 한 달 전에 발견되었다는 것이다.
팀은 앞으로의 경로를 제시받았다. 하나는 이 API가 건전하지 않다고 선언하고, Rust에서는 누수될 수 없는 타입을 갖는 것이 불가능하다고 말하는 것이었다. 다른 하나는, Send(다른 스레드로 보낼 수 없는 타입)와 Sync(다른 스레드가 빌릴 수 없는 타입) 같은 마커 트레이트와 유사하게, 안전하게 누수시킬 수 없는 타입을 위한 새로운 마커 트레이트를 추가하는 것이었다. 팀은 궁지 속에서 덜 침습적이고 구현하기 쉬워 보였던 옵션(이 API 제거)을 결국 선택했다. 이 결정을 뒷받침한 것은 클로저를 기반으로 한 스코프드 스레드용의 알려진 대체 API가 있었다는 사실이었다. 이것이 현재 표준 라이브러리에 존재하는 API가 되었다.
안타깝게도, 스코프드 스레드를 건전하게 만들 때 쓴 요령은 스코프드 비동기 작업(tasks)에는 통하지 않았다. 이는 많은 곤혹을 낳았다. 이 근거로 볼 때, “리크포칼립스”를 수습하는 압박 속에서 내린 결정은 분명 잘못이었다. 하지만 당시엔 아무도 몰랐다. 역사적 관점에서, 당시 의사결정 과정의 매우 빗나간 측면을 나는 강조하고 싶다. 스코프드 스레드는 “재미있는 트릭”으로 프레이밍된 반면, Mutex 같은 동적 잠금 메커니즘은 스레드 간 상태를 공유하는 주된 방법으로 인식되는 경향이 있었다. 이런 생각이 틀렸다고 본다. 스코프드 스레드는 Rust 타입 시스템에서 가장 흥미로운 부분이었고, 스코프드 비동기 태스크는 더더욱 흥미로울 것이다. 사용자는 동적 락(과 그에 따른 교착 가능성) 을 사실상 제거할 수 있다. 이것이 바로 오늘날 구조적 동시성(structured concurrency) 이라 불리는 것이며, Rust의 타입 시스템과 결합하면 강력한 시스템 구현 방법이 된다. Rust가 이 실수를 반드시 고쳐서, 말 그대로 ‘잠금을 풀어’(unlock) 시스템 언어로서의 잠재력을 온전히 발휘할 수 있도록 해야 한다고 생각한다.
이제 마지막 요점으로 넘어가자. 수명 규칙은 대중의 인식 속에서 흔히 “가비지 컬렉션을 피하는 방법”으로 틀지워지지만, 실상 그것은 훨씬 더 깊고 중요한 구성물이다. 수명 규칙은 가변 상태와 별칭 상태를 동시에 허용하는 언어에서, 상태가 변경되는 동안에는 별칭되지 않도록 보장함으로써, 프로그래밍을 감당할 만하게 만드는 방법이다. 이는 시스템의 거동을 이해하는 데 놀라울 만큼 강력한 도구다. 시스템의 거동을 지역적으로 분석할 수 있기 때문이다. “먼 거리의 유령 같은 작용”을 걱정할 필요가 결코 없다. 그리고 모나드로 명령형 제어 흐름을 수작업으로 재구성하지 않아도 된다. 내가 여러 번 썼듯이: 모나드는 변이 없이도 프로그래밍할 수 있음을 영리하게 보여주는 방법이고, 수명은 그냥 변이를 써도 됨을 더 영리하게 보여주는 방법이다.
안타깝게도, 대부분의 사람들은 Rust에서 잘못된 교훈을 얻은 듯하다. 그들은 수명과 소유권에 관한 모든 것이 가비지 컬렉션을 피하고자 Rust가 어쩔 수 없이 채택한 더러운 짐으로 보인다. 하지만 이는 완전히 거꾸로다! Rust는 공유 가변 상태에 관한 규칙을 채택했고, 그 덕분에 가비지 컬렉션을 피할 수 있었다. 이 규칙들은 그 자체로도 좋은 생각이다.
Rust의 기법들에 대해 널리 배포된 가장 유망한 대안은 “값 의미론(value semantics)” 개념이다. 이는 크리스 래트너가 개발한 Swift와 Mojo 같은 언어가 채택했다. 이 모델에서는 어떤 값을 변경할 때 그 값이 별칭되어 있다면, 별칭된 값이 당신의 변이로 인해 바뀌지 않도록 값을 복사한다. 이는 어느 정도의 성능 오버헤드를 수반하지만, 데이터 타입 설계를 영리하게 하면 줄일 수 있다. 래트너의 언어들은 특히 최적화된 코드에서 오버헤드를 더 줄이기 위해 일종의 함수 인자 수정자를 채택했다. 의심스러운 벤치마크에 근거해, Mojo를 홍보하는 래트너의 현재 회사(1억 3천만 달러를 모았다!)는 Mojo가 Rust보다 더 쉽고 빠르다고 주장하는 글들을 발표했다. 나는 솔직히 심각하게 회의적이다. 그렇다고 Rust가 훔쳐오길 바라는 Mojo의 기능이 없다는 뜻은 아니다(예컨대 SIMD와 GPU 프로그래밍 같은 데이터 병렬 기법의 더 나은 통합).
하지만 값 의미론에는 성능 오버헤드 이상의 더 깊은 문제가 있다. 내가 어떤 객체에 메서드를 호출해 그것을 변이하는 동안 그 객체에 대한 참조를 동시에 들고 있다고 하자. 이때 그 객체를 깊게 복사하는 것이 맞을 수도 있고, 아닐 수도 있다. 어쩌면 나는 실제로 공유 가변 상태를 원하고, 안전을 보장하기 위해 셀이나 락을 사용하도록 코드를 재설계하고 싶을지 모른다. 혹은 이는 알고리즘 구상에 결함이 있음을 반영하며, 컴파일러가 내 사고의 충돌을 알려줌으로써 나를 도면으로 되돌려 보낼지도 모른다. 다시 말해, 나는 컴파일러가 최소한의 보장을 지키기 위한 코드를 그냥 삽입하길 바라지 않는다. 오히려 컴파일러가 내 작업을 점검해, 내가 옳다고 자신 있게 단언할 수 있는 알고리즘을 개발하도록 도와주길 바란다.
명확한 예를 하나 들자. 공유 가변 상태가 버그를 야기하는 고전적 사례는 이터레이터 무효화(iterator invalidation) 문제다. 동적으로 크기가 변하는 객체 배열을 순회하면서 새 객체를 그 배열에 푸시하고 싶다고 하자. 메모리 안전이 없는 언어에서는, 이로 인해 배열이 재할당되어 이후의 순회가 해제 후 사용(use-after-free)이 될 수 있다. 값 의미론을 가진 언어에서는, 배열을 복사해 새 배열을 만들 것이고, 본질적으로 내 삽입을 지워버린다. 이는 정의되지 않은 동작보다는 낫지만, 거의 분명히 내 의도는 아니다. 어쩌면 나는 배열을 링 버퍼처럼 쓰려고 했을지 모르고, 그런 의미론을 가진 타입을 사용해야 했을 것이다. 어쩌면 나는 다른 변수에 푸시하려고 했던 걸지도 모른다. 어쩌면 내 사고가 단지 흐려져 있었던 것일 수도 있다.
내가 Rust의 해법이 완벽하다고 말하는 것은 아니다. 실제로 수명 문법은 난해하고 사용자에게 혼란스럽다. 하지만 Rust를 이긴다고 주장하는 어떤 해법이라도 최소한 같은 수준의 보장을 제공해야 하고, 가능하다면 더 나아가야 한다. 이상적으로는 새로운 언어들이 어떠한 공유 가변 상태도 없이 명령형 프로그래밍을 허용해야 한다. 이런 맥락에서 내가 매우 흥미롭게 보는 언어가 Hylo다. 이 언어는 공유 가변 상태의 회피를 보장하면서도, 참조를 일급 값으로 허용하지 않는다. 이 접근에는 장단이 있다. 성공 가능성을 가지려면, Rust에서 수명 매개변수화된 타입들이 표현하는 바를 표현할 일종의 효과 시스템 혹은 코루틴 시스템이 필요하다고 본다. Hylo든 다른 언어든, 이 보장을 제공할 추가 기법의 발전을 간절히 기다린다. 그래야, 토니 호어의 진단과는 반대로, 참조의 도입—점프와 같은 그것—이 우리가 말끔히 회복해낸 과거의 한 발자국 후퇴였음을 입증할 수 있을 것이다.