2P by neo 4달전 | favorite | 댓글 1개

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 계열 언어들을 살펴보는 것도 좋겠음.

A dialect of Lisp that's embedded in Python
https://hylang.org/