This utility includes 'gensym, macro_expand, bq(backquote of Lisp), c(comma of Lisp), ...'.

You can require a file which use macros by 'require_with_macro(required_name, delete_expanded_file, *macro_objs)'

I made this about three months ago. At that time, I thought I should
make the manual and the better examples of macro use like monads than
this trivial control structures before the release. Now I realize that
I can't have enough time to complete this job. So I gave up writing
the manual. Maybe you can see how to use it in comments and unit
tests.

Here is the source code:


Shamefully, I don't know how to make a gem. I would appreciate it if someone could make this as a gem.
저작자 표시 비영리 변경 금지

Tag : Lisp, macro, RUBY

Why Lisp? II

Posted 2009/05/13 14:50 by chanwoo

리습은 특별하다. 여러 가지 언어들이 자신은 특별하다고 주장하지만, 리습은 그런 모든 언어들이 가질 수 없는 점을 가졌다는 점에서 특별하다. 아마 프로그래밍 언어를 크게 가른다면, '리습 / 그 외의 언어들'로 가를 수 있을 것이다. 허무맹랑한 주장이라고 생각하는가? 한 번 그 이유를 들어보기 바란다.

몇 가지 비유를 들어보겠다.

만일 요술램프의 지니가 당신에게 한 가지 소원만 들어준다고 했다고 하자. 당신은 어떤 소원을 빌겠는가? 예쁜 여자친구? 어마어마한 돈? 영원한 생명? 글쎄. 나 같으면 어떤 소원이든 들어주는 반지를 달라고 하겠다.

무협지에서 어떤 사람의 내공이 가장 강한가? 오랫동안 수련한 사람? 글쎄. 북명신공을 지녀서 다른 사람의 내공을 흡수하는(악랄할 수도 있지만) 능력을 가진 녀석의 내공이 시간이 지남에 따라 가장 강해지지 않을까?

드래곤볼에서 어떤 종족의 특성이 가장 좋다고 생각하는가? 재생이 되는 나메크인? 변신이 되는 프리더의 종족? 단연 샤이어인이다. 전투를 거듭할 때마다 상대만큼 강해지는 녀석들이기 때문이다.

마찬가지다.

어떤 언어가 한 가지 특성을 마음대로 가질 수 있다면 어떤 특성을 가지는 게 좋을까? 다른 언어의 어떤 장점이든 가져올 수 있는 특성이 제일 좋지 않겠는가? 그리고 이게 바로 리습이 가진 능력이다.

C를 보라. 객체 지향 프로그래밍을 하기 위해서는 구조체와 함수포인터를 사용해서 흉내내는 수 밖에 없다. define_class 라는 추상화를 만들어낼 능력이 없기 때문이다.

자바를 보라. foreach 라는 추상화를 사용하기 위해 자바 프로그래머들은 1.5 버전까지 기다려야만 했다. 언어에서 foreach 라는 추상화를 만들어 낼 능력을 가지고 있지 못하기 때문이다.

리습은 '매크로'라는, C의 매크로와는 매우 다른, 추상화 능력을 가지고 있다. 그리고 이 매크로를 통해 객체 지향 프로그래밍, lazy evaluation, continuation, 하스켈에서 자랑으로 여기는 monad 등등의 개념들을 라이브러리로 흡수해 왔다. 언어 자체는 전혀 변경하지 않고서! 이것은 이렇게 할 수 있다는 주장이 아니라, 역사적인 사실을 말하는 것 뿐이다. 1980년대 후반, 객체 지향 개념이 유행하자 리습에서는 OOP를 라이브러리로 추가했다. OOP를 라이브러리로 추가한다는 게 무슨 말인지 이해가 안 간다면, 다음과 같은 예가 도움이 될 것이다. 당신이 수학에 관한 라이브러리를 사용한다면 sin, cos 같은 추상화를 사용할 수 있게 된다. 그렇지 않은가? 마찬가지로 OOP에 관한 라이브러리를 사용한다면 define_class 라는 추상화를 사용할 수 있게 되는 것이다.

