까멜레오 OCaml 지원.

Posted 2008. 4. 28. 13:48
까멜레오는 주로 Python과 C로 작성되었습니다. UI와 위젯 작성은 파이썬으로 하고, 속도가 생명인 미디어 재생/분석 코드는 C로 작성되어 있습니다.  그런데, 프로젝트를 하다보니 코드를 C로 작성하면 아무래도 작성 시간도 오래 걸리고, 품질도 그리 높지가 않더군요. 특히, 메모리 깨먹는 버그나 멀티쓰레드 관련 버그는 정말 괴롭습니다.

최근에 이런 문제를 돌파해보고자 함수 언어인 OCaml을 까멜레오 구현 언어로 추가하기 위한 작업을 끝마쳤습니다. 특히, 비디오 처리(video processing) 코드를 OCaml로 작성해서 오류를 줄이고 작성 속도도 높여보자는 것이 가장 중요한 취지입니다.

기존 까멜레오 코드가 상당 부분 파이썬으로 코딩되어 있기 때문에 OCaml와 Python을 연결하기 위해 Pycaml 프로젝트를 가져다가 조금 고쳤습니다. OCaml과 Python이 직접 붙는 것은 아니고, Ocaml의 C 인터페이스를 이용해 Python/C API에 붙이는 방식으로 구현되어 있습니다.

아직 기본적인 데이타 타입 변환만 있고, Python의 list, tuple. dict와 OCaml의 list, tuple, assoc list 등을 변환해주는 코드 등이 부족한 상태라 사용이 그리 쉽지는 않은 상태입니다. 이쪽은 OCaml로 코드를 작성하면서 조금씩 추가해 나가려고 생각하고 있습니다.



파이썬에서 모듈을 임포트할 때는 현재 패키지의 위치에 따라 상대적으로 모듈을 임포트합니다.

예를 들어 glux.draw.pixmap 모듈에서 import cairo하면 glux.draw.cairo, glux.cairo, cario 순으로 임포트를 시도합니다. 만약 glux.draw.cairo라는 모듈이 우연히 존재했다면 원래 의도했던 cairo 모듈을 임포트하는 게 쉽지 않은 일이 됩니다. sys.modules를 통해서 직접 모듈을 찾아오거나, glux.draw.cairo를 glux.draw.gluxcairo 같은 이름으로 변경해야 합니다. 그래서 파이썬 개발자들은 보통 패키지 내부 모듈이라도 표준 라이브러리와 겹치는 이름을 사용하길 꺼려합니다.

다행히 파이썬 2.5부터는 PEP328에 정의된 from __future__ import absolute_import를 사용하면 절대 임포트(absolute import)를 기본으로 만들 수 있습니다. 절대 임포트를 사용하면 위와 같은 상황에서 glux.draw.cairo가 아닌 cairo 모듈을 임포트할 수 있습니다. 이후에 나올 파이썬(파이썬 3000 포함)은 절대 임포트가 기본이 되고 import .cairo와 같은 형태로 점을 찍어서 상대 임포트를 한다고 하는군요.

까멜레오 개발하면서 상대 임포트에 따른 의도치 않은 모듈 임포트를 겪은지라 포스트를 남겨봤습니다.

Using Haskell from Python with ctypes

Posted 2007. 8. 7. 03:01
Recently, I am trying to make it possible to write Chameleo plugins in Haskell. Chameleo is wirtten mostly in Python and there is no direct method to call Haskell functions in Python. However, Python is the most powerful glue language in the world(Okay, maybe next to Perl), it is not hard to use both Haskell and Python at the same time within an application. PythonVsHaskell shows an example which compiles the Haskell code with ghc and generates a dll file. Python ctypes can call these functions in the DLL as if they are normal C functions.

MissingPy seems to be another option for calling Python code from Haskell (but not calling Haskell code from Python). The recent version is 0.8.9, but it seems to be still in beta quality.

Parametric Polymorphism과 Dynamic Typing

Posted 2006. 10. 5. 23:18
동적 타이핑에 대한 여러 가지 오해가 있는데, 그 중 하나가 동적 타이핑을 써야만  Polymorphism이 간단해진다고 데 믿는 것이다. 일반적으로 많은 프로그래머들이 C++의 Template이나, Java/C#의 Geneircs을 쓰다가 파이썬/루비의 Parametric Polymorphism만  접해보고 나서 성급하게 내리는 결론이 아닐까 쉽다.

실제로 2 개의 인자를 받아서 큰 값을 리턴하는 max 함수를 예로 들어보자.

<파이썬의 예>
>>> def max(a, b):
...     if (a >= b): return a
...     else: return b
...
>>> max(3,4)
4
>>> max(3.5,4.2)
4.2000000000000002


<자바의 예>
public class Foo {
    public static <T extends Comparable > T max(T a, T b) {
        if (a.compareTo(b) >= 0) return a;
        else return b;
    }

    public static void main(String... args) {
        System.out.println(max(3,4));
        System.out.println(max(3.5,4.2));
    }
}


