다른 뜻에 대한 내용은 LISP(동음이의어) 문서 참고하십시오.
프로그래밍 사이트 선정 프로그래밍 언어 순위 목록 | ||||
{{{#!wiki style="margin: 0 -10px -5px; word-break: keep-all" {{{#!wiki style="display: inline-table; min-width: 25%; min-height: 2em;" {{{#!folding [ IEEE Spectrum 2024 ] {{{#!wiki style="margin: -5px 0" | <rowcolor=#fff> 스펙트럼 부문 상위 10개 프로그래밍 언어 | 직업 부문 상위 10개 프로그래밍 언어 | ||
1 | Python | 1 | SQL | |
2 | Java | 2 | Python | |
3 | JavaScript | 3 | Java | |
4 | C++ | 4 | TypeScript | |
5 | TypeScript | 5 | SAS | |
6 | SQL | 6 | JavaScript | |
7 | C# | 7 | C# | |
8 | Go | 8 | HTML | |
9 | C | 9 | Shell | |
10 | HTML | 10 | C++ |
}}}
}}}
- [ Stack Overflow 2024 ]
- ||<tablewidth=100%><width=9999><-4><bgcolor=#FFA500><tablebgcolor=#fff,#222> 2024년 Stackoverflow 설문조사 기준 인기 상위 25개 프로그래밍 언어 ||
1 JavaScript 14 Rust 2 HTML, CSS 15 Kotlin 3 Python 16 Lua 4 SQL 17 Dart 5 TypeScript 18 어셈블리어 6 Bash 19 Ruby 7 Java 20 Swift 8 C# 21 R 9 C++ 22 Visual Basic 10 C 23 MATLAB 11 PHP 24 VBA 12 PowerShell 25 Groovy 13 Go
- [ TIOBE 2024 ]
- ||<tablewidth=100%><width=9999><-4><bgcolor=deepskyblue><tablebgcolor=#fff,#222> 2024년 8월 기준 검색어 점유율 상위 20개 프로그래밍 언어 ||
1 Python 11 MATLAB 2 C++ 12 Delphi / Object Pascal 3 C 13 PHP 4 Java 14 Rust 5 C# 15 Ruby 6 JavaScript 16 Swift 7 SQL 17 Assembly language 8 Visual Basic 18 Kotlin 9 Go 19 R 10 Fortran 20 Scratch {{{#!wiki style="margin: 0 -10px -5px; min-height: calc(1.5em + 5px);"
{{{#!folding [ 21위 ~ 50위 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px"21 COBOL 36 Scala 22 Classic Visual Basic 37 Transact-SQL 23 LISP 38 PL/SQL 24 Prolog 39 ABAP 25 Perl 40 Solidity 26 (Visual) FoxPro 41 GAMS 27 SAS 42 PowerShell 28 Haskell 43 TypeScript 29 Dart 44 Logo 30 Ada 45 Wolfram 31 D 46 Awk 32 Julia 47 RPG 33 Objective-C 48 ML 34 VBScript 49 Bash 35 Lua 50 Elixir
- [ PYPL 2024 ]
- ||<tablewidth=100%><width=9999><-4><bgcolor=green><tablebgcolor=#fff,#222> 2024년 8월 기준 검색어 점유율 상위 20개 프로그래밍 언어 ||
1 Python 11 Objective-C 2 Java 12 Go 3 JavaScript 13 Kotlin 4 C# 14 MATLAB 5 C/C++ 15 PowerShell 6 R 16 VBA 7 PHP 17 Dart 8 TypeScript 18 Ruby 9 Swift 19 Ada 10 Rust 20 Lua
}}} ||
프로그래밍 언어 목록 · 분류 · 문법 |
(format t "Hello, World!")
리스프 LISP | |
개발자 | 존 매카시 |
최초 공개 | 1958년 ([age(1958-01-01)]년 전) |
[clearfix]
1. 개요
"LISP은 반 세기가 넘었는데도 여전히 변치 않는 완벽함이 느껴져."
"이 싸이클이 영원히 계속될지 궁금하네. 새로운 세대의 코더들 중 몇 명이 매번 LISP을 재발견하는거지."
"네 아버지의 괄호들이란다. 더 고상한 시대에 걸맞은 우아한 무기들이지."
xkcd 297화
"이 싸이클이 영원히 계속될지 궁금하네. 새로운 세대의 코더들 중 몇 명이 매번 LISP을 재발견하는거지."
"네 아버지의 괄호들이란다. 더 고상한 시대에 걸맞은 우아한 무기들이지."
xkcd 297화
리스프는 언어가 아니라 건축 자재이다.
Lisp isn’t a language, it’s a building material.
앨런 케이
Lisp isn’t a language, it’s a building material.
앨런 케이
LISt Processor
리스프(LISP)는 프로그래밍 언어의 한 종류로 대표적인 함수형 언어이다.
프로그래밍 언어의 역사를 말할 때, 현대의 컴퓨터를 위해 등장한 고급 언어 중 가장 오래된 것이 포트란이고, 두 번째로 오래된 것이 바로 이 리스프다. 역사가 긴 만큼 소위 Lisp-family라 불리는 여러 가지 사투리가 존재하고 이것들이 너무 다양하게 난립했기 때문에 공통 표준 규격을 만들어 1984년에 커먼 리스프(Common Lisp)가 탄생했다. 보통 리스프라고 말하면 이러한 사투리까지 모두 포함하는 의미로 사용된다. 최초 표기는 LISP이었지만 어느새 Lisp으로 표기되기 시작했으며, 지금은 Lisp이 널리 쓰인다.
2. 역사
1958년 미국 MIT의 존 매카시가 개발했는데, 당시 매카시는 인공지능을 연구하는 프로젝트에서 FORTRAN에서 목록 작업을 수행하는 서브루틴의 패키지로 Fortran List Processing Language(FLPL)을 구현한다. 이것이 리스프의 모태가 되어 1958년에 리스프가 세상에 등장하게 된다. 이후 상당기간 리스프는 인공지능 관련 언어라는 인식이 강했는데 이맥스(Emacs), AutoCAD 등 인공지능과 전혀 관련없는 분야에서도 리스프가 사용되면서 이러한 인식이 많이 옅어졌다.3. 다른 프로그래밍 언어에 미친 영향
언어 자체가 새로운 기능을 탑재하기 걸맞게 유연하기도 하고, 오랫동안 학계의 특정 분야에서 거의 표준적으로 사용된 언어였기 때문에 리스프가 후대 언어에 끼친 영향은 매우 크다. if-then-else 형식, 재귀함수, 쓰레기 수집, JIT, 동적 타이핑, 동적 메모리 할당, 일급 함수 객체, 액터 모델 등 명령형이고 함수형이고를 불문하고 간접적인 영향까지 포함하면 프로그래밍 언어 중 리스프의 영향을 받지 않은 것을 찾아보기 힘들 정도이다.4. 특징
4.1. Atom
초기 리스프의 데이터형은 Atom과 리스트로 구분되었다. Atom은 상수나 심볼을 뜻하고 리스트는 이러한 Atom 또는 다른 리스트를 원소로 포함하며 유한한 길이를 가진다. 여기서 두드러지는 특징은, Atom의 값이 불변(immutable)이라는 점이다.이후 여러 리습 사투리가 개발되고 분화되는 과정에서 Atom의 불변 특성이 사라지는 경우도 많이 발생했으며 이는 아마도 실용적인 목적에 의해 이렇게 된 것이 아닐까라고 추측한다.
4.2. 리스트
리스프에는 기본적으로 개수를 셀 수 있고 변경 가능한 리스트가 기본 자료형으로 주어진다. 리스트는 그 자체로 Atom과 함께 다른 리스트의 원소가 될 수 있다.여담으로 자료구조를 리스프 방언으로 진행하게 되면 코딩 분량이 팍팍 줄어드는 신세계를 맛볼 수 있는데 이는 리스프가 기본적으로 리스트 자료형을 제공하기 때문인 것도 한몫 한다.(Python으로 해도 유사한 경험을 할 수 있다.)
4.3. 문법
- 표현식 지향 언어
- 리스프는 표현식 지향 언어이다. 즉, 다음과 같은 코드가 실행 가능하다.
{{{#!syntax lisp
}}}
t
는 리스프에서 참값을 나타내는 값이다.(타언어에서 True 또는 true와 같다.)- 표현식(expression) 지향 언어이기 때문에 모든 식은 값을 가진다.
- 일급 객체 함수
- 또 다른 특징으로 리스프는 함수를 일급 객체(first-class object)로 다룬다. 함수를 함수의 인자로 받을 수 있으며 리턴값으로 지정할 수도 있다. 또한 런타임시에 생성할 수도 있다.
{{{#!syntax lisp
(mapcar #'(lambda (x) (1+ x)) '(1 2 3)) ;=> (2 3 4)
}}}
- 전위 표기법
- 또한 기본적으로 전위 표기법(Prefix Notation)을 사용한다. 통상 프로그래밍 언어에서
1+2
를 수행하는 코드를 작성하면 다음과 같이 되는데 된다.
{{{#!syntax cpp
}}}
- 그런데 리스프에서는 다음과 같이
+
이 제일 앞에 나오고 숫자가 뒤에 표기되는 형식을 취한다. 이는+
라는 함수에 숫자 1과 2인 인자를 주어 호출함을 뜻한다.
{{{#!syntax lisp
}}}
- S-Expression
- 이와 같이 괄호와 Atom을 사용하여 표기하는 방법을 리스프에서는 S-Expression(Symbolic Expression)이라고 부른다. 이것 때문에 리스프의 소스 코드는 중첩 괄호의 연속이 된다. 하지만 편집기(Emacs + SLIME)에서 자동으로 짝이 맞는 괄호를 표시해주고 들여쓰기도 해주기 때문에 실제 코딩에 있어선 별다른 문제가 되지 않는다.
- M-Expression(Meta Expression)이라는 것이 초기 리스프에 존재했는데 이것은 일반적인 절차형 언어에서 함수를 호출하는 구문을 떠올리면 된다. 리스프를 최초로 구상한 MIT 인공지능 랩의 매카시는 포트란 등 주변의 여러 언어와 마찬가지로 리스프를 M-Expression으로 표현하기 위해 연구에 몰두했지만 별다른 진전이 없었고 그 와중에 M-exp보다 S-exp를 더 선호하는 신세대 프로그래머들이 나타나서 그냥 M-S 치환 연구를 포기하였다. 보다 자세한 정보는 History of Lisp에서 마지막 문단에 있다.
4.4. Code is data, data is code
데이터와 코드를 저장할 때 같은 자료 구조를 사용하는 프로그래밍 언어를 homoiconic하다고 한다.리스프는 처음부터 후대에 등장하는 다른 프로그래밍 언어와 여러 가지 면에서 달랐다. 가장 큰 특징으로는 코드가 마치 데이터처럼 취급된다는 것이었는데 이는 데이터와 코드가 모두 리스트 형태로 표기되었기 때문이다. 이 리스트는 리스프에서 가장 중요한 요소로 볼 수 있는데 쉽게 말해 자료를 순서를 매겨 저장하는 것이라고 생각할 수 있다. 예를 들어
(+ 1 2)
라는 리스트를 입력하면 코드로 해석이 되어(평가, Evaluation) +
함수에 1과 2를 인자로 넘겨주고, 1 + 2
가 연산되어 3을 출력하며, 평가하지 말고 그대로 리스트로 저장하는 '
를 붙여서 '(+ 1 2)
를 입력하면 첫 번째 원소가 +
이고 두 번째 원소가 1
, 세 번째 원소가 2
인 리스트로 인식된다. 저 리스트를 코드로 보고 그대로 실행할 수도 있고, 실행하지 않고 다른 함수에 리스트형 데이터로 넘겨주는 것도 가능하다는 소리이다.#!syntax lisp
(f) ; f라는 함수를 실행한다.
'(f) ; f라는 값이 담긴 리스트를 만든다.
코드를 데이터로 취급할 수 있다는 장점에 힘입어 셸 언어(scsh 같은), 문서 편집기(대표적으로 Emacs), 설계 시스템(CAD) 같은 소프트웨어의 기능을 늘리는 언어로 사용되거나 인공지능 분야에 많이 사용된다. 그 밖에 자연어 처리, 해석학, 게임 이론 같은 여러 분야에서 광범위하게 사용되고 있다.
리스프가 아닌 언어 중에도 물론 이런 특성을 가진 언어는 존재한다. io 같은 언어가 이러한 특성을 가지고 있다.[1]
4.5. 람다 함수
사실 리스프는 람다 계산법 프로그래밍 언어 형태로 구현한 것에 여러 가지 다른 기능을 추가한 것에 지나지 않는다고 할 수 있다. 따라서 소위 익명 함수를 뜻하는 Lambda Function도 정의하여 사용할 수 있다. 이 자체만으로는 "표현 가능"하다는 것 외에 의미가 없지만 리스프 계통 언어에서 주어지는 기본 함수나 API가 이를 적극 사용하여 코딩 노가다를 줄이면서 동시에 편리하게 코딩을 할 수 있도록 도와주는 강력한 요소이다.예를 들어 Java 7 이하의 API에서 지원하는 컬렉션 정렬 기능을 사용하려면
Comparator
인터페이스를 구현하여 compare
메서드를 구현해야 한다. 고작 이거 하나 하려고 클래스를 하나 더 만들어야 한다. 하지만 리스프에서는 그냥 람다 함수로 대소 비교를 할 수 있는 익명 함수를 정의해서 파라미터로 넘기면 끝이다. 이러한 편의성 덕분에 .NET에서는 진작에 Framework 3.5 버전부터 람다식을 지원하고 있고, Java 역시 8부터 람다식을 수용하여 보다 편리하게 코딩할 수 있게 되었다.4.6. 매크로
리스프의 돋보이는 특징 중 하나로 매크로가 있다. 이것을 사용하면 원하는 바에 따라 함수형으로도, 절차형으로도, 객체 지향형으로도, 논리형으로도 사용이 가능하다.이 기능은 매우 쉽게 말해서 C 언어의
#define
과 유사하지만 훨씬 강력한 기능을 제공한다. C의 #define
은 소스 코드의 전처리기에 의해 미리 정의된 내용을 소스 코드 수준에서 치환하는 데 그치지만, 리스프의 매크로는 모든 것을 리스트에 들어있는 내용으로 치환하는 덕에, 실행 도중에 미리 정의된 프로그램으로 직접 치환된다. 즉, 매크로 자체가 입력되는 리스트의 내용을 미리 정의된 리스트의 내용으로 대체하는 함수이다.리스프의 특성과 맞물린 이 기능이 너무나 강력하기 때문에, 리스프는 (자기 자신을) 프로그래밍 할 수 있는 프로그래밍 언어(Programmable programming language[2])라고도 불리며, 매크로를 사용하면 프로그래머가 리스프 내에서 새로운 문법을 정의하며 사용하는 것이 가능할 정도로 강력하다. 따라서 새로운 패러다임이 나타나도 리스프 내에서 그 기능을 매크로를 사용하여 구현 후, 동일한 리스프 언어로, 동일한 컴파일러를 사용하며 프로그램을 작성하는 것이 얼마든지 가능하다.[3] 리스프로 만들어진 프로그램들도 이런 특징을 이어받는 경우가 많은데, 리스프의 이런 특징을 한계까지 끌어낸 이맥스도 자체 기능을 거의 전부 커스터마이징이 가능하며 에디터가 아니라 그냥 거대한 리스프 인터프리터라는 평가를 받는다.(...)
하지만 기능이 강력한 만큼 양날의 검 같은 존재가 바로 이 매크로이다. 나 혼자서만 작업하는 경우면 상관없으나 여러 사람이 작성하는 프로그램에서 사전에 충분한 협의 없이 만들어 쓰는 매크로는 프로그램 전체에 독이 될 가능성이 높다. 먼저 다른 개발자가 설명을 듣거나 해당 매크로의 소스코드를 완전히 이해하기 전에는 코드를 해독하거나 사용하기 힘들고, 여러 명이 공동 작업으로 프로그램을 작성한다는 자체가 프로그램의 덩치가 꽤 크다는 것을 뜻하기 때문에 충분한 사전검토나 협의 없이 만들어진 매크로는 안 만드니 못하는 경우도 충분히 있을 수 있기 때문이다.
그리고 성능상의 문제나 기타 다른 이유 때문에 매크로가 없는 구현체도 꽤 많이 존재한다.
4.7. Lisp-1, Lisp-2
- 함수와 변수에 같은 이름을 쓸 수 있는 리스프 언어를 lisp-2라고 한다.[4]
- 커먼 리스프는 lisp-2이다.
- 예를 들어 아래와 같이 커먼 리스프에서는
car
라는 이름을 함수에도, 변수에도 쓸 수 있다.
{{{(let ((car "Honda Civic"))
(mapcar (function car) '((foo bar) (baz qux))))
- 이때 함수와 변수 이름을 구분하기 위해 함수를 인자로 넣을 때는 위와 같이 연산자
function
을 적어야 한다. - 연산자
function
을 적는 대신#'
를 적어도 된다.
{{{(mapcar #'car '((foo bar) (baz qux))
- 함수와 변수에 같은 이름을 쓸 수 없는 리스프 언어를 lisp-1이라고 한다.
- 스킴은 lisp-1이다.
- 함수와 변수 이름을 구분할 필요가 없기 때문에 함수를 인자로 넣을 때 별도 표기가 필요 없다.
{{{(map sqrt '(1 2 3 4 5))
- Lisp-1
- 초기의 리스프는 보통 말하는 변수와 함수의 이름이 겹칠 수 없었다. 즉, 함수에 사용되는 심볼과 값을 나타내기 위해 사용하는 심볼이 겹칠 수 없었다. 요즘 식으로 표현하면 둘이 같은 네임 스페이스를 사용하고 있기 때문에 이것이 안 된다. 다음은 Lisp-1인 Clojure 예제 코드로, a에 값 4를 할당하고, 이후 2를 곱하는 함수를 정의한 후 a함수에 값 a의 심볼을 넘겨서 호출을 시도하는 코드이다. Clojure는 Lisp-1이기 때문에 당연히 오류가 발생한다.
{{{#!syntax lisp
#'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에 전달했으므로, 자동으로 형변환을 할 수 없다고 오류가 발생한다.
}}}
- Lisp-2
- 1966년에 제안된 개념으로 Lisp-1과 달리 함수 심볼과 값 심볼의 네임스페이스를 분리시켰다. 이것이 Common Lisp 표준 사양에 포함되면서 널리 알려지게 되었다. 다음 예제 코드는 위의 Clojure 예제와 동일한 내용의 코드로 Common Lisp에서 아무런 문제 없이 실행된다.
{{{#!syntax lisp
4
[2]> (defun a (x) (* x 2))
A
[3]> (a a)
8
}}}
- 하지만 해당 심볼이 함수 심볼임을 나타내는 #'을 붙여서 a를 넘기면 Clojure와 유사한 오류가 발생하는 것을 볼 수 있다.
{{{#!syntax lisp
*** - *: #<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]>
}}}
- Lisp-2는 컴파일 최적화에 이점이 있다고 한다.
5. SICP와 리스프에 대한 오해
자세한 내용은 Structure and Interpretation of Computer Programs 문서 참고하십시오.6. 리스프 사투리
- 현재 가장 유명하면서 많이 쓰이는 리스프 사투리는 다음 2가지가 있다.
그런데 최근 사용량과 인기도는 갑툭튀한 Clojure한테 밀리는 것 같기도 하다. - Common Lisp
- 대표적인 Lisp-2. 기존에 난립하던 여러 리스프 구현체의 스펙을 표준화하면서 동시에 다양한 개념을 소화할 수 있도록 멀티 패러다임 언어로 설계되어 1984년에 표준안이 발표되었다. 그리고 1994년에 개정된 것이 ANSI 표준으로 채택된다.
- Common Lisp는 다목적, 멀티 패러다임 언어로 절차형, 함수형, OOP를 모두 지원하는 다이내믹 프로그래밍 언어의 표준안이다. 즉 이 자체로는 그냥 언어 사양일 뿐이다.
- 따라서 이 표준 사양에 근거하여 다양한 구현이 등장했는데, 당연히 구현체별로 기능을 조금씩 확장하여 다른 부분이 존재하며, 이러한 특성을 고려하여 어느 구현체를 사용할 지 결정해야 한다.
- 주요 구현체
- 괄호 안의 소문자 약칭은 Common Lisp 커뮤니티에서 해당 구현체를 언급할 때 사용하는 약칭이다.
- 기계어 바이너리 구현체는 대부분 Foreign Function Interface(FFI)를 지원하여 이미 존재하는 *.so, *.dll, *.dylib 을 사용할 수 있다. Java Virtual Machine에서 동작하는 구현체는 JNI와 더불어 Java Native Acces가 등장하여 이전보다 편리하게 플랫폼에서 제공하는 기계어 자원을 사용할 수 있다.
- 물론 여기에 소개되지 않은 구현체도 얼마든지 존재한다.
- 상업용 라이선스
- 무료 라이선스
- 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과는 달리 타겟 플랫폼의 기계어로 동작한다.
- Quicklisp와 asdf
- Scheme: 대표적인 Lisp-1이다.
- Racket
- Guile Scheme
- Script-Fu
- 유명한 리스프 사투리
- AutoLISP, VisualLisp
- Arc
- Clojure
- Emacs Lisp(elisp)
- EuLisp
- ISLISP
- Javelin
- Le Lisp
- newLISP
- Railgun[10]
교육용으로 많이 쓰이는 스킴 방언으로, 리스프를 배우기 가장 편한 환경을 제공한다. 실제로 윈도우, 맥, 유닉스 계열에 다 호환되는 IDE를 제공하며, 거기에 GUI, 네트워크 등 웬만한 라이브러리도 다 포함되어있고 (Batteries Included!), 실제 프로그래밍과 기본적인 개념은 비슷하면서, 노가다가 될 수 있는 부차적인 기능들을 단순화시킨 프로그래밍 교육용 모듈까지 따로 포함되어 있다. Realm of Racket이라는 교재를 보면, 중고교생 대상의 기초적인 수준이긴 하지만 스킴 문법과 GUI, 네트워크 프로그래밍, 인공지능 등을 게임 프로그래밍 실습을 통해 겨우 300페이지 이하의 분량으로 설명하고 있다! 사실, 리스프라는 언어 자체가 프로그래밍 언어를 만들기 편리한 언어이기도 하고, Racket은 스킴의 저런 언어 자체의 확장성을 다소 극단적인 레벨로 끌어올린 언어이기에 가능한 것이다.
AutoCAD에 내장되어 일부 변형된 리스프. 실용적으로 리스프가 사용되는 대표적인 예이다.
폴 그레이엄이 만든 리스프 방언이다.
JVM 위에서 운용되며, Lisp 계열 언어의 간결함과 강력함, JVM의 이식성을 가지며 언어에서 지원하는 Software Transactional Memory(STM)과 잘 추상화된 관련 함수를 통한 비교적 쉬운 병행성 구현,[5] 전용 Build Tool인 Leiningen[6]과 Maven Repository가 결합하여 편리한 Build, 라이브러리 사용과 관리, Jenkins, Travis 등의 CI와 연동이 쉽고, Java를 지원하는 클라우드 서비스에 쉽게 올릴 수 있는 등의 장점으로 최근 주목받고 있다. 20세기 스타일을 유지하면서 최첨단 보조도구를 달고 나온 21세기형 Lisp 방언일지도. 매우 보수적인 금융계에서 유별나게 신기술 도입에 적극적인 씨티은행 본사에서 Clojure를 도입해 사용하고 있다고 밝혔다.
이맥스의 기반. Lisp-2이며, Lisp 계열 언어를 코딩하는데 Emacs 보다 좋은 툴은 21세기에도 그다지 많지 않다. 아이러니하지만, Lisp 방언이 아무리 많더라도 결국 그들중 대다수가 공통적으로 사용하는 에디터는 결국 이맥스이고, 이맥스 특징상 elisp을 모르고 사용하기는 좀 거시기하다보니 Lisp 방언 중 가장 많은 사용자를 확보하고 있는 언어이기도 하다.
유럽 버전의 Lisp. Lisp 표준의 권위자 중 한명인 Kent Pitman의 회고에 의하면, Common Lisp이 만들어질 당시, uucp(unix-2-unix copy) 로 서로 소통을 했었는데, 아무래도 국제 네트워크가 활성화되지 않아서 대륙간 메시지가 오가는데 며칠 이상씩 소요되었고, 덕분에 아시아(특히 일본)와 유럽은 논의에서 거의 배제되었다고 한다. 이 때문에 기분나뻐서 유럽 자체적으로 만들어 등장한 Lisp이라고도 하는데, Lisp-1이며 Common Lisp은 과거(하위호환)에 발목을 잡혔고, Scheme은 너무 작으니 그 중간 정도를 노리고 만들어졌다고 한다.
ISO 표준의 LISP이다. 다만, ISO에서 LISP 표준을 정의하려 한 것은 아니고 그냥 ISLISP의 표준이 ISO에서 만들어졌다고 봐야 한다. LISP의 창시자인 존 매카시 교수가 LISP을 하나의 표준으로 가두는 것을 좋아하지 않았기때문에 LISP의 ISO 표준이 아닌, ISLISP 방언의 ISO 표준으로 해달라고 직접적으로 요청했다고 한다.[7] Lisp-2이며, 여러 방언인 Common Lisp, Scheme, EuLisp, Le Lisp에 공통적으로 들어간 교집합 위주로 묶어서 언어를 만들고 표준화를 시킨다고 한다.
프랑스의 유명한 전산학 연구소인 INRIA에서 만들어진 Lisp 방언.
.NET에서 실행된다. 괄호가 적은 Sweet-Expression을 지원하고, 정적 타이핑도 개발되고 있다. 이름의 유래는 미사카 미코토의 능력 '초전자포'.
7. 커먼 리스프와 스킴 차이
- 커먼 리스프에서는 거짓과 빈 리스트를 같게 취급하지만 스킴에서는 그렇지 않다.
- 커먼 리스프에서는 변수와 함수의 이름 공간이 별도로 있지만 스킴에서는 그렇지 않다. 커먼 리스프에서는 함수와 변수의 이름이 같아도 된다.
car
라는 함수 이름이 이미 있어도 같은 이름의 변수를 만들 수 있다.
8. 여담
- 리스프와 관련된 격언(?)으로 Greenspun's tenth rule이라는 게 있다.(Greenspun의 10번째 법칙. Greenspun의 말에 의하면, 1-9번째 법칙이 있지는 않고 그냥 10번째 법칙이라 하면 기억에 잘 남을 것이라 생각했다고 한다.)[11]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)이 있다. 보통 실제 프로젝트에서는 커먼 리스프가 주로 사용되었고, 대학이나 기타 교육 목적으로는 스킴이 많이 사용되었다.
하지만, 근래들어 커먼 리스프는 여전히 강력하긴 하지만 좀 낡았다는 평가를 받고있고, 최근의 함수형 언어 붐(이라기는 좀 힘들지만)에 힘입어 스킴 쪽에 관심이 상당히 쏠린 편인데, 스킴은 실용적인 목적으로 사용하기엔 작아도 너무 작기 때문에[12] 요즘엔 스킴을 이리저리 확장시키고 모던한 기능들을 추가한 스킴 방언이 많이 나오고 있는 편이다. 이 새로운 스킴 방언의 대표적인 예로 저수준 매크로로 언어의 확장성을 크게 높여 여러가지 다른 언어로 쉽게 변신이 가능한 라켓(Racket)이 있다. - 이와 별도로 2007년에 갑툭튀하여 Java Virtual Machine 위에서 돌아가도록 만들어져 자바의 라이브러리를 그대로 끌어다 쓰면서 자바보다 높은 생산성을 자랑하는 클로저(Clojure)가 있다.
- 리스프는 너무나 많은 사투리를 가지고 있기 때문에, 보통 "리스프 커뮤니티"라고 부를 수 있는 것이 존재하지 않으며, 대신 "커먼 리스프 커뮤니티", "스킴 커뮤니티", "래킷 커뮤니티" 등 많이 쓰이는 사투리의 커뮤니티가 따로 존재한다. 당연하게도, 인터넷에서 검색해서 찾을 수 있는 자료 또한 이 항목과 같이 리스프 전반에 대해 설명하는 것이 아니라면, 이런 식으로 특정 방언에 속하는 리스프에 대한 것이 대부분이다. 하지만 한국에서 리스프 계열 언어는 매우 마이너한 언어에 속하며, 따라서 "리스프 커뮤니티"라는 제목 아래에 리스프의 여러 사투리에 대해 논의되는 커뮤니티가 극소수 존재한다.
- 영문권 리스프 프로그래머들은 예/아니오 구문인 P를 일상생활에 이용하는 경우가 많았다고 한다.Q: "Foodp" (밥먹을래?)A: "T" (True = 응.)A: "NIL" (False = 아니.)
- Y Combinator 창업자인 폴 그레이엄이 지독할 정도의 매니아로 알려져 있다. 두 권의 관련 서적을 출판했으며, 비아웹도 LISP 기반으로 만들었다. 관련 에세이도 자주 쓰고 있다.
- "만들면서 배우는 리스프 프로그래밍"이라는 번역서가 있다. 이 책은 절판되었는데 알라딘 온라인 중고에 14만 원짜리 매물이 한 개 있다.[13]
- 프로그래밍 언어 교육 사이트 Exercism에서 아래와 같은 리스프 패밀리를 지원한다.[14]
- Common Lisp
- Scheme
- Emacs Lisp
9. 관련 문서
10. 외부 링크
[1] https://iolanguage.org/[2] C 언어가 유닉스를 만들기 위해 탄생한 언어라면, LISP는 LISP를 만들기 위해 탄생한 언어라는 농담도 있다.[3] 어차피 모든 범용 프로그래밍 언어는 튜링 완전하기 때문에 기능면에서는 동일하다. 새로운 패러다임의 언어가 등장한다고 해서 전에 없던 완전히 새로운 것이 가능하게 되는 것이 아니고, 그저 표현하는 방식이 바뀌는 것 뿐이며 리스프는 언어 자체가 가지고 있는 이러한 특징을 이용하여 새로운 표현 방식, 문법 등을 embedding 하는 것이 가능하다. 물론 이럴 경우, 항상 실행 효율과 성능이 좋다고 보장할 수 없다. 구현 가능하다는데 의의가 있다. 즉 매크로를 쓰려면 잘 만들어야 한다.[4] Lisp-1 vs Lisp-2 (single-value-space vs multi-value-space)[5] 프로그래밍에서 기술적인 난이도로 가장 어려운 것으로 꼽는 것 중 하나가 바로 이것이다. 여러 프로세스나 쓰레드가 동시에 한 곳에 데이터를 읽고 쓸 때, 통제를 안하면 당연히 값이 엉망이 되는데, 통제하는 게 어려울 뿐더러 성공해도 제대로 못 하면 성능이 극악으로 떨어진다. 이를 효율적으로 잘 하는 것이 관건이다.[6] 이름은 'Leiningen Versus the Ants'라는 단편소설에서 따온 듯하다. 참고로 자바의 대표적인 빌드 툴이 Ant이다.[7] 참고로, 매카시 교수는 ANSI Common Lisp의 스펙이 만들어질 때도 비슷한 요청을 했다고 한다.[8] https://github.com/kimtg/Javelin[9] http://www.newlisp.org/[10] https://github.com/Academy-City/Railgun[11] https://philip.greenspun.com/bboard/q-and-a-fetch-msg?msg_id=000tgU&topic_id=22&topic=Ask+Philip[12] 그래서, 기존에도 스킴 컴파일러들은 표준적인 부분 이외에 다들 나름의 꽤 커다란 확장을 같이 탑재해서 배포했었다. 이런 경향이 반영된 탓인지, 최근의 표준인 R6RS부터는 더이상 작다고 보긴 힘들어졌는데, 스킴 위원회쪽에서 이런 경향에 반발세력도 꽤 있던 탓인지, 이제는 아예 small scheme, large scheme 두 가지 표준을 운영하는식으로 바뀌어 버렸다.[13] https://www.aladin.co.kr/shop/UsedShop/wuseditemall.aspx?ItemId=13986526&TabType=1[14] https://exercism.org/tracks?page=1