다른 언어에서 어떤 매력적인 개념이 등장하든지 리습은 바로 그 개념을 흡수할 수 있다. 그 개념은 라이브러리 형태로 추가되기 때문에 언어의 코어는 전혀 커지지 않고, backward compatibility도 완벽하게 유지된다. 라이브러리가 추가되면 언어에 새로운 문법이 추가될 거라고 생각하는가? 어떤 사람들은 리습의 매크로가 새로운 문법을 추가하는 거라고 생각하는데 이는 착각이다. 리습에는 operator 뒤에 operand 들이 온다는 문법 - 예를 들면 (operator operand1 operand2 ...) - 외에 다른 문법이 없다. 방금 당신은 리습에 존재하는 모든 문법을 배운 것이다.

foreach와 같은 새로운 제어 구조를 만들어내는 것은 Smalltalk와 Haskell도 할 수 있다. 하지만, 어떤 개념/추상화든지 가져올 수 있는 능력을 가진 것은 리습 뿐이다. 리습, 스몰토크, 하스켈이 신의 언어라고 불리는 세 가지 언어지만, 리습은 언어 자체를 변경시키지 않고도 어떤 개념이든 흡수할 수 있다는 점에서 다른 모든 언어들과 근본적으로 다르다. 그리고 이런 차이를 만들어내는 것은 다음 세 가지이다. 어떤 언어가 리습인지를 판단할 때 다음과 같은 세 가지를 만족한다면 리습이라고 할 수 있을 것이다.

1. 데이터와 코드의 형태가 같다(homoiconicity). 리습이 어떤 개념이든 쉽게 흡수할 수 있는 것은 단지 매크로 때문만은 아니다. 여기서 나열하는 1번과 2번 특성이 매크로의 작성을 극도로 쉽게 만들어 준다.

2. 문법이 (하나밖에) 없다. Paul Graham 같은 사람들은 ‘리습에는 문법이 없다.’라고 말하기도 한다. (operator operand1 operand2 ...) 이게 리습 문법의 전부다..(이것이 다른 메타 프로그래밍이 가능한 언어들과 리습을 차별화하는 요소이다. 리습에는 파서가 없다. 리습의 코드 자체가 트리 형태로 되어 있기 때문에 메타 프로그래밍을 통해 조작해야 하는 abstract syntax tree가 어떤 형태일지 고민할 필요가 없다. 코드 그대로가 곧 그것이기 때문이다. 프로그래머는 늘 보던 코드 형태 그 자체를 조작하면 되기 때문에 메타 프로그래밍을 통해 어떤 결과가 나올지를 예상하는 것이 아주 쉽다. 반면에 '파서가 있는' 다른 언어들의 경우에는 코드가 파싱된 결과물인 트리를 조작한 후 그 트리가 unparse된 결과가 자신이 의도한 코드가 되도록 메타 프로그래밍을 해야 한다. 평소에 익숙히 봐오던 프로그램 코드이지만 파싱된 결과물이 어떤 구조의 트리로 바뀌는지 알게 뭐란 말인가? 이 같은 작업은 매우 '비직관적이고', '어려운' 일이다. 프로그래머가 평소에 봐오던 코드와 최대한 비슷한 형태로 코드를 데이터처럼 다루기 위해서 스트링을 사용할 수도 있지만, 이 같은 방법을 사용할 경우 inadvertent variable capture를 피할 수 있는 방법을 보통 제공하지 않기 때문에 구멍난 추상이 되기도 쉽고, 결정적으로 스트링은 트리처럼 구조적으로 다루기가 용이하지 않기 때문에 조금이라도 복잡한 메타 프로그래밍을 제대로 하기란 쉽지 않다.)

3. 매크로를 가지고 있다.

if 문, 가비지 콜렉션, REPL(대화형 셀)을 이용한 interactive programming, functional programming 등등은 모두 리습에서 처음 등장한 개념들이다. 다른 언어들이 이 같은 개념을 흡수하며 자신을 acceptable Lisp, 또는 alternative of Lisp이라고 광고한다. 파이썬이나 루비 같은 언어들이다. 하지만 위에서 나열한 리습의 가장 핵심적인 특성 세 가지는 이상하게도 어떤 언어도 흡수하지 못하고 있다. 왜라고 생각하는가? 저 특성들을 흡수하고 나면 그 언어는 리습이 되어버리기 때문이다.

