LISP


(print "Hello, World!")

프로그래밍 언어의 한 종류

lisp_cycles.png

Lots of Irritating Superfluous Parentheses LISt Processsor의 준말. 대표적인 함수형 언어이다. 프로그래밍 언어의 역사를 말할 때, 현대의 컴퓨터를 위해 등장한 고급 언어 중 가장 오래된 것이 포트란이고, 두 번째로 오래된 것이 바로 이 리스프다. 역사가 긴 만큼, 소위 Lisp-family라 불리는 여러 가지 사투리가 존재하고, 이것들이 너무 다양하게 난립했기 때문에, 공통 표준 규격을 만들어 1984년에 커먼 리스프(Common Lisp)가 탄생했다. 보통 리스프라고 말하면 이러한 사투리까지 모두 포함하는 의미로 사용된다. 최초 표기는 LISP 이었지만 어느새 Lisp으로 표기되기 시작했으며, 지금은 Lisp이 널리 쓰인다.

1 짤막한 역사

1958년 미국 MIT의 존 매카시가 개발했는데, 당시 매카시는 인공지능을 연구하는 프로젝트에서 FORTRAN에서 목록 작업을 수행하는 서브루틴의 패키지로 Fortran List Processing Language(FLPL)을 구현한다. 이것이 리스프의 모태가 되어, 1958년에 리스프가 세상에 등장하게 된다. 이후 상당기간 리스프는 인공지능 관련 언어라는 인식이 강했는데, 이맥스(Emacs), AutoCAD 등 인공지능과 전혀 관련없는 분야에서도 리스프가 사용되면서 이러한 인식이 많이 옅어졌다.

2 다른 프로그래밍 언어에 미친 영향

언어 자체가 새로운 기능을 탑재하기 걸맞게 유연하기도 하고, 오랫동안 학계의 특정 분야에서 거의 표준적으로 사용된 언어였기 때문에, 리스프가 후대 언어에 끼친 영향은 매우 크다. if-then-else 형식, 재귀함수, 쓰레기수집, JIT, 동적 타이핑, 동적 메모리 할당, 일급 함수객체, 액터 모델 등 명령형이고 함수형이고를 불문하고 간접적인 영향까지 포함하면 프로그래밍 언어 중 리스프의 영향을 받지 않은것을 찾아보기 힘들 정도이다.

3 특징

3.1 Atom

초기의 리스프에 데이터형은 Atom과 리스트로 구분되었다. Atom은 상수나 심볼을 뜻하고, 리스트는 이러한 Atom 또는 다른 리스트를 원소로 포함하며 유한한 길이를 가진다. 여기서 두드러지는 특징은, Atom의 값이 불변(immutable)이라는 점이다.

이후 여러 리습 사투리가 개발되고 분화되는 과정에서 Atom의 Immutable 특성이 사라지는 경우도 많이 발생했으며, 이는 아마도 실용적인 목적에 의해 이렇게 된 것이 아닐까라고 추측한다.

3.2 리스트

리스프에는 기본적으로 갯수를 셀 수 있고 변경 가능한 리스트가 기본 자료형으로 주어진다. 리스트는 그 자체로 Atom과 함께 다른 리스트가 원소가 될 수 있다.

여담으로 자료구조를 리스프 방언으로 진행하게 되면 코딩 분량이 팍팍 줄어드는 신세계를 맛볼 수 있는데, 이는 리스프가 기본적으로 리스트 자료형을 제공하기 때문인 것도 한 몫 한다. (Python으로 해도 유사한 경험을 할 수 있다.)

3.3 문법

리스프는 표현식 지향 언어이다. Homoiconicity 특성에 힘입어 문장이나 식, 데이터의 구분 없이 모든 것은 리스트와 그 안에 내용을 채우는 표현식으로 간주된다. 표현식이 평가(evaluate)될 때 실제 코드가 실행되며, 평가 후 값이 나온다. 값 또한 코드와 데이터 구분 없이 아무것이나 올 수 있다. 즉 코드 실행의 결과가 값 뿐만이 아닌 함수도 될 수 있다.

또한 기본적으로 전위 표기법(또는 폴란드 표기법, Polish Notation)을 사용한다. 즉, 통상 프로그래밍 언어에서 1 + 2 를 수행하는 코드를 작성하면 다음과 같이 되는데

1 + 2

