프로그래밍의 ‘역사의 종말’

ko생성일: 2025. 9. 22.갱신일: 2025. 9. 23.

프로그래밍 패러다임의 종착지를 수학적 DSL과 런타임의 분리로 전망하고, 사용자 영역의 다양한 관심사가 런타임(혹은 타입 검사)으로 이전되는 흐름과 그 파급효과를 논한다.

역사

나는 프로그래밍에서의 ‘역사의 종말’(end of history)이 어떤 모습일지에 대해 꽤 많은 시간을 들여 생각한다. 여기서 ‘역사의 종말’이란 프로그래밍 패러다임이 더는 유의미하게 발전하지 않는 지점을 뜻한다.

나는 프로그래밍의 ‘운명’에 관심이 있다. 왜냐하면 그 최종 패러다임에 우리를 더 가깝게 데려다주는 오픈 소스 프로젝트에 참여하는 걸 선호하기 때문이다. 내 경험상 이런 종류의 일은 수명이 길고, 영향력이 크며, 프로그래밍 전체 분야를 앞으로 나아가게 돕는다.

그렇다면 프로그래밍에서의 ‘역사의 종말’은 어떤 모습일까? 그것은 다음 중 어느 것일까:

  • … 이미 와 있는가?

일부 사람들은 프로그래밍을 이미 해결된 문제로 보고, 모든 새로운 언어나 패러다임을 기존 것의 외형만 바꾼 재포장으로 여긴다. 그들의 관점에서 남은 일은 장인정신을 다듬으며 서서히 최적화하고, 버그를 제거하며, 사람 관리나 자금 조달 같은 비기술적 문제를 다루는 정도다.

나는 개인적으로 이 철학에 동의하지 않는다. 최소한 함수형 프로그래밍 패러다임이 객체지향과 명령형 패러다임을 서서히 대체해 나가리라고 믿기 때문이다(물론 함수형 프로그래밍이 반드시 최종 패러다임이라는 뜻은 아니다).

  • … 인공지능인가?

어쩌면 기계가 우리의 자연어 지시를 코드로 번역해 우리의 의도를 정밀하게 전달해야 하는 부담에서 벗어나게 해 줄지도 모른다. 혹은 충분히 똑똑한 AI 기반 IDE가 프로그램의 대부분을 자동 완성해 줄 수도 있다.

나는 이것에도 동의하지 않으며, 데익스트라가 자신의 에세이 On the foolishness of “natural language programming”에서 이 논리를 훌륭히 논파했다고 생각한다.

사실, 정답이 무엇인지 나는 확신하지 못하지만, 프로그래밍에서의 ‘역사의 종말’이 어떤 모습일지에 대한 나만의 추측을 제시해 보겠다.

수학적 DSLs

내 생각에 프로그래밍의 다음 논리적 단계는 두 개의 겹치지 않는 프로그래밍 영역으로 분화되는 것이다:

  • …을 위한 런타임 구축
  • … 수학적 프로그래밍 언어

구체적으로, 프로그래밍 언어는 더 수학적인 성격으로 진화하여, 사용자가 자신의 의도를 전달하기 위해 작성하는 프로그램이 순수한 수학적 표현에 가까워질 것이라고 본다.

예를 들어, 부울 논리의 “and” 연산자와 함수 합성 연산자의 다음 수학적 명세를 보자:

True  && x     = x
x     && True  = x
False && False = False

(f . g)(x) = f(g(x))

이러한 수학적 명세는 동일하게 실행 가능한 Haskell 코드이기도 하다(주류 함수 구문을 닮게 하려고 괄호를 조금 더 쳤을 뿐). Haskell은 코드가 순수한 수학적 표현과 정의에 가깝기를 지향하는 언어의 한 예다.

이처럼 이상화된 수학적 인터페이스를 제시하는 어떤 언어든 현실 세계가 지저분하기 때문에 ‘후드 아래’에 상당한 복잡성을 도입해야 한다. 바로 여기서 그런 보기 싫은 세부를 덮어 주는 런타임 구축이 필요해진다.

다시 말해, 나는 프로그래밍의 ‘역사의 종말’이란 순수한 수학적 표현과 현실 세계를 잇는 인터페이스가 되는 것이라고 예측한다.

과거와 현재

이미 사용자 영역(userland)의 코드를 더 수학적으로 만드는 이 추세가 진행 중인 사례들을 몇 가지 들어 보겠다:

  • 메모리 관리

수동 메모리 관리는 대부분의 프로그래밍 언어에서 한때 ‘사용자 영역’의 관심사였다. 그러나 새로운 언어의 일반적인 추세는 자동 메모리 관리(러스트는 예외)다. 메모리 관리는 예전에는 프로그래머가 신경 써야 하는 명시적 부수효과였고, 이를 런타임(가비지 컬렉션이나 기타 방법을 통해)으로 밀어 넣음으로써 언어는 더 ‘순수’해졌고, 이상화된 수학적 표현에 한 걸음 더 다가갈 수 있었다.