예전에 다른 포스트에서 리습을 배우면 여러 가지 언어에 존재하는 개념들을 한 번에 배울 수 있다고 말한 적이 있다. 이제 그 이유가 왜인지 알았으리라 생각한다. 리습은 다른 언어가 장점으로 내세우는 개념을 손쉽게 흡수해버리기 때문에 멀티 패러다임 언어가 되기 쉽다. ‘리습으로는 함수형 프로그래밍을 해야 하고 객체 지향 프로그래밍은 못할 거야’라던가, ‘리습에서는 반복을 위해 재귀를 사용해야만 하고 루프문 같은 건 사용하지 못하겠지’와 같은 생각들은 모두 착각이다. 당신이 어떤 라이브러리를 사용하는지에 따라 리습은 어떤 모습의 언어도 될 수 있다. OOP 라이브러리를 사용한다면 객체 지향 방식으로 프로그래밍 할 수 있는 것이다.

하스켈에서 내세우는 monad 라는 개념을 배우고 싶은가? 루비나 파이썬, 스몰토크를 살펴봐서는 알 수 없을 것이다. 그렇다고 모나드가 무엇인지 배우기 위해 하스켈을 익힐 필요는 없다. 리습을 아는 사람이라면 리습을 통해 모나드의 개념을 익힐 수도 있고, 실제로 모나드 라이브러리를 사용할 수도 있다.

라이브러리의 수? 좋은 IDE? 약간 더 좋은 표현력? 이런 것들은 다 피상적인 것으로 금방 따라잡힐 수 있는 것들이다. 실제로 모든 C/C++ 라이브러리는 Common Lisp에서 호출 가능하고, 모든 자바 클래스는 Clojure라는 리습에서 사용 가능하다.(Clojure는 JVM 위에서 돌아가는 리습으로, 자바 코어나 자바 라이브러리들을 번거로운 절차 없이 바로 호출 가능하다. 속도도 파이썬이나 루비와는 비교할 수 없을 정도로 빠르다.)

요약하면, 리습의 무서움은 그 잠재력에 있다. 이 글을 읽고 리습에 관심이 생겼다면 Clojure라는 리습을 한 번 살펴보기 바란다. 내가 아는 한 가장 아름답고, 강력한 언어다. Erlang의 강점인 concurrent programming을 위한 특성까지도 가지고 있다. pragprog.com에서 도 나와 있으니 공부하기도 어렵지 않다. 가장 최근에 나온 리습 dialect이기 때문에 기존의 Common Lisp이나 Scheme이 가지고 있는 단점들이 없고 정말 말끔한 느낌을 주는 언어다.

마지막으로 리습에 관련된 일화를 간단히 소개하며 끝낼까 한다. ILC 2002(International Lisp Conference 2002)중에 있었던 일이라고 한다. 출처는 다음과 같다. http://smuglispweeny.blogspot.com/2008/02/ooh-ooh-my-turn-why-lisp.html

Peter Norvig이 ILC 2002 키노트 스피치를 맡았을 때였다. 그는 스피치에서 '파이썬이 리습이다'라는 주장을 했다고 한다.(아마도 파이썬이 리습의 대용으로 충분하다 라는 주장이었을 것으로 생각된다.) ILC 에서 키노트 스피치를 맡아 '파이썬이 리습이다'라는 주장을 펼친 것은 마치 마틴 루터가 부활절 바티칸 미사를 맡아 진행하다가 개신교를 선언한 것 마냥 놀라운 행동이라고 할 수 있다. Peter가 스피치를 마치고 질문을 받았을 때, 그 자리에 있던 John McCathy(리습의 창시자)가 물었다고 한다. "파이썬은 코드를 데이터처럼 다룰 수 있습니까?" Peter의 대답은 "아니오"였고 Peter는 John이 계속해서 질문하기를 기다렸지만 John은 더 이상 아무 말도 하지 않았다.