리스프에서는 다음과 같이 + 이 제일 앞에 나오고 숫자가 뒤에 표기되는 형식을 취한다. 이는 + 라는 함수에 숫자 1과 2인 인자를 주어 호출함을 뜻한다.

(+ 1 2)

이와 같이 괄호와 Atom을 사용하여 표기하는 방법을 리스프에서는 S-Expression(Symbolic Expression) 이라고 부른다. 이것 때문에 리스프의 소스 코드는 중첩괄호의 연속이 된다. ((()()(()))(()))

M-Expression(Meta Expression)이라는 것이 초기 리스프에 존재했는데, 이것은 일반적인 절차형 언어에서 함수를 호출하는 구문을 떠올리면 된다. 나중에 이것을 동등한 S-Expression으로 변환하고, 이를 평가(Evaulate)하면 결국 코드를 실행하는 것과 동일하다는 것이 밝혀져서 이후에 완전히 묻혔다. (...)

3.4 Homoiconicity, 또는 Code is data, data is code

리스프는 처음부터 후대에 등장하는 다른 프로그래밍 언어와 여러 가지 면에서 달랐다. 가장 큰 특징으로는 코드가 마치 데이터처럼 취급된다는 것이었는데, 이는 데이터와 코드가 모두 리스트 형태로 표기되었기 때문이다. 이 리스트는 리스프에서 가장 중요한 요소로 볼 수 있는데, 쉽게 말해 자료를 순서를 매겨 저장하는 것이라고 생각할 수 있다. 예를 들어, (+ 1 2) 라는 리스트를 입력하면 코드로 해석이 되어(평가, Evaluation) + 함수에 1 과 2 를 인자로 넘겨주고, 1 + 2 가 연산되어 3 을 출력하며, 평가하지 말고 그대로 리스트로 저장하는 ' 를 붙여서 '(+ 1 2) 를 입력하면 첫번째 원소가 + 이고 두번째 원소가 1, 세번째 원소가 2 인 리스트로 인식된다. 즉, 저 리스트를 코드로 보고 그대로 실행할수도 있고, 실행하지 않고 다른 함수에 리스트형 데이터로 넘겨주는 것도 가능하다는 소리.

코드를 데이터로 취급할 수 있다는 장점에 힘입어, 셸 언어(scsh 같은), 문서 편집기(대표적으로 Emacs), 설계 시스템(CAD) 같은 소프트웨어의 기능을 늘리는 언어로 사용되거나, 인공지능 분야에 많이 사용된다. 그 밖에 자연어 처리, 해석학, 게임 이론 같은 여러 분야에서 광범위하게 사용되고 있다.

리스프가 아닌 언어 중에도 물론 이런 특성을 가진 언어는 존재한다.io같은 언어가 이러한 특성을 가지고 있다. 하지만 아무도 안쓰고 있다. Julia 언어도 이 특성을 가지고 있다.

3.5 람다 함수

사실 리스프는 Lambda Calculus를 프로그래밍 언어 형태로 구현한 것에 여러 가지 다른 기능을 추가한 것에 지나지 않는다고 할 수 있다. 따라서 소위 익명 함수를 뜻하는 Lambda Function 도 정의하여 사용할 수 있다. 이 자체만으로는 "표현 가능"하다는 것 외에 의미가 없지만 리스프 계통 언어에서 주어지는 기본 함수나 API 들이 이를 적극 사용하여 코딩 노가다를 줄이면서 동시에 편리하게 코딩을 할 수 있도록 도와주는 강력한 요소이다.

예를 들어 자바 7 이하의 API에서 지원하는 컬렉션 정렬 기능을 사용하려면 Comparator 인터페이스를 구현하여 compare 메서드를 구현해야 한다. 즉, 고작 이거 하나 하려고 클래스를 하나 더 만들어야 한다. 하지만 리스프에서는 그냥 람다 함수로 대소비교를 할 수 있는 익명 함수를 정의해서 파라미터로 넘기면 끝이다. 자바도 8 부터 이러한 람다 표현식을 수용하여 보다 편리하게 코딩할 수 있게 되었다.

3.6 매크로

리스프의 돋보이는 특징 중 하나로 매크로가 있다. 이것을 사용하면 원하는 바에 따라 함수형으로도, 절차형으로도, OOP로도, 논리형으로도 사용이 가능하다. 사용 가능한 프로그래머들의 우월감이 쩔기로 유명하다.

