저서 소개: ‘型システムの仕組み’
에드 씨는 타입 시스템의 원리를 설명하는 저서 ‘型システムの仕組み’를 소개했습니다. 이 책은 유명 교과서 ‘Types and Programming Languages’의 핵심 내용을 TypeScript를 통해 직접 타입 체커를 구현하며 배울 수 있도록 구성되어 있습니다. 기본적인 타입 시스템부터 서브타이핑, 재귀 타입, 제네릭스 등 고급 기능까지 확장하는 방법을 다루며, 독자들이 이러한 언어 기능을 갖춘 타입 체커를 직접 만들 수 있도록 안내합니다. 저자는 이 책을 통해 루비 타입 개발에 참여하는 인구가 늘어나기를 기대하며, 특히 Steep나 Sorbet 등 다른 루비 타입 체커에 관심 있는 개발자들에게도 유용한 자료가 될 것이라고 강조했습니다.
TypeProf 소개 및 데모
TypeProf는 현재 개발 중인 루비 편집기 지원 기능으로, 일반적인 타입 분석기와는 다른 독자적인 복잡한 알고리즘을 사용합니다. 모든 메서드에 타입을 명시하지 않아도 어느 정도의 타입 추론이 가능하며, 편집기 상에서 타입 가능성 표시, 정의 점프, 이름 변경 등의 기능을 제공합니다. Steep나 Sorbet와 달리 명시적인 타입 주석 없이도 동작하는 것을 목표로 합니다. VS Code 데모에서는 다음과 같은 기능들이 시연되었습니다: * 타입 추론 표시: 메서드 정의 위에 추론된 타입이 흐릿하게 표시됩니다. * 정의 점프 (Go to Definition): 메서드 호출부에서 정의부로 이동합니다. * 타입 오류 감지: 잘못된 인자 전달 등 타입 불일치 시 오류를 표시합니다. * 메서드 완성 (Completion): 메서드 이름 자동 완성 기능을 제공합니다. * 자동 이름 변경 (Auto-Rename): 변수나 상수 이름을 변경하면 참조하는 모든 곳이 자동으로 업데이트됩니다. * RBS 정의 점프: 루비 파일 외에 RBS 파일에 정의된 타입 선언으로도 점프할 수 있도록 개선되었습니다.
TypeProf의 최근 개선 사항 및 난제
TypeProf는 작년 발표 이후 루비 문법 지원을 대폭 확장하여 표준 라이브러리 전체를 오류 없이 분석할 수 있게 되었습니다. 또한 Ruby Wasm 위에서 TypeProf가 동작하도록 구현되어 브라우저에서도 TypeProf를 체험할 수 있습니다.
1. 진단 심각도 설정 (diagnostics_severity
): 현재 TypeProf는 오탐(false positive)이 많기 때문에, 사용자 편의를 위해 진단 메시지의 심각도(오류, 경고, 정보, 힌트, 없음)를 설정할 수 있는 기능을 추가했습니다. 이는 기존 루비 프로젝트에 Steep를 도입할 때 겪는 어려움(많은 오류 메시지)을 해결하기 위해 Steep가 도입했던 기능에서 영감을 받았습니다.
2. 분석 단위 (analysis_unit_dirs
) 도입: 대규모 프로젝트에서 TypeProf의 타입 추론 방식(호출 체인을 따라 타입 추론)은 확장성 문제가 발생할 수 있습니다. 이를 해결하기 위해 analysis_unit_dirs
설정을 통해 디렉토리 단위로 분석 단위를 나눌 수 있는 기능을 구현했습니다. 각 분석 단위는 독립적으로 분석되며, 단위 간의 인터페이스는 RBS 파일을 통해 명시적으로 타입을 선언하여 처리합니다. 이는 모든 메서드에 타입을 쓰는 것은 비현실적이지만, 디렉토리나 젬, 팩(Rails Packs)과 같은 단위 간의 인터페이스에는 타입을 명시하는 것이 좋다는 저자의 철학을 반영합니다.
3. 루비 상수 탐색의 복잡성으로 인한 버그 해결: TypeProf 개발 중 루비의 복잡한 상수 탐색 규칙으로 인한 난해한 무한 루프 버그가 발생했습니다. 루비의 상수 탐색은 상속 체인과 모듈 인클루드를 통해 이루어지는데, 특히 M { module M; end }
와 같이 중첩된 모듈 구조에서 include M
이 발생할 경우, TypeProf의 재분석 로직이 외부 M과 내부 M 사이를 반복적으로 오가며 무한 루프에 빠지는 문제가 있었습니다. 이 문제는 RBS Inline에서도 유사한 형태로 발생했습니다. 해결책으로, include
문의 인자로 사용되는 상수(예: include M
)에 대해서는 상속 체인을 통한 상수 탐색을 포기하고, 명시적인 전체 경로(예: ::M::M
)를 사용하도록 요구하는 방식으로 접근하기로 했습니다. 이는 루비 상수 탐색의 예측 불가능성과 복잡성을 고려한 현실적인 타협안입니다.