01장. 궂이 왜? 혹은, 반론에 대한 답변
누구나 Lisp를 알고 있지 않나요? 우리 중 많은 사람이 Lisp를 포함한 세 개 이상의 서로 다른 언어들을 소개하는 강의를 수강한 적이 있습니다. 저 역시 1975 년경 Lisp를 처음 접했을 때 그랬습니다. 당시에는 이 언어가 거의 쓸모없다고 생각했죠. 일반적인 방식으로 아무것도 하지 못했고, 느렸으며, 그 괄호들만으로도 누구나 미쳐버릴 것 같았습니다!
만약 여러분의 Lisp 경험이 1985년 경 이전이라면, 아마도 저와 같은 견해를 공유하고 계실 것입니다. 하지만 1984년은 ’빅 브라더’가 실제로 현실화되지 않았던 해 (그렇긴 했나요?), 첫 번째 첨단 (하지만 오늘날의 기준으로는 초라한) 매킨토시가 대량 출하되기 시작한 해였습니다. 바로 그해 Lisp 세계는 변하기 시작했습니다. 불행하게도 대부분의 프로그래머들은 이를 눈치채지 못했습니다. Lisp의 운명은 인공지능 (AI) 과 결부되어 있었는데, Lisp가 성숙해가는 시기에 AI 는 급격히 쇠퇴하고 있었습니다 – 바로 ’AI 의 겨울’이었던 것입니다. 어떤 이들은 이것이 Lisp에게 불운이었다고 말합니다. 하지만 저는 다른 동적 언어들에 대한 관심의 부활과 실무자 및 벤더들이 함께 고민해 온 문제들을 바라보며, Lisp가 너무도 앞서 있었는지도 모른다고 생각합니다.
수년에 걸쳐 제 Lisp에 대한 견해는 변했습니다. 이제는 단순히 제가 가장 좋아하는 프로그래밍 언어일 뿐만 아니라, 프로그래밍에 관한 저의 사고를 구조화하는 방식이 되었습니다. 이 책이 제 열정을 전달하고, 어쩌면 여러분의 Lisp에 대한 의견도 바꿔주기를 바랍니다.
아래에는 Lisp에 대한 일반적인 반론들을 나열했습니다. 이들은 동료들, 지인들, 관리자들과 저 자신의 과거 경험에서 비롯된 것입니다. 각 항목마다 실제로 사실인 부분이 무엇인지, 관점의 차이인지, 그리고 초기 Lisp 구현의 암흑 시절에서 남아있는 유산인지에 대해 설명하겠습니다. 가능한 한 다른 언어들과 비교하는 것은 피하려고 합니다. Lisp는 독자적인 방식을 가지고 있으며, 여러분이 Lisp를 평소 사용하는 언어만큼 잘 이해하게 되면 스스로 비교해 보실 수 있을 것입니다. 결국 Lisp가 언제 적합한지 알 정도로 Lisp를 이해하거나, 여러분의 개인 도구상자에 Lisp의 자리를 찾게 된다면, 제가 할 일을 다한 것입니다.
더 이상의 소개 없이, 여러분이 Lisp를 피하고 있는 13 가지 이유를 제시합니다:
예전에 Lisp를 봤는데 도대체 이해하지 못하겠다.
이것은 정말 어려운 문제입니다. 대부분의 프로그래밍 언어는 서로 더 비슷할 뿐, Lisp와는 많이 다릅니다. 컴퓨터 언어의 계보를 살펴보면, 오늘날 가장 널리 사용되는 언어들이 알골 (Algol) 계열의 후손임을 알 수 있습니다. 알골 계열 언어들의 공통 특징으로는 식을 위한 대수적 표기법, 변수의 가시성을 제어하는 블록 구조, 그리고 값이나 효과를 위해 서브루틴을 호출하는 방식 등이 있습니다. 이러한 개념들을 이해하면, 다른 알골 계열 언어를 시작할 때 키워드 이름과 문장 부호 스타일 같은 표면적인 차이점만 공부해도 됩니다.
하지만 Lisp는 정말 다릅니다. 만약 여러분이 오직 알골 계열 언어의 코드만을 읽어보셨다면, Lisp 코드를 이해하는 데 도움이 될 친숙한 문장 부호나 블록 구조를 찾을 수 없습니다. 대신 낯선 이름들이 의미 없어 보이는 괄호들의 중첩 속에 나타날 뿐입니다. Lisp에서 괄호가 바로 그 문장 부호입니다. 다행히도 그 사용법은 매우 단순합니다. 예를 들어 C나 파스칼의 연산자 우선순위 규칙을 기억하는 것보다 훨씬 간단하죠. Lisp 개발 환경은 심지어 여는 괄호와 닫는 괄호를 매칭해 주는 편집기를 제공하기도 합니다.
Lisp 표현식(expressions) 이 어떻게 구성되는지 이해한 후에는, 그 식들이 무엇을 의미하는지를 배워야 합니다. 이는 더 어렵습니다. 왜냐하면 Lisp에는 다른 곳에서는 찾아볼 수 없는 많은 기능들을 제공하거나, 익숙한 개념에 낯선 이름을 붙여주기 때문입니다. Lisp를 정말로 이해하려면 내부 작동 원리를 알아야 합니다. 대부분의 훌륭한 프로그래머들처럼, 여러분도 컴퓨터가 어떻게 작동하는지, 그리고 선호하는 컴파일러가 선호하는 언어의 문장을 기계어로 변환하는지에 대한 마음속 모델(mental model) 을 가지고 계실 것입니다. 하지만 Lisp는 기계 조직의 세부 사항으로부터 사용자를 격리시키려 애쓰기 때문에, 이 접근법을 Lisp에 적용하면 스스로를 미치게 만들 수 있습니다. 네, 여러분은 일부 제어권을 포기하게 됩니다. 놀랍게도 프로그램이 컴파일러를 통해 기계의 비트로 어떻게 매핑되는지 걱정하는 것을 멈추면, 프로그램의 정확성(correctness) 측면에서 상당한 이점을 얻게 됩니다. 그 대가가 합리적인가요? 이에 대해서는 나중에 살펴보도록 하겠습니다.
이 책은 여러분에게 Lisp를 읽고 쓰는 법을 알려 줄 것입니다. 또한 DEFUN, CONS, FLET 과 같은 새로운 단어들을 인식하고 이해하는 법, 그리고 궁극적으로는 선호하는 프로그래밍 언어로 생각하는 것처럼 Lisp로 사고하는 법까지 알려드릴 것입니다.
괄호때문에 프로그램이 눈에 안들어 온다.
이 문제 중 한 부분은 익숙하지 않기 때문입니다. 이전 절에서 이에 대해 이야기했습니다.
다른 부분이 진짜입니다: 많은 괄호들을 처리해야 한다는 점입니다. 다행히 Lisp 프로그래밍 환경의 편집기에는 괄호의 짝을 깜빡이거나 강조 표시하거나, 괄호가 균형을 이루는 표현식 전체를 조작하는 방식으로 괄호의 개수를 일일이 세는 작업을 자동화해 주는 기능이 있다. 마지막으로, 스타일 문제도 있습니다. 적절하게 들여쓰기를 하면 다른 언어에서와 마찬가지로 Lisp 프로그램의 가독성이 향상됩니다. 하지만 줄바꿈(vertical whitespace)은 Lisp에서 가독성을 떨어뜨리는 경우가 많습니다.
이 책에서는 Lisp 코드의 기계적 측면과 스타일적 측면 모두를 다룰 것입니다. 이 책을 다 읽을 때쯤이면 어떤 코드가 읽기 좋은 코드인지 나름의 기준이 생기고, 자신의 생각을 논리적으로 설명하고 뒷받침할 수 있게 될 것입니다. 이러한 자신감이 생겼을 때, 여러분은 미학적으로 아름다운 Lisp 코드를 작성할 수 있을 뿐만 아니라 다른 사람의 코드도 읽을 수 있게 될 것입니다. 그러면 더 이상 괄호가 걱정거리가 되지 않을 것입니다.
Lisp로 프로그램 짜는 사람이 없다.
뭐? 나만 남았나? 그렇지 않을 것 같은데…
진지하게 말하자면, 매일 Lisp 코드를 작성하는 사람들이 꽤 많습니다. 이들은 어려운 문제를 해결하는 프로그램을 작성하며, 고용주에게 전략적 우위를 제공합니다. 새로운 고용주로 이직할 의향이 있는 훌륭한 Lisp 프로그래머를 찾기 어렵습니다; Lisp을 사용하는 기업들은 그들의 전략적 우위와 그들의 Lisp 프로그래머들을 집요할만큼 보호합니다.
한데, 스프레드시트, 데이터베이스, 워드 프로세서, 게임 같은 일반 소비자용 제품에서는 Lisp를 찾아보기 어려운 것이 사실입니다. 하지만 애초에 그런 종류의 작업은 Lisp가 가장 뛰어난 성능을 발휘하는 분야가 아닙니다. Lisp는 복잡한 시스템과 프로세스를 분석하고 제어해야 하는 제품에서 찾아볼 수 있습니다. 이런 분야에서는 여러 출처에서 얻은 다양한 종류의 데이터 사이에 존재하는 복잡한 관계를 바탕으로 유용한 결론을 신뢰성 있게 도출하는 능력이, 번개처럼 빠른 수치 계산이나 화려한 그래픽보다 더 중요하기 때문입니다(오늘날의 Lisp 시스템은 후술한 두 분야에서도 선두 주자에 근접한 수준에 이르게 될 지 도 모르겠습니다)
Lisp는 단순하고 일관된 문법을 갖추고 있으며, 시스템 설계자가 새로운 언어를 처음부터 만들지 않고도 Lisp에 새로운 함수를 추가할 수 있기 때문에 확장 언어로도 사용됩니다. 이러한 용도의 대표적인 사례로는 Emacs 편집기와 AutoCAD 설계 프로그램이 있습니다.
물론 Lisp은 여전히 인공 지능과 고급 컴퓨터 언어 설계 연구에서 가장 자주 사용되는 언어이지만, 이 책에서는 이 두 주제 중 어느 것도 다루지 않을 것입니다. 이 책을 마치면, Lisp을 사용하여 해결해야 할 문제와 솔루션 설계를 어떻게 접근해야 하는지에 대한 필요한 지식을 갖추게 될 것입니다.
아, 그리고 한 가지 더: 모든 대량 시장 제품이 Lisp을 사용하지 않는다는 것은 완전히 사실이 아닙니다. 초보 컴퓨터 사용자를 위한 Microsoft의 “Bob” 환경은 Lisp으로 개발(그리고 제공)되었습니다.
Lisp은 그래픽 인터페이스를 제공해주지 않는다.
이는 아이러니합니다. 최초의 그래픽 사용자 인터페이스 중 일부는 1970년대 초 Lisp 머신에 등장했습니다. 사실, 1995년에도 당신은 이 초기 Lisp 환경들 중 하나의 DOS 적응 버전을 구매할 수 있습니다 – 20년 전과 동일한 GUI를 가지고 있죠.
Windows와 Macintosh를 위한 주요 Lisp 개발 환경은 각 운영체제의 GUI 기능 중 일부만 지원합니다. 지원하지 않는 기능을 추가하는 것도 가능하지만, Microsoft와 Apple이 각각 권장하는 언어인 C++를 사용하는 편이 더 쉬울 것 입니다.
Windows와 Macintosh 모두 동일한 그래픽 사용자 인터페이스를 원한다면, 이를 가능케 해 주는 Lisp용 윈도잉 환경을 적어도 두개 정도 찾을 수 있습니다. 문제는 이렇게 만든 Lisp GUI는 Macintosh 사용자에게도 Windows 사용자에게도 익숙하지 않은 인터페이스가 될 것이라는 점입니다.
특정 플랫폼에 맞는 간단한 그래픽 인터페이스만 빠르게 만들고 싶다면, 상용 Lisp 환경 중 어느 것을 사용하더라도 필요한 기능을 제공해 줄 것입니다. 있다. 상용 개발 환경에는 모두 마우스로 항목을 가리켜 클릭하거나 끌어다 놓는 방식으로 창과 대화상자를 만들 수 있는 그래픽 인터페이스 빌더를 가지고 있습니다. 다만, 화려하고 다양한 부가 기능까지는 기대하지 않는 것이 좋습니다.
Lisp에서 다른 사람이 작성한 코드를 호출할 수 없다.
이는 대부분 사실이 아닙니다. 대부분의 Lisp 개발 환경은 C 또는 Pascal의 호출 규약을 사용해 외부 함수를 호출하는 방법을 제공합니다. 외부 프로그램에서 다시 Lisp 코드를 호출하는 것도 가능합니다.
다만 Lisp에서 C++ 코드를 호출하려면, 대개 해당 C++ 코드를 감싸는 C 래퍼(wrapper)를 작성해야 합니다.
Lisp의 가비지 컬렉터 때문에 예측 불가능한 멈춤 현상이 발생한다.
이 내용은 아마도 “Lisp는 느리다“라는 논의에서 다루는 편이 좋겠지만, 별도의 주제로 다룰 만큼 흥미로운 이야기가 많습니다. Lisp 프로그램에서는 메모리에 있는 객체를 가리키는 모든 참조가 사라지면 가비지가 발생합니다. 다른 언어로 작성한 프로그램에서는 마지막 참조가 사라지는 시점에 프로그래머가 해당 객체가 차지하던 메모리를 해제하도록 처리해야 합니다: 이 작업을 확실하게 처리하지 못하면 메모리 누수가 발생합니다. 결국 프로그램이 사용할 수 있는 메모리 공간이 더 이상 접근할 수 없는 객체들로 가득 차서, 프로그램이 계속 실행되는 데 필요한 여유 메모리가 부족해질 수 있습니다. 동적 메모리를 많이 할당하고 직접 재사용하도록 관리하는 복잡한 프로그램을 작성해 본 적이 있다면, 이것이 얼마나 까다로운 문제인지 잘 알 것입니다.
Lisp는 사용하지 않는 메모리를 프로그래머가 직접 해제하지 못하게 하는 방식으로 메모리 누수 문제를 우회합니다. 컴퓨터는 어떤 메모리 블록에 더 이상 접근할 수 있는 경로가 남아 있지 않은지를 정확하게 판별할 수 있다는 것이 핵심 아이디어입니다. 이렇게 더 이상 접근할 수 없는 메모리 블록을 **가비지(garbage)**라고 합니다. 프로그램의 어떤 부분에서도 더 이상 쓸모가 없기 때문입니다. 가비지 컬렉터는 자동으로 실행되어 이렇게 사용되지 않는 메모리 블록을 모으고, 다시 사용할 수 있도록 준비합니다. 이를 구현하는 알고리즘은 상당히 복잡하지만, Lisp 시스템에는 이러한 기능이 기본적으로 내장되어 있습니다.
역사적으로 가비지 컬렉션은 속도가 느렸습니다. 초기의 가비지 컬렉터는 말 그대로 시스템을 몇 시간씩 멈춰 세우기도 했습니다. 성능이 워낙 좋지 않았기 때문에 초기 Lisp 프로그래머들은 메모리가 완전히 바닥날 때까지 가비지 컬렉션을 꺼 둔 채 프로그램을 실행하곤 했습니다. 그러다가 메모리가 모두 소진되면 직접 가비지 컬렉션을 실행하고, 그날 남은 시간 동안 집으로 돌아가곤 했습니다.
지난 20년 동안 가비지 컬렉터의 성능을 개선하기 위해 여러 우수한 소프트웨어 공학 기법이 적용되었습니다. 현대적인 Lisp 시스템은 가비지를 한꺼번에 처리하기 위해 기다리는 대신, 거의 연속적으로 조금씩 가비지 컬렉션을 수행합니다. 그 결과, 성능이 매우 낮은 데스크톱 컴퓨터에서도 가비지 컬렉션으로 인한 멈춤 시간은 대개 1~2초를 넘지 않습니다.
이 책의 뒷부분에서는 가비지 컬렉션을 더 자세히 살펴보고, 가비지 생성을 줄이는 기법도 소개할 것입니다. 프로그램이 생성하는 가비지가 적을수록 가비지 컬렉터가 처리해야 할 작업도 줄어들게 됩니다.
Lisp는 너무 방대한 언어다.
약 1,000페이지에 달하는 Common Lisp: The Language나 그보다 더 두꺼운 최신 ANSI 표준 문서인 X3.226: Programming Language Common Lisp를 살펴보면, Lisp가 방대한 언어라는 생각이 들기 쉽습니다.
하지만 Lisp에는 사실상 문법이라고 할 만한 것이 거의 없고, 스페셜 폼(special form)이라고 부르는 기본적인 언어 요소도 불과 수십 개에 불과하다는 점을 고려하면, Lisp는 오히려 매우 작은 언어처럼 보이기 시작합니다.
사실 앞서 언급한 문서들은 대부분 다른 언어라면 라이브러리 함수라고 부를 만한 기능을 설명하고 있으며, 그보다 적은 비중으로 개발 도구에 대해서도 다루고 있습니다. 여러분이 가장 좋아하는 프로그래밍 언어의 언어 매뉴얼을 가져와 봅시다. 여기에 서드파티 라이브러리 서너 개의 매뉴얼을 더해 봅니다. 개발 유틸리티, 고급 자료구조, 범용 입출력 기능 등을 제공하는 라이브러리들 말입니다. 그런 다음 브라우저, 인스펙터, 디버거 등 개발 도구의 매뉴얼도 전부 가져와 쌓여 가는 더미에 보태 봅니다. 이제 전체 페이지 수를 세어 봅니다. 그래도 1,000페이지가 여전히 엄청난 분량으로 느껴질까요?
이 책을 다 읽을 때쯤이면 매뉴얼이 있든 없든, Lisp에서 필요한 정보를 어디서 어떻게 찾아야 하는지 알게 될 것 입니다.
Lisp는 인공지능 연구에만 사용하는 언어다.
전혀 사실이 아닙니다. Lisp는 한 명 또는 소수의 프로그래머가 개발해야 하는 대규모 프로젝트에 사용됩니다. 또한 요구 사항이 명확하게 정의되지 않았거나, 적절한 해결책을 찾기 위해 여러 가지 실험을 거쳐야 하는 작업에도 적합합니다.
인공지능 분야가 바로 이러한 조건을 모두 충족한다는 사실을 알 수 있습니다. 하지만 이런 조건에 해당하는 분야는 인공지능뿐만이 아닙니다. 공장의 작업 일정 관리, 운송 경로 계획, 군수 물류, 소나 및 지진파의 반향에서 특징 추출, 외환 거래, 컴퓨터 및 컴퓨터 네트워크 구성, 산업 공정 진단 등 다양한 분야에서도 Lisp가 활용됩니다.
이러한 분야는 대중 시장을 겨냥한 애플리케이션은 아니지만, 이를 개발하는 조직에 상당한 수익을 안겨 줍니다. 비용 절감을 통해 수익을 창출하는 경우도 많습니다.
Lisp에는 쓸 만한 프로그래밍 도구가 없다.
이 생각이 틀렸다는 것을 여러분에게 보여 주고 싶습니다. 이 책의 여러 장에서는 Lisp 개발 환경이 제공하는 유용한 도구들을 소개합니다.
Lisp는 메모리를 너무 많이 사용한다.
내 Mac과 PC에서 실행되는 Lisp 개발 환경은 RAM 4~8MB만으로도 충분히 원활하게 작동합니다. 메모리가 부족한 상황에서는 그보다 적은 용량으로도 실행할 수 있습니다.
반면 통합 C++ 개발 환경은 12~20MB의 메모리를 사용합니다. 두 환경 모두 비슷한 수준의 도구와 기능을 제공합니다.
Lisp는 디스크 공간을 너무 많이 차지한다.
내 Mac과 PC에서 사용하는 Lisp 개발 환경은 C++ 개발 환경보다 디스크 공간을 훨씬 적게 차지합니다.
내 하드 디스크에서 Lisp가 차지하는 공간은 적게는 약 5MB에서 많게는 약 30MB에 이릅니다. 30MB를 차지하는 시스템은 파일 관리자, WYSIWYG 방식의 워드 프로세서, 그래픽 프로그램, 일정 관리 프로그램은 물론이고(하마터면 잊을 뻔했지만) Lisp 개발 환경까지 내장한 완전한 프로그래밍 환경입니다.
반면 C++ 개발 환경은 적게는 약 20MB에서 많게는 약 150MB를 차지합니다.
쓸 만한 Lisp 컴파일러를 찾을 수 없다.
어떤 컴퓨터를 사용하느냐에 따라 다르지만, 불과 1~2년 전만 해도 실제로 문제가 되었습니다. 또한 Lisp 시장에서 경쟁이 활발하지 않다는 것도 사실입니다. Lisp 개발 업체는 한 손으로 꼽을 수 있을 정도밖에 되지 않습니다.
하지만 Lisp 시장을 지원하는 업체들은 대체로 오랫동안 사업을 이어 왔으며, 좋은 평판을 쌓아 왔습니다. 데스크톱 컴퓨터의 처리 속도와 저장 용량이 계속 향상됨에 따라, Lisp 개발 업체들도 이러한 플랫폼에 점점 더 많은 관심을 기울이고 있습니다.