1P by GN⁺ 8시간전 | ★ favorite | 댓글 1개
  • Arthur Whitney가 만든 50줄짜리 K 언어 인터프리터 C 코드를 분석하며, 그의 독특한 코딩 스타일을 해석한 기록
  • 코드에는 매크로 기반의 압축 문법, 비표준 C 확장, 암묵적 인자 사용 등 일반적인 C 코드와 다른 실험적 구조가 다수 포함
  • 작성자는 각 매크로와 함수의 의미를 직접 해석하며, APL 계열 언어의 철학코드 밀도의 장단점을 탐구
  • 코드의 장점으로는 짧은 길이와 높은 구성력, 단점으로는 비표준 문법과 가독성 저하를 지적
  • 결론적으로, 이 코드는 “짧게 쓰는 법”보다 문제를 완전히 이해한 뒤 코드를 쓰는 사고방식의 중요성을 보여주는 사례로 평가됨

Arthur Whitney와 그의 코드

  • Arthur Whitney는 A, K, Q 언어kdb, Shakti 데이터베이스를 설계한 컴퓨터 과학자
    • kdb는 금융권에서 사용되는 초고속 시계열 데이터베이스, Shakti는 그보다 빠른 버전으로 1조 행 규모 데이터셋을 처리하도록 설계됨
  • 그의 언어들은 APL의 영향을 강하게 받은 배열 기반 언어로, 간결성과 수학적 표현력을 중시함
  • 글의 초점은 금융 응용이 아니라, 휘트니가 작성한 C 코드의 독특한 스타일을 분석하는 데 있음

50줄짜리 K 인터프리터의 구조

  • 공개된 ksimple 리포지토리에는 휘트니가 며칠 만에 작성한 약 50줄의 C 인터프리터가 포함
  • 코드의 핵심은 a.ha.c 두 파일로 구성되어 있으며, 매크로를 통한 함수 정의 축약포인터를 정수처럼 사용하는 구조가 특징
  • typedef char*s,c; 구문을 통해 s를 문자열 포인터, c를 문자형으로 정의
  • s Q=(s)128;은 포인터를 정수처럼 사용한 예로, 코드 전반에서 Q가 오류 상태를 나타내는 특수 값으로 쓰임
  • ({e;}) 형태의 statement expression?: 연산자 등 GCC 확장 문법이 다수 사용됨

주요 매크로와 함수의 의미

  • #define _(e...) ({e;}) : 여러 문장을 하나의 표현식으로 묶는 매크로
  • #define i(n,e) : 반복문 축약 표현, for 루프를 한 줄로 표현
  • #define Q(e) 등은 에러 처리 매크로, Qr, Qd, Qz는 각각 rank, domain, not-yet-implemented 오류를 반환
  • _s, _i, f, F 매크로는 함수 선언을 간소화하며, 암묵적으로 인자 x, a를 사용
  • ax, ix, nx 등은 데이터 타입 판별 및 인덱싱 매크로, ax는 “x가 원자(atom)인지”를 판단
  • f(w,write(1,ax?&x:x,ax?1:strlen(x));x)출력 함수, 원자면 문자로, 벡터면 문자열로 출력

인터프리터의 동작 방식

  • m(x) 함수는 메모리 할당 및 길이 정보 포함 포인터 생성, 벡터의 최대 길이는 255바이트
  • g(a,v) 매크로는 원자/벡터 연산을 통합 처리, not, sub, At, _A 등의 함수가 이를 기반으로 정의
  • G(f,o) 매크로는 이항 연산자 함수 자동 생성, <, ==, +, *, &, | 등의 연산을 지원
  • cat, rev, cnt, Tak 등은 벡터 조작 함수, revind 함수를 이용해 역순 인덱스를 생성
  • e() 함수는 재귀적 평가기, 문자열을 오른쪽에서 왼쪽으로 읽으며 단일 문자 변수, 숫자, 연산자를 처리
  • main()은 입력을 받아 e()로 평가 후 결과를 출력하는 REPL 루프 형태

코드 스타일에 대한 평가

  • 장점
    • 조합 가능한 매크로로 구성된 간결한 원시 연산 집합
    • 짧은 코드 길이로 인해 스크롤 없이 전체 로직을 한눈에 파악 가능
    • 고밀도 표현을 통해 코드의 논리적 구조를 압축
  • 단점
    • char*를 정수처럼 사용하는 비의미적 타입 처리
    • ASCII 코드 직접 사용, 복잡한 삼항 연산자, 비표준 문법 등으로 인한 가독성 저하
    • 암묵적 인자짧은 변수명으로 인해 의도 파악이 어려움
  • 중립적 요소
    • GCC 전용 문법(?:, statement expression)은 흥미롭지만 이식성 저하
    • 암묵적 인자 사용은 소규모 코드에서는 유용하나, 대규모 코드에서는 혼란 유발 가능
    • 짧은 이름은 익숙해지면 효율적이지만, 의미 전달력은 약함