이 기능은 매우 쉽게 말해서 C 언어의 #define과 유사하지만 훨씬 강력한 기능을 제공한다. C의 #define은 소스 코드의 전처리기에 의해 미리 정의된 내용을 소스 코드 수준에서 치환하는 데 그치지만, 리스프의 매크로는 모든 것을 리스트에 들어있는 내용으로 치환하는 덕에, 실행 도중에 미리 정의된 프로그램으로 직접 치환된다. 즉, 매크로 자체가 입력되는 리스트의 내용을 미리 정의된 리스트의 내용으로 대체하는 함수이다.

리스프의 특성과 맞물린 이 기능이 너무나 강력하기 때문에, 리스프는 (자기자신을) 프로그래밍 할 수 있는 프로그래밍 언어(Programmable programming language[1])라고도 불리며, 매크로를 사용하면 프로그래머가 리스프 내에서 새로운 문법을 정의하며 사용하는 것이 가능할 정도로 강력하다. 따라서 새로운 패러다임이 나타나도 리스프 내에서 그 기능을 매크로를 사용하여 구현 후, 동일한 리스프 언어로, 동일한 컴파일러를 사용하며 프로그램을 작성하는 것이 얼마든지 가능하다.[2] 리스프로 만들어진 프로그램들도 이런 특징을 이어받는 경우가 많은데, 리스프의 이런 특징을 한계까지 끌어낸 이맥스도 자체 기능을 거의 전부 커스터마이징이 가능하며 에디터가 아니라 그냥 거대한 리스프 인터프리터라는 평가를 받는다. (...)

하지만 기능이 강력한 만큼 양날의 검 같은 존재가 바로 이 매크로이다. 나 혼자서만 작업하는 경우면 상관없으나 여러 사람이 작성하는 프로그램에서 사전에 충분한 협의 없이 만들어 쓰는 매크로는 프로그램 전체에 독이 될 가능성이 높다. 먼저 다른 개발자가 설명을 듣거나 해당 매크로의 소스코드를 완전히 이해하기 전에는 코드를 해독하거나 사용하기 힘들고, 여러 명이 공동 작업으로 프로그램을 작성한다는 자체가 프로그램의 덩치가 꽤 크다는 것을 뜻하기 때문에 충분한 사전검토나 협의 없이 만들어진 매크로는 안만드니 못하는 경우도 충분히 있을 수 있기 때문이다.

그리고, 성능상의 문제나 기타 다른 이유 때문에 매크로가 없는 구현체도 꽤 많이 존재한다.

3.7 Lisp 1, Lisp 2

3.7.1 Lisp 1

초기의 리스프는 보통 말하는 변수와 함수의 이름이 겹칠 수 없었다. 즉, 함수에 사용되는 심볼과 값을 나타내기 위해 사용하는 심볼이 겹칠 수 없었다. 요즘 식으로 표현하면 둘이 같은 네임 스페이스를 사용하고 있기 때문에 이것이 안된다. 다음은 Lisp 1인 Clojure 예제 코드로, a에 값 4를 할당하고, 이후 2를 곱하는 함수를 정의한 후 a함수에 값 a의 심볼을 넘겨서 호출을 시도하는 코드이다. Clojure는 Lisp 1이기 때문에 당연히 오류가 발생한다.

user=> (def a 4)
#'user/a
;; 아랫줄에서 심볼 a 는 (fn [x] (* x 2)) 함수로 바뀐다.
user=> (defn a [x] (* x 2))
#'user/a
user=> (a a)

ClassCastException user$a cannot be cast to java.lang.Number  clojure.lang.Numbers.multiply (Numbers.java:146) 
;; 함수인 a를 받아서 *를 수행하는 clojure.lang.Number.multiply에 전달했으므로, 자동으로 형변환을 할 수 없다고 오류가 발생한다.

3.7.2 Lisp 2

1966년에 제안된 개념으로, Lisp 1과 달리 함수 심볼과 값 심볼의 네임스페이스를 분리시켰다. 이것이 Common Lisp 표준 사양에 포함되면서 널리 알려지게 되었다. 다음 예제 코드는 위의 Clojure 예제와 동일한 내용의 코드로, Common Lisp에서 아무런 문제 없이 실행된다.

[1]> (setq a 4)
4
[2]> (defun a (x) (* x 2))
A
[3]> (a a)
8

