2015년 6월 26일 금요일

초심자의 첫번째 언어

코딩 초심자의 첫 번째 언어로는 크게 두 가지 의견이 대립한다고 생각한다. 첫 번째는 C이고, 다른 하나는 해당 시기의 패러다임을 대표하는 적당한 하이레벨 언어이다. 이 둘은 각각 다른 전제를 깔고 있다.


C를 첫 번째 언어로 가르치자는 주장은 컴퓨터가 결국 상당히 오랜 기간동안 노이만 머신이나 그와 유사한 형태의 기계일 것이라는 가정을 깔고 있다.

C의 로우레벨함에 진저리를 내는 사람도 많지만, 어쨌거나 C는 어셈블러 코딩을 피하기 위한 언어로 일정 정도의 추상화를 제공하기 위해서 등장한 언어였고 등장할 당시에는 어셈블러에 비해 30%의 오버헤드를 떠안은 상태로 성공적인 OS 프로그래밍을 할 수 없으리라고 여겨지기도 했다.

그렇다면 C는 구체적인 CPU 아키텍쳐를 감추고 무엇을 공통요소로 추출해서 무엇으로 추상화하는가? 나는 그것이 결국 노이만 머신이라고 생각한다. C언어를 통해서 바라본 컴퓨터는 각 리소스가 메모리 주소로서 접근되고 그것을 처리하는 장치가 있는 장치이다.

이 가정이 C언어를 첫 언어로 가르치자는 주장을 정당화하는지 여부를 평가하자면 크게 두 가지를 평가해야 한다고 본다. 한 가지는 이 가정이 현실에 부합하는가, 그리고 다른 한 가지는 그것이 사실이라면 유의미한가.

나는 가정이 현실에 부합한다고 본다. 굳이 C언어를 가르쳐야 하느냐는 의문과 함께 cs101 언어로 자바, 파이썬 등이 채택되는 동안, 여전히 우리의 컴퓨터는 노이만 머신에 기반한 구조를 가지고 있다.

문제는 이 사실이 유의미하느냐는 문제이다. "컴퓨터는 노이만 머신에 기반한 구조를 가지고 있고 C언어는 이에 가깝다. 그런데 그래서 그게 뭐?" 라는 질문은 첫 번째 질문에 대한 대답보다 확신에 차서 대답하기는 힘들다.

컴퓨터의 동작 원리를 이해하기 위해서 C언어를 가르쳐야 한다면 정말로 그것으로 설명이 되는가? C언어를 배운다고 해서 아키텍쳐와 OS 과목에서 가르치는 원리들 - 메모리 로컬리티와 작업 내용이 캐쉬 위에 올라가느냐 또는 분기 예측의 성공과 실패 여부가 속도에 미치는 영향, 가상 메모리와 페이징 및 메모리의 파편화, 쓰래슁 등을 이해할 수 있는가? 이런 로우레벨한 문제를 고려한 코드를 짜는 데 C언어로 코딩을 배우는 것이 도움이 되는가? 실상 생각해보면 별 도움이 되지 않는다. C언어로 메모리를 수동관리해가면서 코드를 짜는 것은, 로우레벨한 문제의 상당히 한정된 일부를 다뤄보는 데 그칠 뿐이다.


두 번째 입장, 적절한 하이레벨 언어로 코딩을 가르치자는 입장은 computational thinking 이라고 부르는 어떤 정신을 심어주는 것이 핵심적인 문제이고 로우레벨한 문제는 이것을 가능하게 하는 수단이지 그 자체가 목적이 아니라는 입장이라고 요약하고자 한다. 또한 위에서 언급한대로, 실상 로우레벨한 문제는 그 자체로 배워야 할 거리들을 형성하고 있어서, "노이만 머신으로 추상화한 언어"인 C언어에서는 이미 다룰 수 없는 부분을 많이 가지고 있다. 그건 C언어로 코딩을 배운다고 해결될 문제가 아니라, 그 자체로 따로 배워야 할 문제이다.

이런 주장에 대해서 내가 가진 의구심은, 지금까지 관찰되어온 바에 의하면 코드를 다른 어떤 것으로 추상화한 것보다는 노이만 머신으로 추상화한 것이 더 오랫동안 유효했다는 점이다. lisp의 역사는 사실은 C보다도 길고, 각종 사물에 대한 메타포로 코딩하는 OOP에 대한 열광도 이제 조금은 식은 이 시점에, 자바같은 극단적으로 명사(noun)적인 사고를 강요하는 언어가 장기적으로 도움이 되었을까, 함수형 패러다임은 과연 컴퓨터가 노이만 머신에서 멀어질 때까지 유효할까 등에 의문을 가지게 된다.

