프로그래밍 언어는 절차를 기술한다. 세계를 서술하지 못한다.


프로그래밍 언어는 인류 최고의 발명 중 하나다

프로그래밍 언어는 모호하지 않다. x = 3 + 4는 언제 어디서 실행해도 7이다. 해석의 여지가 없다.

프로그래밍 언어는 검증 가능하다. 구문 오류는 컴파일 전에 잡힌다. 타입 오류는 실행 전에 잡힌다. 테스트가 실행되면 결과는 통과 아니면 실패다.

프로그래밍 언어는 튜링 완전하다. 계산 가능한 모든 것을 표현할 수 있다. 충분한 시간과 메모리가 있으면 어떤 절차든 기술할 수 있다.

이 시리즈에서 자연어의 한계로 지적한 것들 — 모호함, 검증 불가능, 구조 부재 — 을 프로그래밍 언어는 전부 해결했다.

그렇다면 프로그래밍 언어로 AI의 컨텍스트를 표현하면 되는 것 아닌가.

안 된다.


프로그래밍 언어는 절차를 기술한다

다음은 유효한 Python 코드다.

def calculate_revenue(units_sold, unit_price):
    return units_sold * unit_price

이 코드는 명확하고, 검증 가능하고, 실행 가능하다. 그런데 이 코드가 표현하는 것은 무엇인가.

“판매 수량에 단가를 곱하면 매출이 된다.”

이것은 절차다. 방법이다. HOW다. 입력이 들어오면 무엇을 하는지를 기술한다.

이제 다음을 표현해보자.

“삼성전자의 2024년 3분기 매출은 79조원이다.”

이것은 절차가 아니다. 사실이다. WHAT이다. 아무것도 실행하지 않는다. 세계의 상태를 서술한다.

Python으로 이것을 어떻게 표현하는가.

samsung_revenue_2024_q3 = 79_000_000_000_000

변수에 숫자를 할당했다. 작동은 한다. 그러나 이것은 서술이 아니다. 저장이다. 이 코드는 다음을 알지 못한다.

  • “삼성전자"가 어떤 종류의 엔티티인가.
  • “매출"이 무엇인가. 재무 지표인가, 물리량인가.
  • “2024년 3분기"가 시간인가, 버전인가, 라벨인가.
  • 79조원이라는 값의 출처는 무엇인가.
  • 이 값은 얼마나 확실한가.

변수명 samsung_revenue_2024_q3는 사람이 읽으면 의미를 짐작할 수 있다. 기계에게 이것은 임의의 문자열이다. xyzzy_42로 바꿔도 실행 결과는 같다.

프로그래밍 언어에서 변수명은 의미를 갖지 않는다. 의미는 코드 바깥에, 프로그래머의 머릿속에 있다.


더 정교하게 해도 같다

클래스를 만들면 되지 않는가.

class FinancialReport:
    def __init__(self, company, metric, period, value, currency):
        self.company = company
        self.metric = metric
        self.period = period
        self.value = value
        self.currency = currency

report = FinancialReport("삼성전자", "매출", "2024-Q3", 79_000_000_000_000, "KRW")

나아졌다. 구조가 생겼다. 그러나 여전히 문제가 있다.

company가 문자열 “삼성전자"다. “Samsung Electronics"도, “SEC"도, “005930"도 같은 회사다. 이것을 코드가 아는가. 모른다. 문자열이 같은지 다른지만 비교할 수 있다.

metric이 문자열 “매출"이다. “매출"과 “매출액"과 “revenue"가 같은 것인가 다른 것인가. 코드는 모른다. 문자열이 다르니까 다른 것이다.

스키마를 정의하면 되지 않는가. Enum으로 회사 목록을 관리하고, 지표 목록을 관리하고. 된다. 작동한다.

그러면 이제 다음을 표현해보자.

“이순신은 위대했다.”

opinion = Opinion("이순신", "was", "위대했다")

이것은 무엇인가. 이순신이라는 문자열과 “위대했다"라는 문자열을 묶은 것이다. “이순신은 위대했다"를 표현한 것이 아니라 “이순신"과 “위대했다"를 저장한 것이다.

“위대했다"의 의미를 코드는 모른다. “위대했다"와 “훌륭했다"가 비슷한지, “비겁했다"와 반대인지, 코드는 알 수 없다.

재무 데이터처럼 정형화된 사실은 그나마 가능하다. 평가, 맥락, 관계, 추상적 서술은 프로그래밍 언어의 표현 범위 밖이다.


코드는 자기가 뭘 하는 코드인지 모른다

def process(data):
    result = []
    for item in data:
        if item["value"] > threshold:
            result.append(transform(item))
    return result

이 코드는 완벽하게 실행된다. 그런데 이 코드가 무엇을 하는 코드인가.

