루비에서 10.times { ... }와 같은 코드는 단순히 “10번 반복하라”는 명령이 아니라, 숫자 10이라는 Integer 객체에 :times라는 메시지를 보내는 행위입니다. 이 메시지 패싱 개념은 파이썬의 object.method(argument) 방식과 달리, 객체가 메시지를 수신하고 그에 대한 응답 방식을 스스로 결정하는 객체 자율성을 강조합니다.
메시지 패싱과 메서드 호출의 차이
-
전통적인 언어: 컴파일 시점에 메서드를 찾아 호출합니다.
-
루비:
10.send(:times) { ... }와 같이 런타임에:times라는 심볼 메시지를 객체 10에 보냅니다. 객체는 이 메시지를 받고 어떻게 응답할지 결정합니다. -
method_missing을 통해 존재하지 않는 메시지에 대한 응답을 정의할 수 있으며, 이는 메시지가 메서드와 독립적으로 존재함을 보여줍니다.
Smalltalk의 영향
루비의 이러한 메시지 패싱 철학은 Smalltalk에서 크게 영감을 받았습니다. Smalltalk에서는 5 timesRepeat: [ ... ]나 #(1 2 3) do: [ ... ]와 같이 모든 것이 메시지 패싱으로 이루어집니다. 루비의 Integer.times, Array.each, Enumerable 모듈의 다양한 메서드들은 이 Smalltalk의 컬렉션 프로토콜(Collection Protocol)을 구현한 것입니다. Enumerable을 포함하고 each 메서드를 구현하면 map, select, reduce 등 다양한 반복 메서드를 자동으로 사용할 수 있습니다.
for 루프가 이질적인 이유
for 루프는 루비의 메시지 패싱 패러다임과 일치하지 않는 구문적 요소입니다. for 루프는 변수를 외부 스코프에 노출시키는 등 부작용을 일으키며, 객체에 반복의 의미를 직접 부여하는 루비의 방식과 다릅니다. 이는 루비가 ‘구문보다는 프로토콜(Protocol over Syntax)’을 중시하는 철학을 따르기 때문입니다. 루비는 객체가 스스로 반복 방식을 정의하고 메시지에 응답하도록 장려하며, 이는 루비 코드를 작성하는 독특한 스타일을 형성합니다.