결론과 교훈

  • 이 코드는 단순히 “짧게 쓰는 법”이 아니라, 문제를 완전히 이해한 뒤 코드를 작성하는 사고방식을 보여줌
  • 휘트니의 코드는 이미 완성된 수학적 모델을 코드로 옮긴 형태, 즉 “생각을 코드로 표현한 결과물”
  • 작성자는 자신이 평소 코드 안에서 문제를 해결하려는 습관을 반성하며,
    앞으로는 코드 작성 전 개념적 모델링과 사고 정리의 중요성을 강조
  • 최종적으로, 이 실험은 “코드를 읽는 능력”을 단련하고, 코드 밀도와 사고 명료성의 균형을 탐구한 경험으로 정리됨

향후 실험 아이디어

  • 인터프리터 확장 실습 제안:
    • 부동소수점 벡터 지원
    • 255개 이상 원소 처리
    • 다자리 숫자 및 변수명
    • 배열 리터럴과 공백 무시
    • 메모리 관리 및 오류 표시 기능 추가
    • 미구현 함수 완성
  • 이러한 확장은 휘트니식 코드 스타일을 유지하면서 실제 사용 가능한 언어로 발전시키는 실험이 될 수 있음
Hacker News 의견
  • 이 코드의 매크로들은 공통 연산을 압축하기 위한 것임
    나는 J Incunabulum을 먼저 읽고 나서 이 코드를 봤는데, C에 익숙한 프로그래머들이 중간부터 읽기 시작하면 초반의 매크로 정의들이 혼란을 줄 수 있음
    매크로들이 서로를 기반으로 쌓이기 때문에 코드가 빠르게 추상화의 사다리를 오름
    특히 Iterate 매크로(i)가 마음에 드는데, 장황한 루프를 한 글자로 줄여줌
    이런 밀도 높은 코드가 읽기 어려운 이유는, 수십 줄 안에 수많은 추상화를 만들고 바로 사용하는 방식 때문임
    그래서 한 글자씩 천천히 읽어야 함
    수백 개의 얇은 파일로 구성된 대규모 코드베이스에서 일해본 입장에서는, 이런 극단적인 압축성이 오히려 신선하게 느껴짐

    • 나도 Incunabulum을 읽고 비슷한 생각을 했지만, 변수 이름을 이모지로 바꿔보니 훨씬 이해가 쉬워졌음
      이모지 버전 코드 이미지를 보면 알 수 있듯, 문제의 일부는 정보 밀도뿐 아니라 문자 형태(orthography) 에 있음
      현대 언어들은 식별자에 기호나 이모지를 쓰지 못하게 하는데, 이런 시각적 구분이 가능했다면 훨씬 읽기 쉬웠을 것임
      또 대부분의 에디터가 구문별 색상(syntax highlighting)을 쓰지만, 토큰 기반 색상(각 식별자마다 고유 색상)이 더 유용할 때가 많음
      Sublime Text의 “hashed syntax highlighting”이 그 예임
      이렇게 바꾸니 코드의 구조가 한눈에 들어왔음
    • 개발자들이 오히려 거대한 코드베이스를 선호하는 것 같음
      나는 하위 디렉터리 없이 grep *.[ch]로 바로 찾을 수 있는 구조를 좋아함
      Java 프로젝트는 특히 작은 파일이 너무 많고 내용이 빈약해서 찾기가 힘듦
      IDE가 있으면 낫겠지만 나는 안 씀
      Whitney는 인터뷰에서 모든 코드를 한 페이지에 담고 싶다고 했고, 그의 “IDE”는 Windows 콘솔과 Notepad라고 함
  • Arthur Whitney의 C 코드를 이해하려면 먼저 APL 계열 언어를 배워야 함
    그렇지 않으면 단순히 이상한 C 스타일로 보일 뿐임
    Whitney는 C를 APL처럼 쓰려는 것임
    공백이 없고, 한 글자 이름을 쓰며, 한 줄 함수로 구성된 스타일은 APL에서도 동일함
    Pascal 프로그래머가 #define begin { 같은 걸 쓰는 것과 비슷하지만, Whitney는 그보다 훨씬 독창적임

    • APL 사용자 입장에서도 이건 이상하게 보임
      Whitney가 만든 K 언어는 이런 스타일을 쓰지만, 한 줄 함수는 전통적인 APL에서는 불가능했음
      매크로나 삼항 연산자, 암묵적 변수명 같은 건 APL에 없음
      APL의 본질은 불변 배열 연산인데, Whitney의 C 스타일은 그 철학과는 다름
    • “Pascal 프로그래머가 C로 넘어와서 #define begin { 하는 것 같다”는 말에 대해, “아, Stephen Bourne처럼 말이지”라고 농담함
    • 처음엔 함수형 언어처럼 보였는데, 곧 C 전처리기의 공포를 떠올리게 됨
    • 이미 글의 서두에서 “Whitney의 C는 APL에서 영감을 받았다”고 설명하고 있음
      단순 요약 댓글이 많다는 지적임
    • J를 배우는 것도 괜찮을 것 같음
      APL보다 접근성이 높고, 일반 키보드로 입력 가능한 기호를 쓰기 때문임
  • Shakti를 찾아보다가 Wikipedia 링크k.nyc로 리디렉션되는 걸 봤는데, 페이지에는 ‘k’ 한 글자만 있음
    소스를 보니 정말 <div>k</div>뿐이었음
    이건 HTML 버전의 Whitney식 미니멀리즘 같음 — 불필요한 건 모두 제거하고, 컴파일러가 암묵적으로 처리하도록 맡긴 느낌임

    • k
  • 이 블로그 글은 Whitney의 코딩 스타일에 대한 평가와 상관없이 훌륭한 분석글
    작성자가 8시간 만에 쓴 것치고는 깊이가 있고, 결론 부분이 특히 인상적이었음
    원문 링크

  • Stephen Bourne이 C를 Algol처럼 만들려던 시도가 떠오름
    mac.h 예시
    expand.c 예시를 보면 비슷한 감성이 느껴짐

  • 모든 분야에는 “best practice”가 있지만, 그건 평균적인 경우에만 잘 맞음
    특정 상황에서는 오히려 반대로 가는 게 더 나을 때도 있음
    결국 집단 지혜는 기본값으로 삼되, 스스로 생각하기 시작하면 그 틈이 보이기 마련임

    • 그래서 “best practice”라는 표현이 싫음
      사실은 평균적인 수준의 절충안일 뿐임
      효율성과 성능을 희생하고 유지보수성과 일관성을 얻는 거래임
    • 좋은 제품을 만드는 것과 코드베이스의 가독성이나 학습 곡선은 별개임
      한쪽이 잘된다고 해서 다른 쪽도 자동으로 따라오는 건 아님
  • 코드에 대해 공격적으로 굴지 않고 균형 잡힌 시각을 보여줘서 좋았음
    재미있게 읽었고, 나중에 다시 읽어볼 생각임

  • 이런 코딩 스타일이 특정 패러다임인지 궁금했음
    실제 프로젝트에서 이런 코드를 본 적은 거의 없고, “business card ray tracer” 같은 예외만 있음
    Whitney가 만든 J 언어의 소스 코드도 비슷하게 극도로 압축된 스타일을 가짐

    • 맞음, 이건 Whitney의 고유한 코딩 스타일
      그의 배열 언어 인터프리터들에서 일관되게 쓰이며, 몇 페이지 안에 전체 구현을 담는 것으로 유명함
      관련된 HN 논의들을 모아둔 메타 댓글 링크도 있음
    • “이런 코드를 실제로 본 적 없다”는 말에, “운이 좋았음”이라고 답함
      이건 더 이상 C가 아니라, C 위에 새로 만든 내부 DSL 같은 언어임
      C는 단지 첫 번째 컴파일 타깃일 뿐임
    • J, K 같은 APL 계열 언어와 유사함
      비ASCII 기호를 쓰고, 극단적인 밀도로 많은 정보를 한 페이지에 담을 수 있음
      익숙해지면 추상화 계층이 줄어드는 장점도 있음
    • 비슷한 접근으로 만든 co-dfns 관련 영상도 있음
      C는 아니지만 비슷하게 고밀도 스타일로 작성됨
    • 농담처럼 “이건 OCC(Obfuscated C Code)”라고 부름
  • 다음 매크로 정의들을 보면

    #define _(e...) ({e;})
    #define x(a,e...) _(s x=a;e)
    #define $(a,b) if(a)b;else
    #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}}
    

    이런 식으로 공통 연산을 압축하는 매크로들이지만, 너무 과감해서 “** 전쟁 범죄급 코드**”라고 농담함

    • 일부 매크로는 문법적으로도 잘못됨
      예를 들어 #define $(a,b) if(a)b;else는 괄호가 없어 버그 유발 가능성이 큼
      단순히 게으른 작성임
  • 이런 매크로 사용은 좋은 예라고 생각함
    겉보기엔 무섭지만, 사실 간결한 선언형 C 스타일
    다만 밀도가 높고, 생소한 매크로 문법 때문에 읽기 어렵게 느껴질 뿐임
    “oo”는 아마 무한대 오류 상태나 디버그용 출력일 가능성이 있음

    • 나도 이런 매크로들이 유용하다고 생각함
      DO(n, code) 같은 간단한 반복문 매크로가 있으면 좋겠음
      작은 연산 단위(예: opcode, forth word, APL 연산자)를 한 줄로 표현하면 전체 구조를 한눈에 볼 수 있음
      코드의 ‘사이 공간’ 에서 의미를 읽는 게 중요하다고 느낌
    • “좋은 매크로 사용”이라는 말에 단호히 “아니오”라고 반박함