매출 데이터를 필터링하는 코드인가. 환자 기록을 선별하는 코드인가. 센서 데이터를 정제하는 코드인가.

코드 자체는 모른다. data, value, threshold, transform — 전부 추상적 이름이다. 이 코드가 재무 시스템의 일부인지 의료 시스템의 일부인지는 코드 바깥의 맥락에 의존한다.

주석(comment)으로 쓸 수 있다. 그러나 주석은 자연어다. 기계가 이해하지 못한다. 주석이 코드와 불일치해도 컴파일러는 모른다. 주석은 사람을 위한 것이지 기계를 위한 것이 아니다.

AI가 코드를 컨텍스트로 받을 때 이 문제가 직접적으로 나타난다. 코드가 자기 정체성을 갖고 있지 않으므로 AI가 매번 추론으로 정체성을 재구성해야 한다. 추론이므로 비용이 들고, 틀릴 수 있다.


근본적 이유

프로그래밍 언어가 세계를 서술하지 못하는 것은 설계의 결함이 아니다. 목적이 다르기 때문이다.

프로그래밍 언어의 목적은 기계에게 절차를 지시하는 것이다. “이 입력이 들어오면 이 연산을 수행하라.” 프로그래밍 언어의 의미론(semantics)은 실행의 의미론이다. 모든 구문이 “기계가 무엇을 하는가"로 해석된다.

x = 3은 “x라는 메모리 위치에 3을 저장하라"는 지시다. if x > 0은 “x가 0보다 크면 다음 블록을 실행하라"는 지시다. return x는 “x의 값을 호출자에게 돌려주라"는 지시다.

전부 동사다. 전부 행위다. 전부 절차다.

“삼성전자는 한국의 기업이다"는 동사가 아니다. 행위가 아니다. 절차가 아니다. 세계가 어떤 상태인지를 기술하는 것이다.

프로그래밍 언어에는 이것을 위한 자리가 없다. 변수에 저장할 수는 있지만, 그것은 기술이 아니라 저장이다. 저장된 값의 의미는 코드의 관할이 아니다.


JSON, YAML, XML은 어떤가

프로그래밍 언어가 아니라 데이터 형식은 어떤가.

{
  "company": "삼성전자",
  "metric": "매출",
  "period": "2024-Q3",
  "value": 79000000000000,
  "currency": "KRW"
}

구조는 있다. 필드가 명시적이다. 그러나 의미는 없다.

“company"가 기업을 뜻하는지 JSON은 모른다. “삼성전자"가 다른 어딘가의 “Samsung Electronics"와 같은 것인지 JSON은 모른다. 이 JSON 객체와 저 JSON 객체가 같은 엔티티에 대한 서술인지 JSON은 모른다.

JSON은 구조를 제공하지만 의미를 제공하지 않는다. 키-값 쌍이지, 엔티티-관계-속성이 아니다.

스키마를 정의하면 나아진다. JSON Schema, Protocol Buffers, GraphQL. 필드의 타입이 정의되고, 필수 여부가 정의되고, 참조가 정의된다.

그러나 이것들은 전부 특정 시스템을 위해 설계된 구조다. 범용적인 지식 표현이 아니다. 재무 데이터 스키마로 역사적 인물의 평가를 표현할 수 없다. 의료 데이터 스키마로 기업 간 경쟁 관계를 표현할 수 없다.

도메인마다 별도의 스키마. 스키마마다 별도의 도구. 스키마 간에 상호운용성이 없다.

이 한계는 왜 MD/JSON/XML로는 안 되는가에서 더 상세히 다룬다.


LISP는 어떤가

여기까지 읽은 사람 중 일부는 반례를 생각했을 것이다.

LISP.

(is 삼성전자 (company korea))
(revenue 삼성전자 2024-Q3 79000000000000)
(great 이순신)

S-expression은 트리 구조이고, 코드가 데이터이고 데이터가 코드다. 호모아이코니시티(homoiconicity).

실제로 초기 AI가 전부 LISP 기반이었다. SHRDLU, CYC, 전문가 시스템. LISP로 지식을 표현하고, 추론 엔진을 돌렸다. “프로그래밍 언어로 세계를 서술할 수 없다"에 대한 역사적 반증이 존재하는 것처럼 보인다.

그러나 세 가지 이유로 반례가 성립하지 않는다.

LISP가 아는 것과 프로그래머가 정한 것

(is 삼성전자 company)에서 is가 “~이다"라는 관계를 뜻한다는 것을 LISP는 모른다. 프로그래머가 그렇게 정한 것이다.

iszzz로 바꿔도 LISP는 상관없다. (zzz 삼성전자 company)는 LISP에게 완벽히 유효한 표현이다.

LISP는 구조를 제공한다. S-expression이라는 트리. 그러나 그 구조 안에 담긴 의미는 언어가 아니라 프로그래머가 부여한 것이다. JSON에서 키의 의미를 JSON이 모르는 것과 본질적으로 같다.

