Lisp로 가는 길: 왜 Lisp인가
요약
Lisp는 단순한 문법을 넘어, 언어 자체를 문제에 맞게 확장하는 사고방식이 핵심입니다. 코드와 데이터를 리스트로 다루는 동형성 덕분에 매크로를 통해 새로운 제어 구조나 문법 추상화를 만들 수 있습니다. Lisp 개발은 REPL 주도 개발 방식으로 진행되어 프로그램의 지속적인 진화가 가능합니다.
핵심 포인트
- Lisp의 핵심은 언어를 문제에 맞게 확장하는 사고방식입니다.
- 코드와 데이터 모두 리스트로 표현되는 동형성이 매크로 구현을 가능하게 합니다.
- REPL 주도 개발 방식으로, 프로그램을 중단 없이 진화시킬 수 있습니다.
- 매크로는 인자를 평가하지 않고 코드를 보존하여 언어의 구조적 확장을 이룹니다.
- Lisp는 괄호 문법보다
언어를 문제에 맞게 확장하는 사고방식이 핵심이며, REPL·패키지·심볼·조건·재시작까지 익혀야 진가가 드러남 - 코드와 데이터가 모두 리스트로 표현되는
동형성 덕분에, 매크로는 평가 전 코드를 데이터처럼 다뤄 새 제어 구조와 문법 추상화를 만들 수 있음 - Common Lisp에 없는
while
도 defmacro
로 추가할 수 있지만, 함수로 흉내 내면 인자가 즉시 평가되어 body
가 코드가 아니라 값이 되는 차이가 생김
- Lisp 개발은 실행 중인 프로세스에 코드를 계속 평가하는
REPL 주도 개발에 가까워, 함수·매크로·변수를 재정의하며 프로그램을 중단 없이 진화시킬 수 있음 - 언어 확장성과 라이브 시스템이 결합되면 내부 DSL을 사용자에게 노출하는
확장 가능한 소프트웨어를 만들 수 있고, AutoLISP와 Emacs가 대표 사례임
Lisp가 요구하는 다른 프로그래밍 감각
- Lisp를 처음 접한 프로그래머는 괄호가 많은 문법, 독특한 들여쓰기,
format
의 첫 번째 인자로 표준 출력을 지정하는 방식에서 낯섦을 느낌
-
Lisp를 익히려면 코드 읽기뿐 아니라 패키지와 심볼, 프로젝트 생성, 라이브러리 가져오기,
REPL, 조건과 재시작까지 다뤄야 함 -
더 큰 변화는 알고리듬을 구성하는 방식에서 나타남
-
Lisp는 다른 언어에서 하기 어려운 일을 가능하게 만들고, 알고리듬의 형태도 달라지게 함
-
문제에 맞춰 언어를 먼저 키운 뒤, 그 언어로 프로그램을 작성하는 흐름으로 이어짐
-
Blub paradox는 덜 강력한 언어만 써 본 프로그래머가 Lisp의 강점을 알아보기 어려운 이유를 설명하는 개념임
확장 가능한 언어
- Lisp는 스스로를 확장할 수 있으며, Lisp 전문가들은 이를
programmable programming language라고 부름 - 핵심 도구는
macro
임
-
C, Rust, Swift의 매크로처럼 반복 코드 제거에만 머물지 않음
-
Lisp 매크로는 언어의 일부가 되는 새 구성 요소를 만들 수 있음
-
Common Lisp에는 기본
while
연산자가 없지만, defmacro
로 직접 추가 가능함
(defmacro while (condition &body body)
`(loop while ,condition do
(progn ,@body)))
- 이
while
매크로는 condition
이 참인 동안 body
의 여러 명령을 실행함
loop
는 복잡한 반복을 작성하는 매크로임
progn
은 여러 표현식을 순서대로 실행하고 마지막 표현식의 결과를 반환함
- 결과적으로
(loop while ... do (progn ...))
대신 (while ...)
형태를 쓸 수 있어, 코드가 짧아지고 C의 while
과 비슷한 구조가 됨
매크로와 함수의 결정적 차이
- 같은 동작을 함수
fake-while
로 만들면 실패함
(defun fake-while (condition body)
(loop while condition do
(funcall body)))
fake-while
호출에서는 인자가 먼저 평가됨
condition
은 참값 t
가 됨
progn
내부의 (print counter)
가 실행되고 (decf counter)
가 2
를 반환함
body
는 코드 블록이 아니라 값 2
가 됨
- 이후
funcall
은 함수를 기대하지만 2
를 받기 때문에 The value 2 is not of type FUNCTION
같은 타입 오류가 발생함
- 반면 매크로
while
은 condition
과 body
를 즉시 평가하지 않고 보존함
macroexpand-1
로 확장 결과를 확인할 수 있음
- 컴파일러는 확장된
(LOOP WHILE ... DO (PROGN ...))
형태의 코드를 받음
- 매크로는 함수와 달리 인자를 미리 평가하지 않고
순수한 데이터로 다루기 때문에 이런 변환이 가능함
리스트, s-expression, 동형성
-
Lisp 프로그램은
symbolic expression, 즉 s-expression의 연속으로 구성됨 -
s-expression은 두 가지로 나뉨
-
원자(atom): 숫자, 문자열, 심볼 같은 데이터 단위
-
리스트(list): 원자나 다른 리스트를 담는 컬렉션
-
Lisp라는 이름은 LISt Processing에서 왔고, 프로그램을 구성하는 명령들도 리스트로 표현됨
-
Lisp에서 리스트는 주요 자료구조이면서 코드 작성 방식이기도 함
-
코드와 데이터가 모두 리스트로 표현되는 성질을
동형성(homoiconicity) 이라고 함 -
같은
(+ 1 2)
리스트도 평가 여부에 따라 다르게 동작함
(+ 1 2)
는 코드로 평가되어 3
을 반환함
'(+ 1 2)
는 평가되지 않고 데이터 리스트 그대로 반환됨
- Lisp는 Polish notation을 사용해 함수 이름을 리스트의 첫 요소에 두고, 뒤에 인자를 둠
- 코드와 데이터의 경계가 얇기 때문에 매크로는 소스 코드를 변환할 수 있고,
프로그램을 쓰는 프로그램을 만들 수 있음
매크로가 만드는 코드 추상화
- 언어를 확장하면 특정 프로그램 영역에 더 표현력 있는 새 구성 요소를 만들 수 있음
- 반복되는 보일러플레이트는 커스텀 매크로로 대체해 코드를 줄이고 시간을 절약함
- 새 제어 구조를 만들어 리소스 접근과 폼 평가를 제어할 수 있음
- 매크로는 코드를 생성하고 성능을 개선하며, 복잡하고 오류가 나기 쉬운 코드를 감추는
문법 추상화를 제공함
Lisp는 라이브 시스템
-
Lisp는 단순한 프로그래밍 언어라기보다
실행 중인 라이브 시스템으로 다뤄짐 -
일반적인 언어에서는 에디터에서 코드를 작성한 뒤 컴파일하고 실행해 관찰·테스트·디버깅하는 흐름이 많음
-
Lisp에서는 먼저 Lisp 프로세스를 시작하고, REPL에 붙인 뒤, 프로젝트를 그 프로세스 안에 로드함
-
REPL은 Read-Eval-Print Loop의 약자로, 코드를 평가하고 즉시 결과를 보는 대화형 환경임
-
현재 프로그램을 실행 중인 Lisp 프로세스를 들여다보는 창 역할을 함
-
Lisp 개발자는 실행 중인 프로세스 안에서 코드를 계속 평가하고 결과를 REPL에서 바로 확인함
-
개별 함수 테스트
-
데이터베이스 질의
-
변수 검사
-
디버깅 같은 작업도 REPL에서 수행 가능함
-
Lisp 프로세스는 멈출 필요가 없어 몇 주 동안 살아 있을 수 있음
-
함수, 매크로, 변수는 내부 메모리인 환경(environment)에 계속 정의·재정의되며, 이런 흐름을
REPL 주도 개발이라고 부름
Lisp에서의 핫 리로딩
-
웹 프런트엔드에서는 파일 저장이나 페이지 새로고침을 통해 JavaScript 코드가 핫 리로드되는 경험이 흔함
-
일반적인 핫 리로딩은 파일 감시, 변경 모듈 재컴파일, 웹소켓을 통한 변경 주입 같은 별도 도구와 기법에 의존함
-
모든 소프트웨어 스택이 같은 수준의 핫 리로딩을 제공하지는 않음
-
일부 백엔드 프레임워크는 재컴파일이 필요함
-
데스크톱과 모바일 개발 환경은 상황이 섞여 있음
-
저수준 언어는 매번 전체 컴파일이 필요함
-
Lisp에서는 핫 리로딩이 별도 도구가 아니라 라이브 환경에서 코드를 평가할 때 자연스럽게 따라옴
-
심볼을 재정의하면 함수, 데이터, 매크로 모두 새 정의를 사용함
-
멈추고 컴파일하거나, 테스트를 별도로 실행하거나, 특수한 디버거를 쓸 필요가 줄어듦
-
Lisp 프로그래밍은 프로그램을 조립한다기보다
진화시키는 방식에 가까움
확장 가능한 소프트웨어
-
언어 확장성과 라이브 시스템은 Lisp에서
확장 가능한 소프트웨어를 만들기 쉽게 함 -
일반적인 데스크톱 프로그램은 플러그인이나 스크립팅 언어를 통해 확장됨
-
개발자는 확장 시스템을 설계하고 유지해야 함
-
사용자는 플러그인이나 확장 작성을 위한 특정 API를 배워야 함
-
Lisp에서는 매크로로 동작을 캡슐화하고 재사용하기 쉽게 만들면서 자연스럽게 DSL을 작성하게 됨
-
소프트웨어를 확장 가능하게 만들려면 내부에서 만든 DSL을 사용자에게 쓰게 하면 됨
-
CMS 서버 예시에서는 서버 사이드 웹페이지 생성을 위한
html
매크로를 사용자에게 노출할 수 있음
- 사용자는 Lisp 변수,
dolist
반복, format
문자열 포매팅을 그대로 함께 사용할 수 있음
- 전통적 템플릿 언어처럼
{{ user_name }}
이나 {% for %}
같은 별도 문법을 배울 필요가 없음
- 이 DSL은 Lisp의 조건문, 재귀, 고차 함수, REPL 단계별 디버깅 능력을 그대로 물려받음
- 수학 그래프 소프트웨어 예시에서는 수식과 그리기를 위한 DSL을 노출해, 사용자가 곡선과 점을 적은 코드로 그릴 수 있음
실제 사례와 Lisp의 위치
-
AutoCAD는 반복 작업 자동화와 복잡한 기하 생성에
AutoLISP를 사용함 -
Emacs는 자체 Lisp 방언으로 대부분 구현되어 높은 확장성을 확보한 텍스트 에디터임
-
PDF 리더
-
웹 브라우저
-
이메일 클라이언트
-
RSS 리더
-
터미널 멀티플렉서
-
Git 클라이언트
-
음악 플레이어
-
채팅 클라이언트
-
스프레드시트
-
IRC 클라이언트
-
데스크톱 창 관리자까지 확장된 사례가 나열됨
-
R. M. Stallman은 Lisp를 강력하고 우아한 프로그래밍 언어의 의미를 이해하게 해주는 언어로 평가함
-
Lisp가 언젠가 기본 언어처럼 널리 쓰일 것이라는 기대는 실현되지 않았고, 앞으로도 그럴 가능성은 낮아 보임
-
그럼에도 Lisp는 1960년대부터 살아남아 Fortran 다음으로 오래된 현역 프로그래밍 언어가 됨
-
Lisp의 가치는 확장성, 대화형 환경, REPL 같은 개별 기능 하나가 아니라
그 조합에서 나옴
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기