(소프트웨어 고고학) 어째서 `String#replaceAll`처럼 메서드 경로의 구분자로 `#`을 쓸까?

Posted on Nov 13, 2019

오늘 아침

다음은 오늘 아침 트위터의 타임라인에서 내가 스쳐본 한 스크린샷의 일부이다:

img1

…뭐에 느낌을 받았냐하면, 바로 저 String#replace(..) 이라는 부분이다. (다른 부분에서는 String.prototype.replace(..) 와 같이 썼는데 굳이 저기서만 갑자기 튀어나온 표기법)

사실 나도 종종 내가 작업한 코드의 문서를 쓰거나2 아니면 다른 사람과 텍스트로 대화를 해야할 때 이렇게 표기를 해왔었다.3

각각의 언어들에서 (내가 생각하는) 저렇게 표기하는 근거들은 다음과 같다:

  1. Java: https://www.oracle.com/technetwork/articles/java/index-137868.html
    • @see Component#getGraphics() JavaDoc안에서 다른 메서드, 필드를 참고로 넣고 싶을 때 이게 표준표기법이고 javadoc도 요렇게 써줘야 링크처리를 해준다.
  2. JavaScript
  3. Ruby

스택오버플로우: Use a hash character or a dot when referring to methods and fields in software documentation? [closed]에 답변이 있으나 내가 볼 땐 내 가설이 더 확실한 이야기라고 본다.4


기원 - Smalltalk

내가 생각하는 기원은 Smalltalk language에서 메서드명을 표기하는 방법에서 시작했다고 생각한다.

더욱이 더 흥미로운 점은 문서를 위한 표기법이 아니고, Smalltalk 코드에서 직접 메서드 값을 얻을 때 사용하는 코드표현식 그자체라는 점이다.

다음과 같이 주로 표기한다:

  1. 인스턴스 메서드: String>>#indexOf:
    • String 인스턴스의 indexOf 메서드. (파라미터를 1개 받는)
  2. 클래스 메서드: String class>>#empty
    • String 클래스의 empty 메서드.

실제로 실행해보면 다음과 같이 실행 가능한 표현식이다 (실행해서 각각의 결과를 Pharo Inspector으로 살펴보는 스크린샷):

inspector

Java, Ruby에서는 그냥 문서 쓸 때 표기법일뿐이었는데, Smalltalk에서는 아예 저게 실행가능한 표현이고, Smalltalk문서, 서적들에서도 그냥 저렇게 표기한다. (Smalltalk 개발환경 안에서도 마찬가지고.)

더 흥미로운 부분은 이 표현식을 분석해보면 나온다:

  1. #blah 와 같은 부분은 Smalltalk에서 Symbol 값의 literal 표현이다.
    • Lisp의 심볼이랑 유사하다. 그리고 Smalltalk에서는 이렇게 메시지/메서드의 이름을 심볼으로 Lisp처럼 표기한다.5
    • Smalltalk 커뮤니티에서는 이런 메시지/메서드 이름을 심볼으로 표기한 것을 흔히 selector 이라고 부른다.
  2. >> 부분은 Class 객체의 “binary operator"으로 정의되어 있다.
    • 좀 더 정확하게는 Pharo Smalltalk에서는 Class의 조상 중 하나인 Behavior.
    • 내용은 “주어진 selector에 해당하는 메서드 코드 값을 찾아 되돌려줌"이다.

그러니까, 결국 다음과 같이 써도 맞는 표현식이다.

String >> #indexOf:

(String class) >> #empty

조금 더 Java스러운 코드나 Lisp식으로 써보면…

// Java-style
String.class.getMethodByName("indexOf"); // Imaginary code

;;; Lisp-style
(get-method String 'indexOf)            ; I know, not an idiomatic Lisp

물론, >> 대신에 getMethodByName() Java 메서드나 #'get-method 같은 Lisp 함수가 있다고 가정한 예시다.

사실 내 생각엔 스페이스 없이 다 붙여쓰는건, 표현식의 덩어리 붙여서 읽기 편하라고 그런거 같다. 예로, Smalltalk에서 2차원 공간에서 좌표를 표기하는 Number>>#@ 메서드는 다음과 같이 보통 literal을 표현한다:

11@42

X=11, Y=42인 Point 객체의 표현이다. 이렇게 붙여쓰는게 더 잘 보이는 경우이니 그런거 같다.

…그러면 뭔가 난감함이 밀려온다 온다. 왜 그럴거면 >> 부분은 몽땅 다 날려버리고 그냥 심볼 표기법만을 그대로 붙여서 써왔던건가? 그렇다.


맺으며

Lisp이나 Smalltalk에서는 평가값 자체의 문자열 표현식을 그대로 다시 평가하면, 그 값 자체로 평가되도록 출력하는 문화가 있다. (가능할 경우에는)

Python도 이런 특성이 남아있다: 어째서 객체를 정의할 때 __str____repr__ 두 가지 다른 방법이 모두 있는지 궁금하지 않았었나? https://docs.python.org/3/reference/datamodel.html#object.__repr__ 6

Java, Ruby은 모두 Smalltalk의 영향을 많이 받은 언어이리라 생각한다.

Java의 경우에는 내 생각엔 거의 “C++의 문법을 뒤집어 쓴 Smalltalk"인거 같으니까. (비슷한 특성을 이야기하자면 JIT, VM, Garbage Collection, Single Inheritance… 끝도 없을거 같은데)

Ruby야 워낙 문법이나 설계 등등 많은 부분을 Lisp, Smalltalk, Perl 등에서 따왔다고 말하니까.

JavaScript야 “Java랑 거의 같은 언어"니까 이렇게 된거 같다.7

그냥 Smalltalk의 영향이 아주 조금이나마 보이는 부분에 이렇게 남아 있고, 그게 조금 말도 안되는 형태이기는 하지만 강제되고 전파되어 온게 흥미롭다.

Smalltalk은 Domain Specific Language을 Lisp이랑 다르게 Macro이 없이도 그냥 주욱 만들어가기 좋은 언어로 의도해 성장해왔고, 그 영향은 현재의 대부분의 객체지향언어들이 Lisp의 매크로 시스템을 갖지 못한데다가 Smalltalk와 더 유사함이 많고 해서 비슷하게 전파되어진거 같다. (Java, C#, Groovy, Scala등등…)

그리고 그런 DSL으로 표기한게 문서화에 그대로 써도 되게 간결하고 확실한 Smalltalk이 흥미롭다.



  1. 출처 https://v8.dev/features/string-replaceall ↩︎

  2. 특히 Java 코드를 작성했다면, JavaDoc에 이렇게 메서드 이름을 표기하는게 표준이고 강제되니까. (이렇게 적어야 링크가 제대로 걸림ㅋ) ↩︎

  3. 그리고 동료들은 대체 왜 그렇게 표기하느냐고 물어왔었다. ↩︎

  4. 아니면 뭐… 어쩔 수 없지. ↩︎

  5. Smalltalk의 설계자 중 한명인 Alan Kay은 이미 Lisp에 대해 알고 있었었고, 이를 좋아했었던거 같다: https://www.quora.com/What-did-Alan-Kay-mean-by-Lisp-is-the-greatest-single-programming-language-ever-designed/answer/Alan-Kay-11?share=1 ↩︎

  6. 하지만 현재는 디버깅을 위한 용도로, 읽는 사람을 테크니컬한 정보로 깜짝놀라게 하고 싶은 용도로 사용한다. 그리고 반대로 사람의 마음을 안정적으로 유지하기 위한 문자열 표현은 __str__ 을 사용한다. ↩︎

  7. 죄송합니다. ↩︎