실제로 러스트는 규칙을 확인시켜 주는 예외다. 러스트는 고수준의 의도 명세에 사용되기보다는 런타임을 구축하는 데 더 적합한 언어로 널리 여겨진다.

  • 함수형 프로그래밍(특히 순수 함수형 프로그래밍)

함수형 프로그래밍은 더 수학적인 스타일로 프로그래밍하는 데 큰 진전이며, 다음을 선호한다:

- 문장(statement)보다 표현식(expression)
- 부수효과보다 순수 함수
- 객체보다 대수적 데이터 타입
- 반복문보다 재귀

이 주제는 왜 나는 함수형 프로그래밍을 선호하는가에서 더 자세히 다룬다.

하지만 함수형 프로그래밍이 공짜 승리는 아니다. (특히 컴파일 언어에서) 고차 함수와 클로저를 효율적으로 지원하는 일은 쉽지 않다. 그래서 덜 정교한 언어 구현일수록 더 명령형에 가깝고 덜 함수형인 경향이 있다.

  • 평가 순서(특히 지연성)

나는 항상 ‘지연 평가(lazy evaluation)’라는 표현이 Haskell의 평가 모델의 장점을 잘 설명하지 못한다고 느껴 왔다. 나는 Haskell이 ‘자동 평가 관리’1를 제공한다고 생각하는 편이 더 낫다. 즉, 프로그래머는 의존 계산의 그래프로 표현식을 지정하고, 런타임이 그 그래프를 가장 효율적으로 축약할 평가 순서를 알아서 결정한다.

이것도 한때 사용자 영역의 관심사였던 것(평가 순서)을 런타임으로 밀어 넣는 또 하나의 사례다. 평가 순서를 가리는 덕분에 우리는 더 수학적인 스타일로 사양을 적을 자유를 얻는다. 수학에서도 평가 순서는 마찬가지로 중요하지 않기 때문이다.

현재와 미래

위의 추세들을 살펴보면 공통 패턴이 드러난다:

  • 사용자 영역의 관심사를 런타임의 관심사로 밀어 넣고, 그 결과:
  • … 프로그램이 더 순수한 수학적 표현을 닮아 가며, 그리고:
  • … 런타임의 복잡성은 크게 증가한다.

당신은 아마 궁금할 것이다. 가까운 미래에 사용자 영역의 어떤 다른 관심사들이 결국 런타임의 관심사로 밀려날까? 내가 떠올리는 몇 가지 예는 다음과 같다:

  • 패키지 관리

이건 미래라기엔 너무 가까워서 이미 벌어지고 있다(참고: Nix와 Dhall). 두 언어 모두 패키지 관리 도구를 따로 써서 언어 바깥에서 처리하는 대신, 언어 차원에서 코드를 가져오는 기능을 내장 지원한다. 이러한 언어 수준의 지원은 외부 코드를 마치 순수한 부분식처럼 프로그램 안에 임베드할 수 있게 하여, 수학적 이상에 더 가깝게 다가가게 한다.

  • 오류 처리

이건 더 많은 설명이 필요하다. 나는 타입 시스템을 오류 처리를 ‘런타임’으로(정확히는 런타임이 아니라 타입 검사기로) 밀어 넣은 논리적 결론으로 본다. Dhall은 이 아이디어를 극단까지 밀어붙인 언어의 예다. Dhall에는 사용자 영역에서 오류를 발생시키거나 잡는 기능이 아예 없다. 모든 오류가 타입 오류이기 때문이다2.

의존 타입총함수적(total) 함수형 프로그래밍 언어의 발전은 오류 처리를 런타임의 관심사로 밀어 넣는 이 이상에 더 가까워지게 만든다.

  • 로깅

사실 나는 언어 차원에서 모든 것을 광범위하게 로깅하는 지원이 아직 보편화되지 않았다는 점이 놀랍다(혹은 이미 있었는데 내가 놓쳤을 수도 있다). 특히 성능에 민감하지 않은 응용 분야에서는, 언어가 구현하기 꽤 평범한 일처럼 보인다.

많은 언어가 이미 프로파일링을 지원하며, 사용자가 성능 예산을 로깅에 쓰겠다고 한다면 프로파일링 지원을 로깅 지원으로 확장하는 일은 큰 도약이 아닐 것 같다.

로깅은 전형적인 부수효과 중 하나로, 본래라면 순수하고 수학적일 수 있었던 코드를 ‘오염’시키는 보기 싫은 디테일이다.

  • 서비스

