Ankur는 루비의 예측 불가능하고 일관성 없는 동작을 여러 예시를 통해 설명합니다.
1. 메서드 호출과 우선순위
- 루비는 ‘모든 것이 메서드 호출’이라는 Smalltalk의 사상을 따르지만, 45가지의 복잡한 우선순위 규칙을 도입하여 일관성을 해칩니다. 이는 간단한 연산에서도 예측 불가능한 결과를 초래하며, 명확한 정신 모델 구축을 어렵게 만듭니다.
2. 추상화 누수 및 수치형 처리
- 고수준 언어임에도 불구하고 Stat 객체처럼 C API의 구현 세부 사항이 노출되어 C 지식 없이는 이해하기 어렵습니다.
- 복소수 타입을 지원하지만, Math 모듈이 아닌 별도의 Gem을 통해야만 완벽히 활용 가능하여 언어의 일관성이 부족합니다.
3. 변수 스코프의 복잡성
- 블록 내에서 지역 변수 할당(
value = 42
)은 동일한 이름의 외부 메서드를 가릴 수 있습니다. - 정규 표현식의 이름 있는 캡처(
(?<value>...)
)는 할당 연산자 없이도 지역 변수를 암시적으로 생성하여 메서드를 가리며, Perl 스타일의 전역 변수($1
,$&
)를 변경하는 예상치 못한 부작용을 일으킵니다. 이러한 동작은 언어의 기본 원칙에서 파생되지 않는 ‘역사적 특성’으로 간주됩니다.
4. 동적 스코프 부재 및 서비스 객체
- 컨텍스트 전달(prop drilling) 문제를 해결하기 위해 Rails의 current_attributes와 같은 프레임워크 ‘마법’이 사용되나, 이는 언어의 정상적인 스코핑 규칙을 벗어납니다. Common Lisp의 동적 스코프는 예측 가능한 언어 기능으로 이를 해결합니다.
- 서비스 객체는 루비에서 동사를 명사로 위장하는 보일러플레이트 역할을 합니다. Dylan과 같은 언어의 일급 동사는 행위 자체를 독립적으로 다루어 코드의 의도를 명확히 합니다.
5. 상속, 메타프로그래밍, 제어 흐름의 난해함
- 루비의 메서드 호출은 복잡한 상속 체인(include, extend, prepend로 수동 조작)을 수동으로 추적해야 하므로 인지 부하가 높습니다.
- 메타프로그래밍은 강력하지만 불투명한 런타임 트릭에 의존하는 반면, Elixir의 매크로는 코드 재작성을 명시적으로 지시하여 추론 가능성을 높입니다.
- 루비는 스택 점프에 세 가지 방법을 사용하지만, Common Lisp/Dylan의 조건 시스템은 이를 단일하고 일관된 방식으로 처리하며, 스택 해제 없이 에러를 복구하는 양방향 대화를 가능하게 합니다.