하지만 해당 심볼이 함수 심볼임을 나타내는 #'을 붙여서 a를 넘기면 Clojure와 유사한 오류가 발생하는 것을 볼 수 있다.

[4]> (a #'a)

*** - *: #<FUNCTION A (X) (DECLARE (SYSTEM::IN-DEFUN A)) (BLOCK A (* X 2))> is
not a number
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead.
ABORT          :R2      Abort main loop
Break 1 [5]> 

4 SICP와 리스프에 대한 오해

Structure and Interpretation of Computer Programs, 일명 SICP는 컴퓨터 과학 입문서이며, Stream, Continuation, Closure 등 매우 기초적이고 기본적인 프로그래밍 개념을 심도있게 공부할 수 있는 명저이다. 그런데, 책에서 사용하는 프로그래밍 언어가 리스프의 사투리중 하나인 Scheme이다. 따라서 리스프를 잘 하려면 이 책을 반드시 공부해야만 할 것 같은 느낌을 주고, 실제로 리스프를 공부하기 위해 이 책을 찾는 사람도 많다.

사실, 대부분의 컴퓨터 공학 입문이 프로그래밍 자체보다 특정 프로그래밍 언어를 습득하는데 집중하는 경향이 있는데, SICP는 원래 프로그래밍의 기초 개념을 공부하기 위한 책이다. 공부하는데 필요한 도구로 Scheme이 선택된 것일 뿐. 물론 저자인 Sussman이 Scheme을 만들기도 했다. (실제 트윅 없이 SICP에서 사용하는 Scheme 코드들을 전부 구동 가능한 Scheme 컴파일러는 찾아보기 힘들다.)

MIT 에서는 1980년에 시작된 6.001 SICP 강의가 2008년에 6.01 "Introduction to Electrical Engineering and Computer Science I" 강의로 변경되면서 Python을 사용하게 되었다. 강의 목적도 전산학의 기초 개념을 가르치는 것에서 Python 을 가르치는 것으로 바뀌었다. 이 시기에, Scheme을 사용하는 다른 여러 강의도 모두 Python을 쓰는 것으로 바뀌었다고 한다. 나중에, 이전의 6.001 과 같은 과목이면서 Scheme을 사용하는 6.037 이 2011년 시작되었다.

Gerald Sussman이 직접 밝힌 Scheme을 사용하는 MIT의 6.001 이 Python 을 사용하는 6.01 로 바뀐 이유는 다음과 같다. (1) Sussman과 Hal Abelson 은 1980년대부터 가르쳐 온 이 과목에 대해 피로감을 느꼈고, 1997년에 학과장 사무실로 찾아가서 "이 과목을 더 이상 가르치지 않겠다"는 의견을 전달했다고 한다. (2) 더욱이, SICP 커리큘럼이 더 이상 오늘날의 엔지니어링에 적합하지 않기 때문이라고 설명했다.

Sussman이 설명하는 오늘날의 엔지니어는, 완벽하게 이해하는 것이 불가능한 (때로는 상업적 보호정책에 의해 내부가 공개되지 않아서 완벽하게 이해하는 것이 불가능한) 매우 복잡한 하드웨어를 위한 코드를 반복적으로 작성한다. 소프트웨어 또한 방대한 라이브러리와 기능을 가지고 있기 때문에 소프트웨어쪽도 하드웨어와 동일한 환경에 처해있다. Sussman은 자신이 가르치던 학생들이 이러한 라이브러리들을 엮어서 프로그램을 작성하기 위해 라이브러리 매뉴얼을 읽는 데에 대부분의 시간을 사용하는 것을 보아왔다고 한다. 따라서 작고 단순한 부분들을 사용하여 큰 시스템을 만드는 SICP의 "합성을 통한 분석(analysis-by-synthesis)" 관점은 더 이상 유효하지 않게 되었다고 한다. 오늘날에는 일단 라이브러리의 기능을 실행하는 코드를 작성하여 이것이 어떻게 동작하는지를 관찰하고, 이를 엔지니어가 원하는 의도대로 조작할 수 있는지를 고민하게 되는 식(poking방식)으로 소프트웨어를 작성하게 된다고 한다. 참조


리스프 자체 또는 함수형 프로그래밍을 이해하는 데에는 SICP보다는 John Allen, "Anatomy of Lisp", McGraw-Hill, 1980 (ISBN: 978-0070011151)이 더 나은 선택일 수 있다.

5 이야기거리

리스프와 관련된 격언(?)으로 Greenspun's tenth rule이라는게 있다.

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.

설명하자면, 커먼 리스프의 여러가지 강력한 기능들은 프로그램이 어느정도 복잡해지면 어딘가에 반드시 필요하게 되고, 그런 기능을 갖고있지 않은 C와 같은 언어로 그런 복잡한 프로그램을 만들면, 결국 본인도 모르는 사이에 커먼 리스프의 그런 기능을 C 언어로 구현하고 있게 되며, 당연히 그런 즉흥적(?) 구현은 오랜 시간에 걸쳐 잘 디자인된 커먼 리스프에 비해 질적으로 떨어질수밖에 없다는 소리.뭔가 오만한것같은데, 리스프 책 몇 권 보면 정말 리스프 프로그래머들의 콧대는 하늘을 뚫는것을 쉽게 알 수 있다.

리스프의 특징과 초기의 설계목적(새로운 언어 기능의 실험) 때문에 리스프는 처음에는 산술 연산 같은 것을 빠르고 가볍게 처리하지 못했다. 먼저 태어난 포트란은 애초부터 이런 쪽으로 먼치킨 이었고, 지금도 그렇다. 그리고 이 때는 컴퓨터의 성능이 한참 구리기도 했다. 이는 동적 타이핑과 특정 위치의 데이터를 검색하는데에 효율적이지 못했던 리스트형 자료구조 위주로 사용하던 초기의 문제였다. 그러나 곧 많은 능력자들에 의해 리스프 컴파일러 및 인터프리터가 꾸준히 개량되고, 언어 자체도 리스트가 아닌 여러가지 자료구조를 탑재하고 타입 지정이 가능하도록 개량되면서, 절차형 언어에 비해 근본적인 한계가 있음에도 이런 문제가 어느 정도 해결되었다.

이런 측면 덕분에 리스프 프로그램 개발과정은 좀 독특한데, 먼저 스크립트 언어처럼 동적 타이핑과 사용하기 편리한 리스트형 자료구조 위주로 빠르게 프로토타입을 뽑아낸 뒤에 프로토타입이 다 완성되면 타입지정과 함께 사용된 자료구조들을 보다 효율적인것들로 바꿔주는식으로 최적화하여 완성하게 된다. 그래서 최종적으로 완성된 리스프 프로그램은 리스트형 자료구조가 전혀 사용되지 않은 경우도 있다. 프로토 타입의 성능은 보통의 스크립팅 언어에 가깝고, 최적화가 완료된 프로그램의 성능은 컴파일 언어에 가까워진다고 보면 된다. 인터프리터에서 파트별로 쉽게 테스트 가능한 함수형 패러다임을 완벽에 가깝게 지원하며, 대부분의 리스프 인터프리터는 프로그램 실행도중에 코드를 변경하고 즉석에서 적용하는게 가능하기때문에 개발 자체는 생각보다 많이 편리한편.

대부분의 오래된 언어들이 그렇듯, 점점 인기가 사그라드는 것이 일반적인데, 리스프는 오히려 2000년대 들어 고전의 향수를 느끼려는(?) 젊은 프로그래머들과, 기존 OOP와 절차형 프로그래밍 언어의 한계를 느낀 개발자들에 의해 인기를 끌고 부활하였다. 그 인기의 중심에는 실용성을 중심으로 C++처럼 언어를 엄청나게 거대하게 키운 커먼 리스프와 함수형 프로그래밍에 중점을 둔 작은 언어인 스킴(Scheme)이 있다. 보통 실제 프로젝트에서는 커먼 리스프가 주로 사용되었고, 대학이나 기타 교육 목적으로는 스킴이 많이 사용되었다.

하지만, 근래들어 커먼 리스프는 여전히 강력하긴 하지만 좀 낡았다는 평가를 받고있고, 최근의 함수형 언어 붐이라기는 좀 힘들지만에 힘입어 스킴 쪽에 관심이 상당히 쏠린 편인데, 스킴은 실용적인 목적으로 사용하기엔 작아도 너무 작기 때문에[3] 요즘엔 스킴을 이리저리 확장시키고 모던한 기능들을 추가한 스킴 방언이 많이 나오고 있는 편이다. 이 새로운 스킴 방언의 대표적인 예로 저수준 매크로로 언어의 확장성을 크게 높여 여러가지 다른 언어로 쉽게 변신이 가능한 라켓(Racket)이 있다.

이와 별도로 2007년에 갑툭튀하여 자바 가상 머신 위에서 돌아가도록 만들어져 자바의 라이브러리를 그대로 끌어다 쓰면서 자바보다 높은 생산성을 자랑하는 클로저(Clojure)가 있다.

리스프는 너무나 많은 사투리를 가지고 있기 때문에, 보통 "리스프 커뮤니티" 라고 부를 수 있는 것이 존재하지 않으며, 대신 "커먼 리스프 커뮤니티", "스킴 커뮤니티", "래킷 커뮤니티" 등 많이 쓰이는 사투리의 커뮤니티가 따로 존재한다. 당연하게도, 인터넷에서 검색해서 찾을 수 있는 자료 또한 이 항목과 같이 리스프 전반에 대해 설명하는 것이 아니라면, 이런 식으로 특정 방언에 속하는 리스프에 대한 것이 대부분이다. 하지만 한국에서 리스프 계열 언어는 매우 마이너한 언어에 속하며, 따라서 "리스프 커뮤니티" 라는 제목 아래에 리스프의 여러 사투리에 대해 논의되는 커뮤니티가 극소수 존재한다.

영문권 리스프 프로그래머들은 예/아니오 구문인 P를 일상생활에 이용하는 경우가 많았다고 한다. 예를 들어:

Q: "Foodp" (밥먹을래?)

A: "T" (True = 응.)
A: "Null" (아니.)

6 리스프 사투리

6.1 2대 리스프 사투리

현재 가장 유명하면서 많이 쓰이는 리스프 사투리는 다음 2가지가 있다. 그런데 최근 사용량과 인기도는 갑툭튀한 Clojure 한테 밀리는 것 같기도 하다.

6.1.1 Common Lisp

대표적인 Lisp-2. 기존에 난립하던 여러 리스프 구현체의 스펙을 표준화하면서 동시에 다양한 개념을 소화할 수 있도록 멀티 패러다임 언어로 설계되어 1984년에 표준안이 발표되었다. 그리고 1994년에 개정된 것이 ANSI 표준으로 채택된다.

Common Lisp는 다목적, 멀티 패러다임 언어로 절차형, 함수형, OOP를 모두 지원하는 다이내믹 프로그래밍 언어의 표준안이다. 즉 이 자체로는 그냥 언어 사양일 뿐이다.

따라서 이 표준 사양에 근거하여 다양한 구현이 등장했는데, 당연히 구현체별로 기능을 조금씩 확장하여 다른 부분이 존재하며, 이러한 특성을 고려하여 어느 구현체를 사용할 지 결정해야 한다.

6.1.1.1 주요 구현체

괄호 안의 소문자 약칭은 Common Lisp 커뮤니티에서 해당 구현체를 언급할 때 사용하는 약칭이다.

기계어 바이너리 구현체는 대부분 Foreign Function Interface(FFI)를 지원하여 이미 존재하는 *.so, *.dll, *.dylib 을 사용할 수 있다. 자바 가상 머신에서 동작하는 구현체는 JNI와 더불어 Java Native Acces가 등장하여 이전보다 편리하게 플랫폼에서 제공하는 기계어 자원을 사용할 수 있다.

물론 여기에 소개되지 않은 구현체도 얼마든지 존재한다.

6.1.1.1.1 상업용 라이선스
6.1.1.1.2 무료 라이선스
  • Armed Bear Common Lisp(abcl): JVM 위에서 동작하는 Common Lisp.
  • Clozure CL(ccl) : Macintosh Common Lisp의 Fork에서 시작한 구현체. 현재는 다른 Unix 계열 운영체제나 Windows 도 지원한다. Clojure와는 다르다! Clojure와는!!
  • GNU Common Lisp(clisp): 바이트 코드(자바의 그것과는 다르다. CPython도 일단 컴파일 자체는 바이트 코드로 된다.) 형태로 컴파일하여 실행되는 구현체. 아직까지 Thread 지원이 제한적이다. 다르게 시작된 GNU CL(gcl)이라는 구현체가 있으며, 이것과 헷갈리면 안된다.
  • CMUCL(cmucl): 카네기 멜론 대학에서 시작된 구현체. 윈도우는 지원하지 않는다. Thread도 지원되지 않는다.
  • Embeddable Common Lisp(ecl): C언어를 기반으로 제작된 프로그램에 임베딩하여 사용될 목적으로 탄생했다. 즉 이것을 C 프로그램 내부에서 불러와 사용하면 리스프 프로그램을 실행시킬 수도, 조금 다르게 사용하면 해당 C 프로그램이 스크립트로 리스프를 사용하게 하는 것이 가능하다.
  • ManKai Common Lisp(mkcl): ecl의 브랜치로, ecl의 여러 부분을 재작성하여 ecl보다 안정성을 높인 구현체.
  • Steel Bank Common Lisp(sbcl): cmucl의 브랜치로 관리, 운영 특성을 향상시킨 구현체. 당연히 cmucl이 동작하는 모든 환경에서 동작하며, 실험적으로 윈도우 환경도 지원하며 빠른 실행 속도로 유명하다. 리눅스에서만 Thread를 완전하게 지원하며 다른 플랫폼에서는 아직 실험단계이다.
  • XCL(xcl): abcl의 제작자가 만드는 또 다른 구현체. abcl과는 달리 타겟 플랫폼의 기계어로 동작한다.
6.1.1.2 Quicklisp와 asdf

Common Lisp를 위한 라이브러리 패키지 시스템과 빌드 시스템이 존재한다. Quicklisp은 인터넷을 통한 패키지를 배포하여 편리하게 받을 수 있도록 만든 시스템이며, asdf는 일종의 빌드 툴이다.

6.1.2 Scheme

대표적인 Lisp-1 이다.

6.2 유명한 리스프 사투리

  • AutoLISP, VisualLisp - AutoCAD에 내장되어 일부 변형된 리스프. 실용적으로 리스프가 사용되는 대표적인 예.
  • Clojure - JVM 위에서 운용되며, Lisp 계열 언어의 간결함과 강력함, JVM의 이식성을 가지며 언어에서 지원하는 Software Transactional Memory(STM)과 잘 추상화된 관련 함수를 통한 비교적 쉬운 병행성 구현[4], 전용 Build Tool인 Leiningen[5]과 Maven Repository가 결합하여 편리한 Build, 라이브러리 사용과 관리, Jenkins, Travis 등의 CI와 연동이 쉽고, Java를 지원하는 클라우드 서비스에 쉽게 올릴 수 있는 등의 장점으로 최근 주목받고 있다. 20세기 스타일을 유지하면서 최첨단 보조도구를 달고 나온 21세기형 Lisp 방언일지도. 매우 보수적인 금융계에서 유별나게 신기술 도입에 적극적인 씨티은행 본사에서 Clojure를 도입해 사용하고 있다고 밝혔다.
  • Emacs Lisp (elisp) - 이맥스의 기반. Lisp-2이며, Lisp 계열 언어를 코딩하는데 Emacs 보다 좋은 툴은 21세기에도 그다지 많지 않다. 아이러니하지만, Lisp 방언이 아무리 많더라도 결국 그들중 대다수가 공통적으로 사용하는 에디터는 결국 이맥스이고, 이맥스 특징상 elisp을 모르고 사용하기는 좀 거시기하다보니 Lisp 방언중 가장 많은 사용자를 확보하고 있는 언어이기도 하다.
  • EuLisp - 유럽 버전의 Lisp. Lisp 표준의 권위자중 한명인 Kent Pitman 의 회고에 의하면, Common Lisp이 만들어질 당시, uucp(unix-2-unix copy) 로 서로 소통을 했었는데, 아무래도 국제 네트워크가 활성화되지 않아서 대륙간 메시지가 오가는데 며칠 이상씩 소요되었고, 덕분에 아시아(특히 일본)와 유럽은 논의에서 거의 배제되었다고 한다. 이때문에 기분나뻐서 유럽 자체적으로 만들어 등장한 Lisp 이라고도 하는데, Lisp-1이며 Common Lisp은 과거(하위호환)에 발목을 잡혔고, Scheme은 너무 작으니 그 중간 정도를 노리고 만들어졌다고 한다.
  • ISLISP - ISO 표준의 LISP 이다. 다만, ISO 에서 LISP 표준을 정의하려 한것은 아니고 그냥 ISLISP 의 표준이 ISO 에서 만들어졌다고 봐야 한다. LISP 의 창시자인 존 매카시 교수가 LISP 을 하나의 표준으로 가두는것을 좋아하지 않았기때문에 LISP 의 ISO 표준이 아닌, ISLISP 방언의 ISO 표준으로 해달라고 직접적으로 요청했다고 한다.[6] Lisp-2이며, 여러 방언인 Common Lisp, Scheme, EuLisp, Le Lisp 에 공통적으로 들어간 교집합 위주로 묶어서 언어를 만들고 표준화를 시킨다고 한다.
  • Javelin - 스크립팅에 적합한 JVM 기반 Lisp. 공식 사이트
  • Le Lisp - 프랑스의 유명한 전산학 연구소인 INRIA에서 만들어진 Lisp 방언
  • newLISP - Perl이나 Python처럼 편리하게 쓸 수 있는 스크립트 언어. 공식 사이트
  • Racket - 교육용으로 많이 쓰이는 스킴 방언으로, 리스프를 배우기 가장 편한 환경을 제공한다. 실제로 윈도우, 맥, 유닉스 계열에 다 호환되는 IDE를 제공하며, 거기에 GUI, 네트워크 등 웬만한 라이브러리도 다 포함되어있고Batteries Included!, 실제 프로그래밍과 기본적인 개념은 비슷하면서, 노가다가 될 수 있는 부차적인 기능들을 단순화시킨 프로그래밍 교육용 모듈까지 따로 포함되어있다. Realm of Racket이라는 교재를 보면, 중고교생 대상의 기초적인 수준이긴 하지만 스킴 문법과 GUI, 네트워크 프로그래밍, 인공지능 등을 게임 프로그래밍 실습을 통해 겨우 300페이지 이하의 분량으로 설명하고 있다! 사실, 리스프라는 언어 자체가 프로그래밍 언어를 만들기 편리한 언어이기도 하고, Racket은 스킴의 저런 언어 자체의 확장성을 다소 극단적인 레벨로 끌어올린 언어이기에 가능한것.

7 Lisp 사투리 비교

  • Common Lisp, Racket, Clojure, elisp 비교: [1]
  1. C 언어가 유닉스를 만들기 위해 탄생한 언어라면, LISP 은 LISP을 만들기 위해 탄생한 언어라는 농담도 있다.
  2. 어차피 모든 범용 프로그래밍 언어는 튜링 완전하기 떄문에 기능면에서는 동일하다. 새로운 패러다임의 언어가 등장한다고 해서 전에 없던 완전히 새로운 것이 가능하게 되는 것이 아니고, 그저 표현하는 방식이 바뀌는 것 뿐이며 리스프는 언어 자체가 가지고 있는 이러한 특징을 이용하여 새로운 표현 방식, 문법 등을 embedding 하는 것이 가능하다. 물론 이럴 경우, 항상 실행 효율과 성능이 좋다고 보장할 수 없다. 구현 가능하다는데 의의가 있다. 즉 매크로를 쓰려면 잘 만들어야 한다.
  3. 그래서, 기존에도 스킴 컴파일러들은 표준적인 부분 이외에 다들 나름의 꽤 커다란 확장을 같이 탑재해서 배포했었다. 이런 경향이 반영된 탓인지, 최근의 표준인 R6RS부터는 더이상 작다고 보긴 힘들어졌는데, 스킴 위원회쪽에서 이런 경향에 반발세력도 꽤 있던탓인지, 이제는 아예 small scheme, large scheme 두가지 표준을 운영하는식으로 바뀌어 버렸다.
  4. 프로그래밍에서 기술적인 난이도로 가장 어려운 것으로 꼽는 것 중 하나가 바로 이것이다. 여러 프로세스나 쓰레드가 동시에 한 곳에 데이터를 읽고 쓸 때, 통제를 안하면 당연히 값이 엉망이 되는데, 통제하는 게 어려울 뿐더러 성공해도 제대로 못 하면 성능이 극악으로 떨어진다. 이를 효율적으로 잘 하는 것이 관건이다.
  5. 이름은 'Leiningen Versus the Ants'라는 단편소설에서 따온 듯 하다. 참고로 자바의 대표적인 빌드 툴이 Ant이다.
  6. 참고로, 매카시 교수는 ANSI Common Lisp 의 스펙이 만들어질때도 비슷한 요청을 했다고 한다.