John McCathy는 루비가 리습으로부터 많은 영향을 받았다는 얘기를 듣고 루비에 대해서도 같은 질문을 한 적이 있다. 대답은 역시나 "아니오"였고, 그는 "루비가 코드를 데이터로 다루지 못한다는 점에 있어서 루비는 리습이 이미 1960년에 도달했던 지점을 아직도 따라잡지 못하고 있다."라고 말했다.

리습을 제외한 모든 언어에 물어보라. "그 언어는 코드를 데이터로 다룰 수 있습니까?" 대답은 언제나 "아니오"일 것이다. 그리고 이것이 리습이 그토록 특별한 이유이다. 코드를 데이터로 다룰 수 있다면? 당신은 어떤 추상화(OOP, monad와 같이 아래로부터 위로 쌓아올려지는 개념을 말하는 것이다. 당신이 함수 하나를 짰다면, 일련의 프로시저를 하나의 함수로 추상화한 것이다.)도 할 수 있는 능력을 갖게 된다. 그게 뭐 그리 중요하냐고? 자바 프로그래머들에게 묻고 싶다. 당신은 자바 1.5 이전에 파이썬의 for와 같은 foreach문을 만들어서 직접 사용할 수 있었는가? 할 수 없었다. Sun이 foreach를 자바에 추가해줄 때까지 기다려야만 했다. 당신은 foreach문이 무엇인지 머릿속에서 알고 있었다. 알고 있는 개념을 왜 만들거나 사용할 수 없단 말인가? 언어의 한계 때문이다. 리습에는 그런 한계가 없다. 리습은 프로그래머가 생각하는 어떤 개념도 추상화할 수 있도록 무한한 자유를 부여하는 유일한 언어다.

정말 마지막으로 ^^; 한 가지만 더 얘기하면 리습을 공부하기 위해 '컴퓨터 프로그램의 구조와 이해 (Structure and Interpretation of Computer Programs)'를 보는 사람들이 있는데, 저 책을 읽으면 그냥 프로그래밍 공부는 되겠지만, 리습의 강점인 'homoiconicity(코드와 데이터의 형태가 같음)와 매크로'를 이해하는 데는 아무 도움도 되지 않는다. 책이 그런 내용을 전혀 담고 있지 않기 때문이다. 한 마디로 저 책은 프로그래밍 공부를 위한 책이지, 리습이 가지는 고유한 특성이나 개념을 알려주는 책은 아니라는 것이다.

혹시 이 주제와 관련하여 궁금한 것이나 다른 나누고 싶은 얘기가 있다면 chanwoo.yoo@gmail.com 으로 메일을 보내주기 바란다.

----------

리습에 관한 오해를 풀고차 추가적으로 몇 자 더 적어봅니다. KLDP에 달았던 댓글을 그대로 가져왔습니다.

"뭔가 사람들의 글을 읽다보면.. '이건 오핸데..'라고 느껴지는 부분이 너무 많아 어디서부터 얘기를 해야 될지 잘 모르겠다는 생각도 듭니다. ㅠㅠ

일단 커먼 리습 코딩은 emacs, vi, 이클립스에서 모두 가능하구요, 마치 visual studio와도 같은 매우 잘 만들어진 리습 전용의 IDE도 두 개나 있답니다. 흐음.. 특정한 에디터를 타는 편은 아닌 것 같아요.

리습의 학습 커브는 ㅠㅠ 정말 짧습니다. 어떤 프로그래밍을 전혀 모르시는 분이 있다고 합시다. 그 분은 아마 프로그램을 짜기 위해 두 단계를 거쳐야 할 겁니다.

1) 일단 그 언어의 문법을 익혀야 합니다. *, ;, {}, [], #, <<, @, $ 등의 기호가 무엇을 의미하는지 알아야 하고, 키워드들의 precedence 규칙 등에 대해 익혀야 할 겁니다.

