GN⁺: 호모아이코닉(Homoiconic) 파이썬 언어
(aljamal.substack.com)McCarthy의 "Lisp in Lisp"를 Python으로 구현하기
-
1960년대 초반 John McCarthy가 개발한 Lisp은 homoiconicity 특성을 가지고 있어서 코드와 데이터가 서로 교환 가능함
- Lisp에서는 코드와 데이터의 경계가 모호해짐
- 이를 통해 Lisp은 자기 자신을 자연스럽게 표현할 수 있음
-
Lisp 1.5 매뉴얼 13 페이지 하단의 코드 반 페이지는 Lisp 그 자체였음
- Alan Kay는 이를 "소프트웨어의 맥스웰 방정식"이라고 칭했음
- 프로그래밍의 전체 세계가 불과 몇 줄의 코드에 압축되어 있음
-
이를 이해하기 위해 Python을 이용해서 "Lisp in Lisp" 코드를 재구현해보는 것이 좋은 방법임
- 대부분의 프로그래머는 Lisp 문법에 익숙하지 않지만 Python 문법은 익숙할 것임
- Lisp 코드의 정신을 최대한 유지하면서 Python으로 재작성하는 것이 목표
Lisp의 M-expression과 S-expression
-
Lisp은 원래 두 가지 구문 flavors를 가지고 있었음
- M-expression (메타 표현식): 코드 flavor
- S-expression (기호 표현식): 데이터 flavor
- 둘은 의미적으로 동일함
-
"Lisp in Lisp" 코드는 M-expression으로 작성되었고, S-expression Lisp을 구현함
-
Lisp M-expression을 Python 코드 구조로 변환하고, S-expression은 Python 리스트로 표현하는 것이 한 가지 방법임
- Lisp은 List Processing의 약자로, 리스트라는 단일 데이터 구조만 사용함
1차 구현
-
Python 리스트 기본 함수들로 Lisp의 기본 연산들을 구현함
- atom(x): x가 리스트인지 확인
- eq(x,y): x와 y가 같은지 확인
- car(x): 리스트의 첫번째 요소 반환
- cdr(x): 리스트의 나머지 부분 반환
- cons(x,y): 원자를 리스트에 추가
- append(x,y): 두 리스트를 합침
-
몇 가지 재귀 기본 요소들은 무시하고, Llama3-70b의 도움을 받아 "Lisp in Lisp" 코드 서브셋에 대한 인터프리터를 빠르게 구현할 수 있음
2차 구현
-
1차 구현에서는 람다 기능이 빠져있음
- 람다는 Lisp에서 함수를 정의하고 호출하는 주요 방법임
- 람다 없이는 재귀를 구현할 수 없고, 재귀 없이는 튜링 완전하지 않음
-
람다를 구현하려면 assoc(x,y)와 pairlis(x,y) 기본 요소가 필요함
- assoc(x,y)는 연관 리스트를 사용한 key/value 사전 lookup
- pairlis(x,y)는 Python의 zip(x,y)와 같음
-
Lisp은 loop이 없어서 간단한 선형 검색에도 재귀를 사용해야 했음
- 하지만 Python에서는 리스트 컴프리헨션을 사용해 우아하게 변환 가능
- evcon과 evlis도 마찬가지로 loop으로 변환 가능
-
eval 함수는 원래 Lisp에서 두 개의 인수를 받음
- 첫째는 표현식(s-exp), 둘째는 환경(environment)으로 key/value 리스트
- 환경은 LAMBDA의 변수 바인딩을 유지함
- 동적 스코핑(dynamic scoping) 기법 사용
GN⁺의 의견
-
Lisp의 homoiconicity 특성을 Python으로 모방한 흥미로운 시도임. 하지만 Lisp만의 고유한 특성을 완벽히 옮기는 데는 한계가 있어 보임. Lisp의 매력을 경험하려면 역시 Lisp 자체를 배우는 것이 가장 좋을 듯함.
-
람다 함수와 동적 스코핑 등 Lisp의 강력한 기능들을 Python으로 구현한 것은 인상적임. 하지만 실제 프로젝트에 적용하기에는 부족한 점이 많아 보임. 교육용이나 연구 목적으로는 가치가 있어 보임.
-
이런 시도 자체가 프로그래밍 언어의 본질에 대해 깊이 생각해 볼 수 있는 계기가 될 수 있음. 언어의 문법과 구현을 떠나 프로그래밍 패러다임의 관점에서 바라보는 시각을 가질 수 있음.
-
Lisp 계열 언어를 배우는 것이 함수형 프로그래밍과 메타 프로그래밍에 대한 통찰을 얻는데 도움이 될 수 있음. Scheme, Clojure, Racket 등 현대적인 Lisp 계열 언어들을 살펴보는 것도 좋겠음.