FrontPage FindPage TitleIndex RecentChanges UserPreferences E D R S I H C
 
Programming In Haskell
MoniWiki/WordIndexWikiWyg/2009-11-16WikiWyg/2009-11-03DebugIteratorBarMacro › ProgrammingInHaskell
This page contains some information about the Korean translation of [http]Programming in Haskell[] by Graham Hutton.

하스켈로 배우는 프로그래밍 입문서인 Programming in Haskell ( http://www.cs.nott.ac.uk/~gmh/book.html )을 이제 다 번역하고 교정 작업도 끝나가고 마무리 단계라 책 광고를 겸해 올립니다. 출판사에서 머리말이 약간 길어도 좋으니 컴퓨터 과학 전공자가 아닌 개발자나 IT 실무자들도 읽고 관심을 가질 만한 내용이 들어있었으면 좋겠다는 부탁이 있어 작성하게 된 옮긴이 머리말(을 가장한 장광설)입니다.


옮긴이 머리말(을 가장한 장광설)


이미 하스켈에 관심을 갖고 이 책을 선택한 분들에게 이 옮긴이 머리말은 그저 지은이 머리말과 책 1장 내용의 장황한 사족에 지나지 않을 것입니다. 특히 프로그래밍을 처음 배울 목적으로 이 책을 선택하신 분들은 이 장황한 옮긴이 머리말을 건너뛰셔도 좋습니다. 그럼에도 굳이 옮긴이로서 머리말을 쓰는 까닭은, 현업 IT 전문가의 입장에서 하스켈이라는 프로그래밍 언어가 어떤 의미로 다가올 수 있을지 좀더 구구절절한 이야기를 풀어놓고 싶기 때문입니다.

하스켈은 원래 더 나은 프로그래밍 언어를 고안하려는 프로그래밍 언어 언구자들이 제때 계산법을 따르는 순수 함수형 언어를 표준화함으로써 연구자들끼리 아이디어를 더욱 효과적으로 교류하려는 연구 목적으로 만들지기 시작한 언어입니다.[1] 그래서 하스켈은 자연히 프로그래밍 언어 분야의 연구 성과를 충실하게 반영하도록 설계되었고, Hugs나 GHC와 같은 하스켈 구현에 꾸준히 연구자들이 참여하여 하스켈 98 표준 이외에도 최근의 프로그래밍 언어 연구 결과를 적용한 여러가지 확장 기능을 추가로 제공하고 있습니다. 이러한 설계와 구현은 언어 자체의 학술적 심미성으로만 그치지 않았으며, 안목 있는 IT 전문가들의 눈에 하스켈을 빠르고 정확하게 소프트웨어를 개발할 가능성을 열어주는 프로그래밍 언어로 돋보이게 했습니다. 이런 IT 전문가들이 업무에 하스켈을 도입했고 그 과정에서 축적된 경험의 일부를 오픈 소스로 공개하여 활발한 하스켈 개발자 공동체를 이루기에 이르렀습니다. 그리하여 하스켈은 오늘날 연구자들에게 각광받는 학술적 프로그래밍 언어인 동시에 범용적인 프로그램을 빠르고 정확하게 작성할 수 있는 실무적 프로그래밍 언어로 발전하였습니다.

실무적인 범용 언어로서의 하스켈

하스켈은 군용 보안 소프트웨어 작성 (예: Galois), 금융상품 분석 (예: Credit Suisse), 하드웨어 설계(예: Bluespec), DNA 및 고분자 화합물을 이용한 신약 개발 (예: Amgen) 등 다양한 업계에서 이미 많은 이윤을 창출하는 상용 소프트웨어의 핵심 기술 구현에 요긴하게 쓰이고 있습니다. 예로 든 대표적인 기업 외에도 매년 함수형 프로그래밍 국제 학회(The ACM SIGPLAN International Conference on Functional Programming)와 함께 열리는 함수형 프로그래밍 사용 기업(Comercial Users of Functional Programming) 모임이 최근 몇 년간 성황을 이루며 하스켈을 비롯한 함수형 언어를 업무에 성공적으로 도입한 기업들의 사례 보고가 잇따르고 있는데, 그 중 하스켈을 업무에 쓰는 기업들의 목록[2]이 하스켈 홈페이지에 정리되어 있습니다. 그리고 하스켈을 업무에 도입하려는 기업에 자문을 제공하는 컨설턴트들[3]도 생겨나고 있습니다.

하스켈 오픈 소스 소프트웨어 공동체의 활동 또한 요즘 들어 더욱 활기를 띠고 있습니다. 널리 쓰이는 스크립트 언어인 펄의 차세대 판 Perl 6 구현 Pugs[4], 패치 이론 바탕의 분산 버전 관리 시스템 Darcs[5], 그리고 하스켈 개발에 가장 많이 쓰이는 글래스고우 하스켈 컴파일러[6] (Glasgow Haskell Compiler, GHC) 또한 하스켈로 개발이 진행되는 오픈 소스 프로젝트입니다. 이런 굵직한 프로젝트 외에도 각종 하스켈 라이브러리 및 응용프로그램, 그리고 C등 다른 언어로 작성된 기존 라이브러리와의 연동 등 요긴한 중소 규모 프로젝트가 활발히 진행되고 있습니다. 아치 리눅스는 현재 500개 이상의 하스켈 관련 배포판 패키지를 제공하고 있으며[7], 데비안 데비안 리눅스도 약 250개 가량의 배포판 패키지를 제공하고 있습니다. 이렇게 하스켈 오픈 소스 개발 활동이 활기를 띠는 것은 최근 몇 년간에 걸쳐 BSD의 port 패키지 관리 시스템과 유사한 하스켈 패키지 관리 시스템인 Cabal[8]이 하스켈 라이브러리와 프로그램을 배포하는 사실상의 표준으로 자리잡도록 했으며, 펄의 CPAN과 같은 Hackage[9]라는 하스켈 패키지 저장소를 중심으로 하스켈 개발자들이 만든 오픈 소스 패키지들을 한데 모아 배포하는 등 하스켈 개발자 공동체의 노력이 모인 결과입니다.

참고로 이 책은 처음 프로그래밍을 배우는 분들도 읽을 수 있도록 구성된 프로그래밍 입문서이므로 하스켈을 실무에 응용하는 보기를 다루지는 않습니다. 이 책을 읽고 하스켈을 실무에 응용하는 데 관심을 갖게 될 IT 전문가들께는 최근 출간된 "Real World Haskell"[10]을 이 책 다음으로 살펴볼 것을 추천합니다. 그 책은 실제로 하스켈을 업무에 쓰는 개발자들이 자신들의 경험을 정리해 놓은 책으로 데이타베이스, 웹, 시스템, GUI, 네트워크, 병렬 프로그래밍 등을 아우르는 실무적인 예제를 포함합니다.

프로그래밍 언어 연구 성과를 충실히 반영한 하스켈의 우수성

그렇다면 여러 분야에서 업계를 선도하는 기업의 IT 전문가들이 더 잘 알려진 다른 프로그래밍 언어들을 두고 왜 굳이 하스켈을 업무에 도입했을까요? 업계마다 기업마다 업무가 판이하게 다르기 때문에 정확히 어떤 이유라고 억측할 수는 없습니다. 하지만 우리가 하스켈을 써 보면서 발견한 것과 마찬가지로 그들도 하스켈을 쓰면서 다른 프로그래밍 언어와 대조되는 장점을 발견하여 하스켈에 매료되었음에 틀림없습니다. 옮긴이들의 제한된 경험으로 하스켈의 면면을 모두 비출 수는 없겠지만, 우리가 하스켈을 직접 쓰면서 그리고 하스켈을 도입하여 성공한 프로젝트들을 접하면서 알게 된 바를 간단히 정리해 보았습니다.

효율성 높은 컴파일 언어이면서도 스크립트 언어처럼 잽싼 하스켈

하스켈은 스크립트 언어의 장점과 컴파일 언어의 장점을 동시에 갖는 언어입니다. 스크립트 언어는 개발을 잽싸게 진행할 수 있다는 장점을 내세우고 있습니다. 이는 스크립트 언어가 대개 고급언어를 직접 실행하는 해석기(interpreter)를 바탕으로 하기 때문인데, 프로그램을 수정한 후 바로 해석기에 불러들여 그 수정한 부분만 따로 검사하기에 매우 편리합니다. 하지만 해석기를 바탕으로 한 언어는 번역기(compiler)로 미리 저급 언어로 번역한 다음 실행하는 컴파일 언어보다 효율이 확연히 떨어지기 때문에 반복적인 수치연산 등 계산량이 많은 코드를 직접 작성하기에 알맞지 않다는 단점이 있습니다. 반면 컴파일 언어는 스크립트 해석기로 돌리는 것보다 훨씬 효율이 뛰어난 실행파일을 생성할 수 있다는 것이 장점입니다. 그러나 C와 같은 컴파일 언어는 작은 부분을 고치더라도 전체 프로그램을 실행 파일로 다시 연결한 다음 돌려보아야 하며 프로그램 시작 지점부터 항상 실행을 시작하기 때문에 수정한 부분만 따로 떼어 검사하기 번거롭다는 단점이 있습니다. 하스켈과 같은 함수형 언어의 경우 프로그래밍 언어 연구자들이 이미 수십 년 전부터 확립한 해석기와 번역기의 경계를 넘나드는 기술을 이전부터 구현하고 있었습니다. 그렇기 때문에 마치 스크립트 언어처럼 잽싼 개발도 가능하고 저급 언어로 번역된 효율성 높은 실행 파일도 얻을 수 있습니다. 최근에는 고급 언어와 그를 번역한 저급 언어를 연동하는 기술이 함수형 언어 구현 뿐 아니라 가상머신 기반의 언어나 스크립트 언어가 가지는 효율상의 한계를 극복하기 위한 JIT 번역(just-in-time compile) 등에도 응용되고 있습니다.

많은 설명보다 간단한 보기로 대표적인 스크립트 언어인 파이썬과 하스켈을 비교해 보면 하스켈 역시 잽싼 개발을 지원하는 언어라는 사실이 와닿을 것입니다. 예컨대, 파이썬 스크립트 test.py 에 제곱을 구하는 함수를 아래와 같이 잘못 정의한 다음
  def square(x): return x + x
해석기에서 불러들여 함수를 시험해 보면 잘못된 결과를 얻습니다.
  >>> import test
  >>> test.square(3)
  6
이제 test.py의 square 함수 정의를 다음과 같이 바르게 고친 다음
  def square(x): return x * x
해석기에서 다시 불러들이면 바뀐 함수 정의대로 올바른 결과를 얻습니다.
  >>> reload(test)
  <module 'test' from 'test.py'>
  >>> test.square(3)
  9
이렇듯 잽싸게 오류를 수정하고 스크립트를 다시 불러들여 바로 시험해 볼 수 있다는 장점이야말로 스크립트 언어가 각광받게 된 가장 큰 이유라고 생각합니다. 하스켈과 같은 함수형 언어의 대화식 환경[11]에서도 이와 마찬가지로 개발을 잽싸게 진행할 수 있습니다. 하스켈 스크립트 Main.hs 에 제곱을 구하는 함수를 다음과 같이 잘못 정의한 다음
    square x = x + x
GHC의 대화식 환경인 ghci에서 불러들여 함수를 시험해 보면 잘못된 결과를 얻습니다.
    Prelude> :load Main
    Main> square 3
    6
이제 Main.hs 의 square 함수 정의를 다음과 같이 바르게 고친 다음
    square x = x * x
다시 불러들이면 바뀐 함수 정의대로 올바른 결과를 얻습니다.
    Main> :reload
    Main> square 3
    9
하지만 GHC에는 대화식 환경 뿐만 아니라 ghc 번역기가 있으므로 완전히 기계어로 번역된 실행 파일을 얻을 수도 있다는 것이 일반적인 스크립트 언어와 차별화되는 점입니다. 다음과 같이 Main.hs 에 프로그램 시작을 나타내는 간단한 main 함수를 추가한 후
    square x = x * x
    
    main = print (square 3)
GHC 번역기로 실행 파일을 만들어 돌려볼 수 있습니다.
  $ ghc Main.hs
  $ ./a.out
  9 

안전한 정적 타입 언어이면서도 동적 타입 언어처럼 유연한 하스켈

하스켈은 안전한 정적 타입 시스템을 바탕으로 하고 있으면서도 동적 타입 언어에 가까운 유연성을 갖는 언어입니다. 그 이유를 전문 용어를 동원해 설명하자면 하스켈은 Hindley-Milner 타입 시스템을 바탕으로 할 뿐 아니라, 타입 클래스라는 독특한 기능을 갖고 있기 때문이라 할 수 있습니다. Hindley-Milner 타입 시스템을 바탕으로 하는 언어는 타입 유추(type inference)를 바탕으로 인자 여러모양새(parametric polymorphism)를 자연스럽게 구사함으로써 동적 타입 언어처럼 유연하게 포괄적인 코드 작성이 가능합니다. 타입 클래스는 여러의미(overloaded) 함수를 Hindley-Milner 타입 시스템과 잘 어우러지면서도 깔끔하게 정의하기 위해 하스켈에서 도입한 독특한 기능으로, 물건 중심 언어의 인터페이스(interface)와 비슷하지만 그보다 더 유연하고 확장성이 있습니다.

여러모양새(polymorphism)란 정적 타입 언어에서 타입에 따른 코드 중복을 줄이고자 함수를 한 번만 정의하여 여러모양으로 쓸 수 있도록 하는 기능을 일컫는데, 그 방법에 따라 여러 종류가 있습니다. 함수형 언어에서는 대개 인자 여러모양새를 구사하며 물건 중심(object oriented) 언어에서는 대개 하위타입 여러모양새(subtype polymorphism)를 구사합니다. 대표적인 물건 중심 언어인 자바(Java)에서 아래와 같이 입력 스트림으로부터 내용을 읽어들이는 getContents 함수를 한 번만 정의하면
  String readContents(InputStream is) { /* ... */ }
인자 타입으로 선언한 InputStream 타입의 물건(object)인 System.in 은 물론 그 하위 타입인 FileInputStream 이나 DataInputStream 타입의 물건인 fis 나 dis 에도 적용할 수 있는데
  FileInputStream fis;
  DataInputStream dis;
  // ...
  String s1 = readContents(System.in); // InputStream
  String s2 = readContents(fis);   // FileInputStream
  String s3 = readContents(dis);   // DataInputStream
이러한 기능을 바로 하위타입 여러모양새라고 합니다. 한편, 현대적인 타입 시스템을 갖춘 함수형 언어인 하스켈에서는 아래와 같이 length 함수를 한 번만 정의하면
    length []      = 0
    length (x:xs)  = 1 + length xs
정수, 글자 등 아무 타입 원소를 갖는 리스트에 모두 적용할 수 있는데
    Main> length [2,3,5,7]  -- [Int] (정수 리스트)
    4
    Main> length ['a','b','c']|  -- [Char] (글자 리스트)
    3
    Main> length [[],[0]]  -- [[Int]] (정수 리스트의 리스트)
    2
이러한 기능을 바로 인자 여러모양새라고 합니다.

과거에 인자 여러모양새를 직접 구사할 수 없었던 정적 타입 물건 중심 언어들이 다른 방법으로 에둘러 인자 여러모양새를 시늉내느라 큰 불편을 겪다 결국 포괄적 프로그래밍(generic programming)을 지원하는 언어 기능을 덧붙일 수밖에 없었습니다. 초기 C++의 경우, 인자 여러모양새를 흉내내기 위해 C에서처럼 조악한 전처리 매크로나 타입 안정성이 전혀 보장되지 않는 void* 를 거치는 임의적 형변환 방식에 의존해야 했습니다. 이후 C++ 표준라이브러리를 제대로 설계하면서 템플릿을 언어에 추가하여 템플릿 메타프로그래밍으로 인자 여러모양새를 직접 표현할 수 있게 됩니다. Java의 경우 모든 물건들의 최상위 타입인 Object 에 대한 하위타입 여러모양새를 이용하되 필요에 따라 동적으로 Object 타입으로부터 원래의 하위 타입으로 강제 변환을 하는 방식을 쓸 수밖에 없었습니다. C의 void* 보다야 낫지만 동적인 상위 타입으로의 강제 변환 역시 타입 안전성을 보장하지 못하므로 정적 타입 언어의 장점을 거스릅니다. 결국 Java도 뒤늦게 제너릭을 추가면서 타입을 검사하고 나중에 지우는 방식으로 인자 여러모양새를 직접 표현할 수 있게 됩니다. 이러한 역사적 사실을 종합해 보면 C++ 템플릿이나 Java 제너릭과 같은 포괄적 프로그래밍의 유래가 바로 탄탄한 이론을 바탕으로 설계된 정적 타입 함수형 언어에서 일상적으로 사용하던 인자 여러모양새라는 것을 알 수 있습니다.

함수형 언어의 인자 여러모양새와 물건 중심 언어의 포괄적 프로그래밍이 다른 점은 함수형 언어에서는 인자 여러모양새가 걸음마 프로그래머도 구사하는 기본 초식인 반면 물건 중심 언어에서는 포괄적 프로그래밍을 상대적으로 많은 노력을 요하는 고급 기술로 여긴다는 것입니다.

물건 중심 언어에서 포괄적 함수나 클래스를 작성하려면 보통 함수나 클래스를 작성할 때와 달리 포괄적 인자를 별도로 표시하는 등의 노력이 추가로 필요합니다. 그렇기에 C++나 Java와 같은 언어로는 라이브러리 설계자라면 여유를 두고 포괄적 프로그래밍을 잘 지원하는 라이브러리를 만들 수 있을 것입니다. 하지만 상대적으로 신속한 개발을 요하는 응용프로그램 개발자라면 이미 만들어진 포괄적 라이브러리를 즐겨 쓸지는 몰라도 포괄적 프로그래밍의 장점을 살리는 코드 작성을 선뜻 내켜하지 않을 가능성이 높습니다. C++나 Java를 다루는 책들도 대개 포괄적 프로그래밍을 고급 기법으로 분류하곤 합니다. 정적 타입 물건 중심 언어에만 익숙했던 개발자들이 파이썬과 같은 동적 타입 스크립트 언어에 열광한 또 하나의 이유가 바로 이것이라 생각합니다. 동적 타입 스크립트 언어는 타입을 무시하고 상대적으로 적은 노력으로 포괄적 프로그래밍을 할 수 있습니다. 그래서 이런 스크립트 언어가 유행한 후 동적 타입 언어만이 유연하며 정적 타입 언어는 유연하지 않다는 생각이 퍼지게 되었습니다.

그러나 현대적 타입 시스템을 갖춘 함수형 언어를 접해 보았다면 정적 타입 언어가 유연할 수 없다는 것은 섣부른 편견이 아닌가 의문을 제기할 수밖에 없을 것입니다. 왜냐하면 앞서 살펴본 length 함수처럼 하스켈에서는 타입 정보를 전혀 표시하지 않고도 여러모양 함수를 작성할 수 있으며 타입 시스템이 자동으로 그 타입을 유추해 주기 때문입니다. 함수형 언어를 전혀 접해 보지 않은 개발자가 length 의 정의를 보면 하스켈을 동적 타입 스크립트 언어로 오해할 정도입니다. 이렇듯 하스켈로 프로그래밍을 배우기 시작하면 인자 여러모양새를 자신도 모르게 구사하며 유연한 포괄적 프로그래밍을 하게 된다는 것을 이 책을 통해 발견하게 될 것입니다. 그리고 비록 당장의 업무에 하스켈을 도입하지 않더라도 하스켈을 익히면서 물건 중심 언어에서 고급 기법으로 여기는 포괄적 프로그래밍을 자신있게 구사할 내공을 쌓는 재미 또한 쏠쏠할 것입니다.

타입 클래스(type class)란 말 그대로 타입(type)의 분류(class)로 덧셈이나 곱셈과 같이 모든 타입에 다 적용 가능한 여러모양 함수는 아니지만 공통점이 있는 많은 타입에 적용 가능한 함수의 타입을 딱 떨어지게 정의하기 위해 하스켈에서 도입한 독특한 기능입니다. 물건 중심 언어의 인터페이스와 유사한 개념이지만 그보다 더 확장성이 있다는 점에서 뛰어나다는 것을 하스켈의 타입 클래스와 자바의 인터페이스를 비교함으로써 알아보기로 합시다. 하스켈의 타입 클래스와 Java의 인터페이스를 비교하기 전에 한 가지 짚고 넘어갈 것은, 물건 중심 언어에서 클래스라 불리는 기능은 같은 이름이지만 완전히 다른 개념이라는 것입니다. Java와 같은 물건 중심 언어에서는 클래스 정의가 곧 하나의 타입에 대응되지만 하스켈에서 타입 클래스는 타입을 모아 놓은 집합을 대표한다고 생각하면 이해하기 좋습니다.

하스켈의 타입 클래스는 물건 중심 언어에서 주로 쓰는 인터페이스라는 기능과 비슷합니다. 예를 들면 Java에는 java.lang.Comparable<T> 라는 인터페이스가 있는데 이것이 바로 하스켈의 Ord 클래스에 해당한다고 볼 수 있습니다. 예컨대, Java에서는 MyType 이라는 새로운 타입을 정의하면서 다음과 같이 인터페이스를 상속받아 compareTo 메서드를 구현합니다.
  // Java 인터페이스 정의
  interface Comparable<T> {
    int compareTo(T x);
    // ...
  }
  
  // 타입을 정의하며 인터페이스 상속
  public class MyType implements Comparable<MyType> {
    public int compareTo(MyType x) {
      // ... 메서드 구현 ...
    }
    // ...
  }
하스켈도 이와 비슷하게 새로 정의한 MyType 타입을 Ord 클래스의 인스턴스로 선언하고 메서드를 구현합니다.
  -- 하스켈 타입 클래스 정의
  class Ord a where
    compare :: a -> a -> Ordering
    (<), (<=), (>=), (>) :: a -> a -> Bool
    max, min :: a -> a -> a

  -- 타입 정의
  data MyType =  -- ...

  -- 타입 정의와는 별도로 인스턴스 선언
  instance Ord MyType where
    compare x y = -- ... 메서드 구현 ...
여기서 눈여겨 볼 점은 Java 인터페이스와 하스켈 타입 클래스의 차이점입니다. Java에서는 인터페이스 상속이 타입 정의의 일부분이지만 하스켈에서는 타입을 정의한 다음 별도로 인스턴스 선언을 합니다. 작은 차이 같지만 타입 정의에 종속된 Java 인터페이스에 비해 타입 정의로부터 자유로운 하스켈 타입 클래스는 월등한 확장성을 갖습니다. 하스켈 타입 클래스는 타입을 정의한 후 언제라도 인스턴스 선언을 추가할 있습니다. 다른 모듈에 타입이 정의되어 있고 심지어 그 소스를 수정할 수 없는 라이브러리에 정의된 타입이라도 클래스 인스턴스로 선언하는 데는 아무 문제가 없습니다. 아직 하스켈에서 타입 정의와 타입 클래스가 느슨한 결합을 함으로써 갖는 장점이 실무에서 어떤 의미가 있을까 갸우뚱하는 분들을 위해 좀더 피부에 와닿는 구체적인 상황을 상상해 보겠습니다.

OO기업 홍보부 지원 담당 데이타베이스 프로그래머 김대리는 Java에 능숙합니다. 홍보부에서 데이타베이스 검색은 웹 인터페이스로 자동화되어 있지만 검색한 고객 정보로 통계를 내기 위해 검색 결과를 매번 스프레드시트로 일일이 옮겨적어야 하는 애로사항이 있었습니다. 김대리는 이를 자동화하는 프로젝트를 Java로 시작하여 Xmlizable 이라는 인터페이스를 정의하고 데이타베이스 엔트리 등 각종 데이타 구조를 Xmlizable 인터페이스를 상속받아 엑셀에서 읽어들일 수 있는 XML로 변환하도록 깔끔하게 처리했습니다. 이후 추가되는 테이블 엔트리도 Xmlizable 인터페이스를 상속해 XML 변환 메서드만 정의하면 되는 확장성 있는 시스템을 구축했다는 생각에 김대리는 행복합니다. 이 일로 김대리는 홍보부에서 신임을 받게 되었고 부장님은 더 정확한 통계 분석을 위해 이전 텍스트 기반 시스템에 남아있는 데이타도 스프레드시트로 불러올 수 있게 통합하는 프로젝트를 김대리에게 맡깁니다. 이전 시스템을 관리하던 개발자는 한참 전에 그만뒀지만 이전 시스템도 Java로 작성되어 있고 소스도 남아있다니 다행이었습니다. 업무가 갑자기 늘어나긴 했지만 김대리에게 큰 문제는 아닙니다. 기존 시스템에서 사용하던 소스코드에서 필요한 타입들이 Xmlizable 인터페이스를 상속하도록 고쳐 XML 변환 메소드만 구현해 주면 되므로 늘상 하던 작업과 별다를 것이 없었습니다.

문제는 OO기업이 XX기업을 인수합병하면서부터 시작됩니다. XX기업 홍보부에서 쓰던 시스템을 통합하는 일이 김대리에게 떨어졌고 부장님은 당연히 김대리가 이번에도 금방 처리해 주겠거니 기대합니다. 김대리도 XX기업 역시 Java를 썼다는 보고에 따라 예전처럼 일정을 잡습니다. 그런데 XX기업의 홍보부는 효율을 매우 중시해서 비공개 상용 라이브러리를 구입해 쓰고 있었으며 데이타도 일반 DBMS가 아닌 Berkeley DB에 들어있었던 것입니다. 이전처럼 인터페이스를 상속해 XML 변환 메소드만 추가하면 될 줄 알았는데, 소스도 없는 라이브러리라 인터페이스 추가는 언감생심입니다. 김대리는 임베디드 파일 DB란 게 있다는 말만 들었지 한번도 다뤄 본 일이 없어 DBMS 수준의 데이타 통합을 하려 해도 일정을 맞출 자신이 없습니다. 인수합병 과정의 칼바람같은 구조조정으로 XX기업 홍보부 전산팀은 온데간데 없고 회사 분위기도 워낙 어수선해 어디 하소연할 데도 없습니다. 김대리는 분명 물건 중심 디자인 원칙을 충실히 따랐는데 뭐가 잘못됐는지 영문을 알 수 없습니다. 내키지는 않지만 밤샘을 해서라도 그 상용 라이브러리에서 필요한 모든 타입을 한 겹씩 감싸안은 데이타 타입을 한 벌 새로 정의하는 방법밖에 없는 듯 합니다. 이런 걸 디자인 패턴에서 데코레이터(decorator) 패턴이라고도 한다니 그래도 아주 잘못된 방법은 아닐 것이라 애써 자위해 봅니다만 뭔가 찜찜하고 억울하다는 느낌은 지울 수 없습니다.

만일 OO기업과 XX기업이 하스켈을 쓰고 있었고 김대리가 Xmlizable 이라는 타입 클래스를 쓰고 있었다면 어땠을까요? 김대리는 고민할 필요도 없이 예전에 하던 대로 XX기업의 데이타 타입들을 Xmlizable 클래스의 인스턴스로 선언하여 XML 변환 메서드만 추가로 구현하기만 하면 될 것입니다. 왜냐하면 하스켈 타입 클래스는 타입 정의에 묶여 있지 않기 때문에 이전의 소스코드를 건드릴 필요가 없기 때문입니다.


마지막으로 하스켈의 타입 클래스와 Java의 인터페이스를 활용하는 보기를 통해 하스켈 타입 클래스가 물건 중심 언어의 진화에 미친 영향을 짚어 보겠습니다. 하스켈에서 특정 클래스와 관련된 타입에만 제한적으로 적용 가능한 함수를 여러의미 함수(overloaded function)라 합니다. 크기 비교가 가능한 타입으로 이루어진 리스트를 정렬해 주는 라이브러리 함수 sort 가 바로 대표적인 여려의미 함수의 예입니다. ghci 대화식 환경에서 sort 의 타입을 알아보면 다음과 같습니다.
    List> :t sort
    sort :: Ord a => [a] -> [a]
Ord a => 라는 클래스 제약이 있기 때문에 아무 리스트 [a] 에 다 적용 가능한 것이 아니라 타입 인자 a 가 Ord 클래스의 인스턴스일 때만 함수 적용이 가능합니다. Java에서도 하스켈의 sort 에 상응하는 sort 함수를 다음과 같이 자바 제너릭 인자 T 가 만족해야 할 인터페이스 제약 extends Comparable<T> 를 표시하여 정의할 수 있습니다.
  public static <T extends Comparable<T>>
    List<T> sort(List<T> list) {
      // ... 아무 정렬 알고리듬으로 list를 정렬 ...
      return list;
    }
이와 같이 제너릭과 인터페이스 제약을 함께 사용해 하스켈의 여러의미 함수에 대응하는 자바 함수를 정의할 수 있는 것은 결코 우연이 아닙니다. 하스켈 타입 클래스 디자인에 핵심적 역할을 한 필립 와들러(Philip Wadler)라는 유명한 프로그래밍 언어 연구자가 이후 자바 제너릭을 디자인하는 데도 핵심적인 역할을 했기 때문입니다. 또한 참고로 현재 C++ 표준안(C++ 98)에는 Java의 인터페이스 제약 표시처럼 하스켈의 클래스 제약에 해당하는 개념을 직접 표현할 방법이 없었는데, 최근에 디자인된 컨셉(concept)이라는 기능이 새 C++ 표준의 초안(C++ 0x draft)에 들어갔다고 합니다. 따라서 다음 C++ 표준부터는 어떤 의미에서는 Java보다 하스켈 타입 클래스와 같은 개념을 더 잘 표현할 수 있는 강력한 기능이 추가될 것으로 기대하고 있습니다. 이와 관련해 더 자세히 알아보려면 필립 와들러 본인이 하스켈 타입 클래스와 자바 제너릭에 대해 구글에서 강연한 동영상[12]을 추천합니다.

----
last modified 2009-09-16 17:08:55
EditTextFindPageDeletePageLikePages