2) 문법을 다 익혔다면 자신이 원하는 함수나 라이브러리들을 찾아서 사용할 수 있을 겁니다. 사용할 수 있는 라이브러리 함수나 메써드들이 어떤 것인지 알아보는 것을 새로운 언어를 배운다고 하지는 않습니다. 그렇죠?

보통 언어를 배운다는 것은 1)의 단계를 말합니다. 그렇지 않나요? 리습에서는 1)의 단계가 거의 없다고 보시면 됩니다. 2)의 단계만 있을 뿐입니다. 라이브러리로 존재하는 dsl을 익힌다는 것 역시 리습에서는 2)의 과정입니다.

리습의 코어와 dsl의 차이는 크지 않은 정도가 아니라 거의 전혀 없다고 할 수 있습니다. 누군가에게 dsl과 리습의 코어 코드를 주고 어떤 것이 코어 리습이냐고 묻는다면 구분할 수 없을 겁니다. 왜일까요? 리습에서 강조하는 얘기 중 하나는(물론 스몰토크와 하스켈도 마찬가지이지만), 'built-in과 추가된 library간의 질적인 차이는 없다'라는 것이기 때문입니다. 둘의 문법은 완전히 동일할 겁니다. operator의 이름만 차이가 있을 뿐이지요.

그리고 기본적인 정의를 재정의함으로써 나타나는 혼란은 오히려 MOP를 사용하는 언어들인 루비나 스몰토크의 문제입니다. 하지만 저 문제는 저 언어들에서도 그다지 문제가 되지 않습니다. 왜냐면 제정신 박힌 사람들이 언어의 코어를 재정의하는 일을 남발하지는 않기 때문입니다. 리습에서도 마찬가지입니다. 매크로의 장점은 코어를 건드리지 않고 내가 원하는 기능을 확장할 수 있다는 것이고, 물론 코어를 건드릴 수도 있지만, 그리고 저도 많은 리습 코드를 보아 왔지만 기본적인 control structure를 재정의하는 코드는 보지 못했습니다. 그러기보다는 차라리 추가적인 control structure를 정의해서 라이브러리로 사용하겠지요.

코드의 분석 및 가독성 문제에 대해서는.. PL 강의노트에 있는 's-expression이 다른 언어들의 형태에 비해 가독성이 좋다'는 문구에 대해서 후배가 저에게 왜냐고 물어본 적이 있습니다. s-expression이 읽기 쉬운 이유는 극도로 uniform한 형태를 지니고 있기 때문입니다. 그저 순서대로 코드를 읽어가면 될 뿐입니다. 어디서부터 코드를 읽어야 할지 모르는, 또는 시선이 여기저기로 왔다갔다 해야 하는 그런 사태가 발생할 수가 없다는 것이죠."

저작자 표시 비영리 변경 금지

Tag : Clojure, Haskell, Lisp, Python, RUBY, SICP, smalltalk, Structure and Interpretation of Computer Programs, 루비, 리습, 스몰토크, 컴퓨터 프로그램의 구조와 이해, 파이썬, 프로그래밍 언어, 하스켈

Clojure with Slime and Emacs

Posted 2009/01/20 21:47 by chanwoo
If you have downloaded clojure and installed it by 'ant', you need directories and files as follows.

clojure-mode
swank-clojure
slime

Here is my directory structure and setting files.

chanwoo-yooyi-macbook-air:clj chanwoo$ ls
clojure            code            swank-clojure
clojure-mode        library
clojure_20081217.zip    slime

~/.emacs


/usr/local/bin/clj (You should do "sudo chmod +x clj")


If you finish settings as the above example, you can run clojure as an inferior mode in Emacs by M+x slime or M+x run-clojure.

2009.5.14 update

How to install 'clojure.contrib'(This is a common library for Clojure)

1) svn co http://clojure-contrib.googlecode.com/svn/trunk/ clojure-contrib
2) ant -Dclojure.jar='directory in which clojure.jar is'/clojure.jar


저작자 표시 비영리 변경 금지

Tag : Clojure, Emacs, Slime

« PREV : 1 : 2 : 3 : 4 : 5 : ... 16 : NEXT »