구조를 제공하는 것과 의미를 내장하는 것은 다르다.

CYC의 30년

이것을 가장 야심차게 시도한 프로젝트가 CYC다.

1984년 시작. LISP 기반으로 범용 지식을 표현하려 했다. 수백만 개의 규칙을 수작업으로 입력했다.

30년이 증명한 것은 가능성이 아니라 한계였다.

도메인마다 온톨로지를 수작업으로 설계해야 했다. 도메인 간 상호운용이 안 되었다. 자연어의 유연성을 따라가지 못했다. 규모가 커질수록 일관성 유지가 불가능에 가까워졌다.

LISP로 지식 표현을 “할 수 있다"는 것은 맞다. “잘 된다"는 것은 30년의 결과가 부정한다.

eval하지 않을 거면 LISP를 쓸 이유가 없다

가장 근본적인 문제.

LISP의 힘은 eval이다. 코드가 데이터이므로 데이터를 실행할 수 있다. 메타프로그래밍, 매크로, 런타임 코드 생성. 이것이 LISP를 LISP이게 만드는 것이다.

그런데 (is 삼성전자 company)eval하면 무슨 일이 일어나는가.

is라는 함수에 삼성전자company를 인자로 넘기는 함수 호출이 된다. 서술이 아니라 실행이 된다.

지식 표현으로 쓰려면 eval하지 않아야 한다. eval하지 않을 거면 LISP의 의미론을 사용하지 않는 것이다. S-expression이라는 구문을 빌려 쓰는 것일 뿐이다.

그것은 “LISP로 세계를 서술하는 것"이 아니라 “LISP의 괄호 표기법으로 데이터를 저장하는 것"이다.

LISP라는 프로그래밍 언어의 의미론 — 실행의 의미론 — 은 여전히 절차를 기술하기 위한 것이다. 구문을 빌려 쓴다고 의미론이 바뀌지 않는다.


세계를 서술하기 위한 언어의 조건

프로그래밍 언어가 절차를 기술하고, 데이터 형식이 구조를 제공하지만 의미가 없고, LISP조차 구문을 빌려 쓸 뿐 서술의 의미론을 갖지 않는다면.

세계를 서술하기 위한 언어는 무엇이 필요한가.

엔티티의 정체성. “삼성전자"가 고유한 식별자를 가져야 한다. “Samsung Electronics"와 같은 것임을 기계가 알아야 한다. 문자열 비교가 아니라 정체성의 동일성.

관계의 표현. “삼성전자는 한국의 기업이다"에서 “한국의 기업"이라는 관계를 표현할 수 있어야 한다. 변수 할당이 아니라 관계의 기술.

서술의 자기 기술. 이 서술이 무엇에 대한 것인지, 누가 말한 것인지, 언제 기준인지, 얼마나 확실한지가 서술 자체에 포함되어야 한다. 코드 바깥이 아니라 코드 안에.

도메인 독립성. 재무 데이터도, 역사적 사실도, 주관적 평가도, 추상적 관계도 같은 형식으로 표현할 수 있어야 한다. 도메인마다 별도의 스키마가 아니라 하나의 범용 구조.

프로그래밍 언어는 이 네 가지를 갖고 있지 않다. 프로그래밍 언어는 이것을 위해 만들어지지 않았기 때문이다. 절차를 기술하기 위해 만들어졌다.

자연어는 이 네 가지를 전부 할 수 있다. 모호하게. 필요한 것은 자연어의 표현 범위와 프로그래밍 언어의 정밀함을 결합한 것이다.


요약

프로그래밍 언어는 모호하지 않고, 검증 가능하고, 튜링 완전하다. 그러나 세계를 서술하지 못한다.

프로그래밍 언어는 절차를 기술한다. “이 입력이 들어오면 이것을 하라.” 전부 동사고 행위다. “삼성전자는 한국의 기업이다"는 행위가 아니다. 프로그래밍 언어에 이것을 위한 자리가 없다.

코드는 자기 정체성을 모른다. 자기가 어떤 도메인에 속하는지, 어떤 목적을 수행하는지, 코드 안에 기록되어 있지 않다.

JSON, YAML 같은 데이터 형식은 구조를 제공하지만 의미가 없다. LISP는 구문을 빌려 쓸 수 있지만 서술의 의미론은 없다. CYC가 30년간 LISP 기반 지식 표현을 시도했고, 증명한 것은 한계였다.

세계를 서술하려면 엔티티의 정체성, 관계의 표현, 서술의 자기 기술, 도메인 독립성이 필요하다. 프로그래밍 언어는 이것을 위해 만들어지지 않았다. 자연어는 이것을 할 수 있지만 모호하다. 필요한 것은 둘 사이의 어딘가에 있다.