서비스 지향 아키텍처도 순수하고 부수효과 없는 코드를 작성하는 데 걸림돌이 되는 또 다른 요소다.

서비스 지향 언어 런타임이 정확히 어떤 모습일지 나는 확신하지 못하지만, 현재의 ‘서버리스’ 해법은 내가 생각하는 바가 아니다. AWS Lambda 같은 것은 여전히 수학적 성격의 코드를 촉진하기에는 너무 저수준이다. 예를 들어, 프로그래밍 과정의 어느 부분이라도 서버리스 코드를 배포하거나 관리하기 위해 별도 도구를 사용해야 한다면, 그것은 순수한 수학적 표현을 작성하는 일에서 상당히 동떨어져 있는 셈이다. ‘서버리스 코드를 위한 Nix나 Dhall’ 같은 것이 필요하다.

결론

누군가는 내 예측이 반증 불가능하다고 비판할지 모른다. 프로그래밍의 어떤 새로운 발전도 런타임 구축이나 수학적 표현 범주에 끼워 맞춰 설명할 수 있어 보이기 때문이다.

그래서 나는 예측의 핵심 기둥을 강조하고 싶다. 시간에 따라 런타임과 수학적 표현은 더 뚜렷이 분리될 것이다. 이것이 예측의 실제 내용이며, 여기서 몇 가지 귀결을 도출할 수 있다.

현재 많은 주류 프로그래밍 패러다임과 엔지니어링 조직은 이 두 책임을 뒤섞고 있다. 그 결과 운영 로직(런타임 관심사)과 ‘비즈니스 로직’(수학적 의도)을 섞어 작성된 소프트웨어 프로젝트가 만들어진다.

내가 예측하는 바는, 업계가 프로그래밍 언어 이론 또는 프로그래밍 언어 공학 경험을 가진 사람들에 대한 뚜렷한 수요를 생성하기 시작할 것이라는 점이다. 이들은 가능한 한 많은 운영상 관심사를 추상화하여 자신들의 기업 도메인에 맞는 순수 수학적 도메인 특화 언어를 지원하는 특수 목적 언어와 런타임을 구축하는 책임을 지게 될 것이다. 그리고 이 언어들은 또 다른 집단의 사람들이 사용하게 될 텐데, 그들의 목표는 인간의 의도를 수학적 표현으로 번역하는 일이다.

이 예측의 한 가지 결과는 가까운 미래에 프로그래밍 언어의 캄브리아기 대폭발이 일어나는 것을 보게 되리라는 점이다. 언어 엔지니어들이 더 많은 운영상 관심사를 런타임으로 밀어 넣을수록, 범용 언어를 만들려 하기보다 특정 목적이나 조직의 필요에 맞게 런타임을 더 정밀하게 맞춤화해야 할 것이다. 다시 말해, 각 언어가 자신들의 틈새에 적응함에 따라 언어 런타임(과 타입 검사기)의 뚜렷한 분절화가 일어날 것이다.

런타임이 분절화되더라도 사용자 영역 코드는 정반대의 추세를 보일 것이다. 서로 다른 언어들로 작성된 프로그램이 더 수학적인 성격을 띨수록 서로 더 닮아 가기 시작할 것이다. 어떤 의미에서 수학적 표현은 사용자 영역 코드의 이식 가능한 ‘공통어(lingua franca)’가 될 것이다. 특히 비수학적 관심사들이 각 언어의 런타임으로 밀려날수록 말이다.

이 예측은 훨씬 반증하기 쉽다.

또, 이 글이 마음에 들었다면 아마 고전 논문 The Next 700 Programming Languages도 재미있게 읽을 것이다.


  1. 또한 이 해석은 진실과 크게 동떨어져 있지 않다. Haskell 표준은 비엄격(non-strict) 평가 전략만을 규정하기 때문이다. GHC는 게으르지만(lazy), Haskell 언어 표준은 구현이 반드시 게으를 것을 요구하지 않는다. 자세한 내용은: Lazy vs.non-strict ↩︎

  2. 물론 이건 약간의 단순화다. Dhall은 Optional 값을 지원하고, 합집합 타입을 이용해 오류를 모델링할 수도 있다. 다만 이런 방식은 흔하지 않으며, 관용적으로 실전의 대부분 Dhall 코드는 타입 검사기로 오류를 잡는다. Dhall은 총함수적 함수형 프로그래밍 언어이며, 런타임 오류를 억제하기 위해 상당한 노력을 기울인다. 예컨대 Text 값의 동등 비교를 아예 금지한다. 또한 언어는 기술적으로 의존 타입이며, 타입 검사 시점에 임의의 코드를 실행해 정적으로 오류를 잡는 것을 지원한다. ↩︎