1. []는 백터에만 쓰는게 아니라 파라메터에서 사용하고, let에서 사용하고 뭔가 정규화되지 않아서 처음 배우는 입장에선 오히려 더 헷깔립니다. 백터만 빼고 모두 () 사용해도 전혀 상관 없을 것 같은데 말이죠. 리습이 좋왔던 점이 문법은 정말 단순하다 였는데 클로저는 리습을 알아도 배우기가 만만치 않네요.
2. 클로저는 () 를 리습보다는 생략하는 편인데 이게 오히려 코드 가독성을 떨어트리는 경향이 있는것 같습니다. ex) clojure : (let [x y z w]) , lisp: (let ((x y) (z w))) 바인딩이 여러개 냐열 될 경우 헷깔릴 수 있을듯요. 물론 아래로 늘려 쓰거가 중간에 , 를 사용해도 되지만, 전반적으로 느낌은 너무 심플하게 만들려고 하다 보니 가독성은 무시한것 같은 느낌이랄까..
()를 좀더 많이 사용하면 타이핑을 더 많이 해야하지만 가독성이 좋와지고 에디팅 하기도 더 편합니다. () 단위로 삭제나 이동이 편리하기땜에..
3. 리습은 class를 사용하기 때문에 오브젝트단위 관리가 편한데 클로저는 class 가 없기때문에 namespace를 가지고 관리를 하는것 같습니다. 쿠테여 class를 쓰지 않는 이유를 잘 모르겠네요.. inheritance 등 편리한 기능들이 나름 있을듯 한데.. multimethod dispatch 도 클라스를 사용하지 않고 구현 하려다 보니 뭔가 어쩡쩡한 느낌이고 해킹같은 느낌이 강하게 드네요. class를 쓰지 않기 때문에 meta object programming 도 지원 안할테고 여튼 득보다 실이 많은 느낌??
4. 클로저는 뭔가 직관적이지 않은것들이 많은것 같습니다. 뒤져보면 방법이 있긴 한데 뭔가 좀 해킹을 하고 있다는 느낌이 듭니다. 한 예로 (for [ x [1 2] y [3 4]] (print x y)) 이러면 (1 3) (2 4) 이렇게 프린트가 될것 같은데 강제로 nested 가 되면서 (1 3) (1 4) (2 3) (2 4) 가 프린트된다는데서 깜짝 놀랐습니다. 이건 디자인 적으로 에러가 아닌가 싶네요. 기본적으로는 unnested for loop 이 맞을 것 같고 nested for loop 이 필요하다면 그냥 (for [x [1 2]] (for [y 3 4] (print x y))) 하면 되는데 말이죠.. unnested for loop 은 불가능 한가요? 음...
물론 장점도 많은것 같습니다. 그래서 더 아쉽다는.. ^^
여하튼 좀더 배우면서 나중엔 제 질문에 대해 다시 곱씹어 보면서 왜 그런지 하나 하나 이해 했으면 합니다.
1. bracket notation은 special form을 구분할 수 있게 해줍니다. 파라미터 받을 때나 바인딩할 때 씁니다. 일관성이 있습니다. 중구난방은 아닙니다(설마요). 참고로 클로저는 파리미터로 받는 자리에서 바인딩까지 할 수 있습니다. e.g. [{:keys [a b]}]. 이상하군요 전 이게 참 좋던데요.
2. ,나 스페이스를 동일시하기 때문에 얻은 syntatic sugar 정도가 아닐까 생각합니다. 이 역시 적응되면 오히려 더 깔끔하게 보입니다.(콩깍지)
3. 대신 protocol 모델이 있죠. protocol은 개인적으로 오브젝트 시스템보다 더 해피해킹 친화적인 느낌입니다.
4. (map print [1 2] [3 4])가 있는데 for를 쓸 필요가 있을까요?
1. []는 백터에만 쓰는게 아니라 파라메터에서 사용하고, let에서 사용하고 뭔가 정규화되지 않아서 처음 배우는 입장에선 오히려 더 헷깔립니다. 백터만 빼고 모두 () 사용해도 전혀 상관 없을 것 같은데 말이죠. 리습이 좋왔던 점이 문법은 정말 단순하다 였는데 클로저는 리습을 알아도 배우기가 만만치 않네요.기존 리습과 차별화 된것 처럼 포장하기 위해서는 타이핑 줄이면서 실제로는 차이 없으면서도 차이 있는 것 처럼 보이게 만드는게 중요했으리라 봅니다.
2. 클로저는 () 를 리습보다는 생략하는 편인데 이게 오히려 코드 가독성을 떨어트리는 경향이 있는것 같습니다. ex) clojure : (let [x y z w]) , lisp: (let ((x y) (z w))) 바인딩이 여러개 냐열 될 경우 헷깔릴 수 있을듯요. 물론 아래로 늘려 쓰거가 중간에 , 를 사용해도 되지만, 전반적으로 느낌은 너무 심플하게 만들려고 하다 보니 가독성은 무시한것 같은 느낌이랄까..
()를 좀더 많이 사용하면 타이핑을 더 많이 해야하지만 가독성이 좋와지고 에디팅 하기도 더 편합니다. () 단위로 삭제나 이동이 편리하기땜에..뭐 익숙해 지면 거의 동일합니다만, 언급하신 문제가 있으므로 뉴라인이나 인덴테이션에 더 신경쓰게 됩니다.
3. 리습은 class를 사용하기 때문에 오브젝트단위 관리가 편한데 클로저는 class 가 없기때문에 namespace를 가지고 관리를 하는것 같습니다. 쿠테여 class를 쓰지 않는 이유를 잘 모르겠네요.. inheritance 등 편리한 기능들이 나름 있을듯 한데.. multimethod dispatch 도 클라스를 사용하지 않고 구현 하려다 보니 뭔가 어쩡쩡한 느낌이고 해킹같은 느낌이 강하게 드네요. class를 쓰지 않기 때문에 meta object programming 도 지원 안할테고 여튼 득보다 실이 많은 느낌??namespace는 package에 대응됩니다. 착각하신 것이 있는데, 클로져는 함수형 언어이지 객체지향 언어가 아니라는 점입니다. 쉽게 말씀드리면 Common Lisp이 Clojure의 수퍼셋이라 보시면 될 겁니다. multimethod는 oop의 그것이 아니라 타입시스템과는 별개의 함수 디스패치하는 방법이죠. 당연히 OOP를 하자고 하면 잘 안됩니다. OO 언어가 아니라 MOP도 당연히 존재하지 않는 것이죠.그런데 희안한 것이 일단 익숙해지면 CLOS 같은 것이 전혀 그립지 않게 된다는 점입니다.
4. 클로저는 뭔가 직관적이지 않은것들이 많은것 같습니다. 뒤져보면 방법이 있긴 한데 뭔가 좀 해킹을 하고 있다는 느낌이 듭니다. 한 예로 (for [ x [1 2] y [3 4]] (print x y)) 이러면 (1 3) (2 4) 이렇게 프린트가 될것 같은데 강제로 nested 가 되면서 (1 3) (1 4) (2 3) (2 4) 가 프린트된다는데서 깜짝 놀랐습니다. 이건 디자인 적으로 에러가 아닌가 싶네요. 기본적으로는 unnested for loop 이 맞을 것 같고 nested for loop 이 필요하다면 그냥 (for [x [1 2]] (for [y 3 4] (print x y))) 하면 되는데 말이죠.. unnested for loop 은 불가능 한가요? 음...맞습니다. 그래서 저는 for를 잘 안씁니다. 굳이 하려면 다음과 같이 해야 하죠(for [[x y] (map vector [1 2] [3 4])](println (* x y)))그런데 위 코드는 사실 위험한 코드입니다. 클로져에서는 side effect가 목적이라면 lazy sequence를 리턴하는 operation은 버그를 유발 할 수 있기 때문이죠.그래서 순수 side effect를 위한 operation을 하는 것이 버그를 미연에 방지하는 방법입니다.(doseq [[x y] (map vector [1 2] [3 4])](println (* x y)))map을 이용한 것도 lazy sequence가 리턴되기 때문에 비추입니다(만, 최근 버전에서는 버그인지 non lazy 가 되더군요)
물론 장점도 많은것 같습니다. 그래서 더 아쉽다는.. ^^
여하튼 좀더 배우면서 나중엔 제 질문에 대해 다시 곱씹어 보면서 왜 그런지 하나 하나 이해 했으면 합니다.까놓고 보자면 마음에 안드는 부분도 꽤 있습니다. 예를들자면 falsey value 인데요. if 등에서 false 나 nil은 falsey value인 반면 #{} {} () [] 등은 truthy value 입니다.저는 (empty? ...)를 많이 사용하는데요, 추천하는 방법은 (seq ...)를 사용하는 겁니다. seq는 predicate가 아니고 conversion function이라 저는 이렇게 된 코드를 보면 짜증이 나더군요.아무튼 2% 부족한 느낌이 드는 설계라 봅니다.또 이게 개인의 재산이다 보니, 기능의 추가나 언어의 확장이 좀 이해 안갈 때가 있는데 이건 뭐 요즘은 흔한 방식이니까요.가장 큰 장점을 저는 현존하는 최고의 GC 기반 JVM에서 움직이는 Lisp이고 (제대로 쓸 수 있다면) 방대한 Java 라이브러리가 가장 큰 매력이라는 생각입니다. 또, Lisp 일은 많지 않지만, Clojure 일은 꽤 많거든요.
다음은 질문하신 내용들에 대한 저의 의견입니다. 저의 의견들 중에는 저의 개인적 취향도포함되어 있음을 미리 밝힙니다.1. []는 백터에만 쓰는게 아니라 파라메터에서 사용하고, let에서 사용하고 뭔가 정규화되지않아서 처음 배우는 입장에선 오히려 더 헷깔립니다. 백터만 빼고 모두 () 사용해도 전혀 상관없을 것 같은데 말이죠. 리습이 좋왔던 점이 문법은 정말 단순하다 였는데 클로저는 리습을알아도 배우기가 만만치 않네요.[]는 벡터 리터럴을 표현하거나 binding-forms(let binding, function paameter binding, loopbinding, doseq binding, for bidning, ...)을 만들 때 사용됩니다. 이 두 가지 경우를제외하고는 달리 쓰이지 않습니다. 아마도 binding-form이라는 공통점을 놓치셔서 오는 혼란인것 같습니다.그리고 클로저가 Common Lisp보다 익혀야 할 표기법이 많다는 점은 저도 인정합니다. 하지만 맵자료형을 생성할 때도, (hash-map ...) 식보다는 {...}의 리터럴 방식이 저는 편하고 더좋습니다. 그리고 이런 리터럴 방식이 DSL을 만들 때에도 훨씬 편리하고요. Common Lisp의경우는 reader macro를 만들어 해결할 수도 있겠지만, 클로저에서는 reader macro의 도입으로초래되는 코드의 복잡성을 피하기 위해 reader macro 생성을 원천적으로 허용하지 않기에, 언어차원에서 리터럴 방식을 제공해 주어야만 합니다. 그리고 클로저가 지원하는 자료형의 수가 얼마안되기 때문에 리터럴의 수가 많다고 보기도 힘듭니다.
2. 클로저는 () 를 리습보다는 생략하는 편인데 이게 오히려 코드 가독성을 떨어트리는 경향이있는것 같습니다. ex) clojure : (let [x y z w]) , lisp: (let ((x y) (z w))) 바인딩이 여러개냐열 될 경우 헷깔릴 수 있을듯요. 물론 아래로 늘려 쓰거가 중간에 , 를 사용해도 되지만,전반적으로 느낌은 너무 심플하게 만들려고 하다 보니 가독성은 무시한것 같은 느낌이랄까..()를 좀더 많이 사용하면 타이핑을 더 많이 해야하지만 가독성이 좋와지고 에디팅 하기도 더편합니다. () 단위로 삭제나 이동이 편리하기땜에..클로저는 괄호의 사용을 최대한 적게 사용하는 쪽을 선택했습니다. 그리고 저는 괄호의 사용을줄인 것이 가독성을 떨어뜨리기보다는 오히려 높여 준다고 봅니다. 시각적으로 볼 때 괄호가적은 것이 코드 이해에 더 도움이 되기 때문입니다. 물론 이로 인해서 초래되는 불편함도 없는것은 아닙니다. 제가 제일 불편하게 느끼는 점은 #_ reader macro를 사용해서 form을 commentout할 때 binding vector나 map 안에서는 항상 쌍으로 표기해 주어야 한다는 점 정도입니다.3. 리습은 class를 사용하기 때문에 오브젝트단위 관리가 편한데 클로저는 class 가 없기때문에namespace를 가지고 관리를 하는것 같습니다. 쿠테여 class를 쓰지 않는 이유를 잘모르겠네요.. inheritance 등 편리한 기능들이 나름 있을듯 한데.. multimethod dispatch 도클라스를 사용하지 않고 구현 하려다 보니 뭔가 어쩡쩡한 느낌이고 해킹같은 느낌이 강하게드네요. class를 쓰지 않기 때문에 meta object programming 도 지원 안할테고 여튼 득보다 실이많은 느낌??클로저는 함수형 언어를 지향하는 리습입니다. 이에 관련한 대답은 예전에 제가 써놓은 글로대신하겠습니다.
4. 클로저는 뭔가 직관적이지 않은것들이 많은것 같습니다. 뒤져보면 방법이 있긴 한데 뭔가 좀해킹을 하고 있다는 느낌이 듭니다. 한 예로 (for [ x [1 2] y [3 4]] (print x y)) 이러면 (13) (2 4) 이렇게 프린트가 될것 같은데 강제로 nested 가 되면서 (1 3) (1 4) (2 3) (2 4) 가프린트된다는데서 깜짝 놀랐습니다. 이건 디자인 적으로 에러가 아닌가 싶네요. 기본적으로는unnested for loop 이 맞을 것 같고 nested for loop 이 필요하다면 그냥 (for [x [1 2]] (for[y 3 4] (print x y))) 하면 되는데 말이죠.. unnested for loop 은 불가능 한가요? 음...원하시는 작업은 (map (fn [x y] [x y]) [1 2] [3 4])와 같은 방식으로 처리가가능합니다. 그리고 for는 원래부터 nested iteration 지원을 목적으로 설계된매크로입니다. 그래서 실제로 logic 프로그래밍할 떄 많애 쓰입니다. logic 프로그래밍의경우에는 중첩의 깊이가 2--3 단계 이상인 경우도 아주 흔한데, 그런 경우를 대비해 미리 만들어놓은 매크로로 이해하시는 것이 좋습니다.
그런 의미에서, 중첩이 아주 많은 경우에는 다음의 첫 번쨰 코드가 두 번째 코드보다 낫다고저는 봅니다만.(for [a [1 2 3 4 5]b [6 7 8 9 10]c [11 12 13 14 15]d [16 17 18 19 20]e [21 22 23 24 25]][a b c d e])(for [a [1 2 3 4 5]](for [b [6 7 8 9 10]](for [c [11 12 13 14 15]](for [d [16 17 18 19 20]](for [e [21 22 23 24 25]][a b c d e])))))
클로저를 배우면서 좀 익숙 해지나 싶더니 잘 이해가 되지 않는게 종종 나오네요.
--
이 메일은 Google 그룹스 'Korean Clojure User Group' 그룹에 가입한 분들에게 전송되는 메시지입니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 clojure-kr+...@googlegroups.com에 이메일을 보내세요.
이 그룹에 게시하려면 cloju...@googlegroups.com에 이메일을 보내세요.
웹에서 이 토론을 보려면 https://groups.google.com/d/msgid/clojure-kr/d800bf73-30f7-4a53-b588-04a22f18ec92%40googlegroups.com을(를) 방문하세요.
더 많은 옵션을 보려면 https://groups.google.com/d/optout을(를) 방문하세요.
Macro 은 리습에서 타 언에 대비 가장 돋보이는 기능 이였습니다. DSL 를 만드는데 최적화 된 넘이라..Macro 를 쓰다 보면 코드가 정말 아름답다라고 느껴지고 나는 예술을 하고 있구나 착각에 빠질 때가 있습니다.클로저는 아직 보진 않았지만 또 어떤 차이가 숨어있을지 기대하고 있습니다. ^^
물론 Macro로 원하는 문법을 구현해 가면서 사용할 수 는 있겠지만 모든 사람이 사용하는 기본 문법이랑 차원이 다른 예기라..그동안 클로저의 발전 과정을 봐보니.. 뭔가 큰 변화가 있길 기대는 안 하는게 좋을 것 같군요.한 사람이 만든거라 개인 취향이 상당이 반영 되 있고 그냥 익숙해지는 수 밖에는 없을 듯 요.