위의 예를 보면 똑같은 일을 하기 위해서 파이썬은 간단히 변수 a, b만 있으면 되는 반면에 자바는 복잡한 문법으로 타입 변수  T에 대한 정보를 줘야 한다. 여기까지만 살펴보면, 파이썬이 훨씬 좋아 보인다.

한 가지 차이가 있다면 파이썬은 동적 타입 검사를 수행하기 때문에 a, b의 인자가 실제로 비교할 수 있는 연산자(>)가 정의되어 있지 않더라도 실행이 된다(실제로 실행할 때 런타임 에러가 발생한다)는 것이고, 자바의 경우는 Comparable 인터페이스를 구현하지 않은 객체를 넘기면 바로 컴파일 에러가 뜬다는 점이다.

그럼에도 불구하고, 여기까지만 살펴보면, 파이썬과 같은 동적 타입 언어가 Parametric Polymorphism을 훨씬 손쉽게 사용할 수 있는 것처럼 보인다. 자바의 Generics는 한 번 보고 직관적으로 사용할 수 있는 수준은 아니기 때문이다. 파이썬 입문자가 엄청난 생산성 향상을 맞보고 동적 타입 언어야 말로 개발자를 위한 언어라고 생각하는 게 무리가 아니다.

그렇지만 정적 타입 언어는 반드시 C++의 Template, Java/C#의 Generics처럼 저렇게 복잡한 타입 정보를 일일이 적어줘야만 하는 운명에 놓인 것인가? 정답은 그렇지 않다. 강력한 정적 타입 언어로 유명한 ML의 예를 살펴보자.

- fun umax (x, y, gt) = if gt(x,y) then x else y;
- umax (2, 3, op >);
val it = 3 : int
- umax (2.3, 3.2, op >);
val it = 3.2 : real

코드만 놓고 보면 파이썬과 별로 차이가 없다. (op >를 직접 코드에 쓰지 않고 인자로 넘긴 이유는 ML에서 > 연산자가 오버로딩 되었다는 점과 ML의 기본 타인 추론 규칙 때문인데, 자세한 설명을 생략한다.) 어쨌거나 x, y 두 개의 인자를 넘기는데, 타입 정보는 따로 준 적이 없다.

혹시ML은 파이썬과 마찬가지로 동적 타입 언어인데, 우리가 잘못 알았던 것일까? 그렇진 않다. ML은 무척 강력한 정적 타입 체킹을 하는 언어이다. 다만 타입 추론(type inference)를 통해서 불필요한 타입 정보를 생략할 수 있게 만들었다. 위 정보를통해서 ML은 x, y 변수가 >= 연산자가 정의되어 있는 임의의 타입임을 안다.

그럼 이 ML 코드가 파이썬 코드랑 뭐가 다른 것일까? 하는 일이 똑같다면 정적 타입 언어와 동적 타입 언어의 구분이 무의미할 테니깐 말이다. 개발자가 실수로 서로 비교할 수 없는 타입인 정수와 문자열을 비교했다고 하자.

ML의 경우
- umax (2.3, "hello", op >);
stdIn:1.1-17.7 Error: operator and operand don't agree [tycon mismatch]
operator domain: real * real * (real * real -> bool)
operand: real * string * ('Z * 'Z -> bool)
in expression:
umax (2.3,"hello",>)

바로 타입 에러가 나는 반면의 파이썬의 경우 int를 문자열로 바꿔서 비교한 후에 아무렇게나 리턴해 버린다. 물론 파이썬의 경우도 정말 정의가 안 된 연산(예를 들어 정수 + 문자열)에 대해서는 런타임에 타입 오류를 내는데, ML은 컴파일 타임에 알 수 있는 버그를 파이썬은 해당 코드를 실행시켜 보아야만 알 수 있다.

요약하면, 동적 타이핑 언어를 사용하면 Polymorphism 구현이 간단해지는 것은 사실이지만, 이 역시 컴파일 타임에 오류를 알 수 없다는 동적 타이핑 특유의 단점을 그대로 가지고 있다. 반대로 정적 타입 언어는 약간의 타입 정보만을 주면 이런 오류를 컴파일 타임에 모두 잡아낼 수 있다. C++, Java/C#처럼 뒤 늦게 Polymorphism을 추가한 경우 사용의 불편함이 있지만, ML처럼 처음부터 언어에 Polymorphism을 녹여넣고 타입 추론을 적극 활용하면 불편함을 최소한으로 줄이는 것이 가능하다.

똑같은 버그를 두고 1) 실행도 해보기 전에 컴파일하면서 잡는 방식, 2) 일일이 테스트하고 QA를 거쳐서 잡는 방식이 있다면 어느 쪽을 선택해야 할지는 자명하다. 소프트웨어 결함의 제거 비용은 시간에 대한 x^2 이상의 함수임을 잊지 말자. 현대의 스크립트 언어는 여러 가지 훌륭한 점이 많지만, 그 훌륭함이 동적 타입 체킹을 하기 때문은 아닐 것이다.