여전히 컴퓨터는 노이만 머신에 가까운 구조로 되어 있고, 이 사실은 적어도 어떤 영역에서 작업을 하는 사람들에게는 중요하다. 위에 언급한 '어차피 따로 배워야 하는 로우레벨한 영역에 대한 지식들'도 사실 노이만 구조를 이해할 때 더 쉽게 이해할 수 있다.


이 둘 사이에서 나는 어떤 강한 의견을 가지고 있지는 않고 양자를 다 부분 긍정하면서 관전할 뿐이다.

다만 이런 관점에서, 첫 번째 언어로 무엇을 택해야 한다는 것은 없지만 받아들일 수 없는 언어들은 있다.

C++은 추상화되지도 않았고 딱히 노이만 머신으로 추상화하는 언어도 아니어서 둘 모두에 속하지 않고, js를 가르친다면 차라리 함수의 first class citizen 특성을 부각시켜서 가르치는 게 맞지 로우레벨 접근 안 되는 C로 가르치는 것은 반대이다.

결국 전형적인 내 입장이 되고 만다. 꼭 이것이어야 한다는 것은 없다, 이것은 용납 불가능하다는 목록은 있다.

2015년 2월 1일 일요일

리눅스상 C/C++ 빌드툴 정리.

"빌드툴이 무엇이다" 라는 것까지는 알고 있는 사람들을 대상으로 리눅스 환경에서 고려될만한 빌드툴들을 가벼운 마음으로 경험상 비교한 것.

크게 보면 다음 세 가지 카테고리로 먼저 분류를 하게 된다.
  1. 직접 cc 명령어로 컴파일하고 링크함. 예제파일 이상의 수준에서 쓸 일 없음.
  2. Makefile을 작성하고 make 명령어로 1을 수행함.
  3. Makefile을 만들어주는 프로그램(예-autotools)을 이용해서 2→1을 수행함.
여기서 1은 대체할 툴이 어쩌고 할 여지가 없고,
2에서 make 대안으로 언급될만한 것은 ninja,
3에서 autotools의 대안으로 언급될만한 것은 cmake, 그리고 내 경우에는 gyp, gn 등


ninja와 make를 비교하면 가장 중요한 차이는 make는 스스로 빌드툴 역할을 하는 상황을 상정하지만 ninja는 하지 않는다는 것이다. make의 경우에 make 자체가 빌드 시스템의 프론트엔드인 경우, 다시 말해서 Makefile을 인간이 직접 작성하는 것을 전제하고 만들어져서 소스가 되는 파일을 눈으로 읽어가면서 작업하는 것이 그래도 할만하고 조건부 빌드같은 기능도 필요한 것으로 간주되어 있다.

반면에 ninja의 경우에는 정말로 빌드를 위한 의존관계 정리만으로 할 일을 최소화하고 있다. 다시 말해서 autotools나 cmake의 백앤드로 작동하기 위한 것이지 스스로 빌드툴 역할을 할 생각은 없다는 것이다. 컨디셔널 빌드? 각종 프로그램적 커스텀? 그건 다 cmake같은 툴에서 ninja 파일을 생성하는 시점에 할 일이다. ninja는 그저 기계적으로 빠르게 의존관계를 처리해서 빌드만 하면 된다. 그리하여 ninja의 리소스파일은 유닉스 텍스트 파일이지만 사람 눈으로 읽기에 친절하게 하려는 목표를 가지고 있지 않다. 그리고 실제로 make와 속도의 차이가 난다고 한다.


그 뒷 단계의 autotools/cmake 등을 비교하면..

autotools는 첫번째 난관은 진정 제대로 쓰려면 m4라는 언어 하나를 더 배워야 한다는 점이다. 개인적으로 "순수하게 declarative한 언어의 예시"로 언급하는 이상으로는 이 언어를 활용하지 못하고 있다.

두번째로는 autoconf-autoreconf와 automake의 이원화된 시스템이라는 점.

세 번째는 첫번째와 두번째의 시너지가 일으키는 복잡성.

마지막으로는 빌드 관리툴이라기보다는 GNU 시스템의 타볼 패키지를 생성하는 도구에 가깝다는 점이다. 다시 말해서 autotools를 기반으로 한 소스코드 디렉토리는 다양한 환경을 지원하는 프로젝트의 소스코드라기보다는 GNU운영체제의 배포용 패키지에 가깝다. GNU 운영체제가 나름 다양한 타겟 아키텍쳐를 지원하지만 근본적으로 GNU 밖에서 쓰기에 적절하지 않다. 윈도우 환경에서 빌드하는 데 악명이 높다.

cmake는 그냥 무난하고 대세스러운 느낌. 특별히 설명할 말을 모르겠다. 특히 지금처럼 잘 알지도 못하는 상황에서는.


지금 무슨 프로젝트를 시작하게 된다면 cmake/ninja가 내 선택이 될 것 같다.


가벼운 인상비평들.