ML 공부를 위한 참고 자료

Posted 2006. 10. 7. 20:13
ML 참고 자료입니다. 이 글을 앞으로 계속 갱신할 예정입니다.

ML 참고 문서 Version 0.1 (2006-10-07)

[1] Programming in Standard ML,Robert Harper Carnegie Mellon University Spring Semester, 2005
http://www.cs.cmu.edu/~rwh/smlbook/

카네기 멜론 대학(CMU)에서 나온 SML 입문서입니다. CMU 학부 학생들이 SML을 처음 공부할 때 보는 책이라고 나와 있습니다. 총 293 페이지로 SML의 기본적인 내용을 아주 자세히 설명하고 있어서 함수형 언어를 처음 배우는 (혹은 프로그래밍을 처음 접하는) 입문자에게 좋은 책입니다.

Pascal에는 subrange type이라는 게 있다. subrange 말이 내포하듯이 정수 타입의 변수에 범위를 주는 것인데, 다음과 같은 형태로 사용할 수 있다.

type Atype = 0..20;
     Btype = 10..20;

var a : Atype;
    b : Btype;

이 경우 변수 a는 0에서 20까지의 값을, 변수 b는 10-20까지의 값만 가질 수 있는 타입이다. 정수의 일부 범위만 사용할 수 있으므로 subrange 타입이라 불린다. Pascal의 type rule은 무척 간단한 편인데, subrange 타입이 들어가면 약간 복잡해진다.

예를 들어 a + b의 타입은 무엇일까? 직관적으로 생각해보면 a + b가 가능한 값은 10에서 40이니깐 10..40 sunbrange 타입이 되어야 할 것이다. 즉 a + b는 a의 타입도 b의 타입도 아닌 새로운 타입이 나오게 된다. 하지만 Pascal에서는 이런 type inference가 복잡하니깐 subrange 타입의 연산은 subrange type의 base type인 integer라고 하고 이 문제를 해결해 버렸다.

만약 a + b의 계산 결과를 새로운 subrange type의 변수 c에 대입(assign) 한다면 컴파일러는 어떻게 해야 할까? 이 경우 보통 dynamic check가 필요하다. 계산 결과가 subrnage type의 범위 내에 들어간다고 보장할 수 없다면 런타임에 검사를 해줘야 하기 때문이다. 물론 위의 예에서 우리가 a + b가 10에서 40을 벗어나지 않는다고 계산했던 것처럼 같은 정보를 컴파일러가 계산해 낸다면, 일부 dynamic check를 성능 향상을 위해 제거할 수 있을 것이다.

이렇게 계산한 값과 c의 subrange를 비교해서 만약 c의 값이 반드시 c의 subrange에 들어가면 dynamic check를 안전하게 제거할 수 있다. 반대로 계산한 값이 항상 c의 subrange를 벗어난다면, dynamic check를 넣는 대신에 컴파일 에러를 발생시켜야 할 것이다.

하지만 항상 이런 식으로 계산 결과의 범위를 예측할 수 있지는 않다. 다음 예제를 살펴보자.


a : integer range 0..20;
b : integer range 10..20;

function foo(i : integer) return integer is ...
...

a := b - foo(10);

이 경우에는 foo(10)이 외부 함수이므로, 이 코드만 봐서는 foo(10)의 범위가 어떻게 될지 예측하기가 어렵다. 결과적으로 b -  foo(10)이 0..20 사이에 포함될 것인지를 결정할 수 없게 된다. 따라서 컴파일러는 실제로는 필요가 없을 수도 있는 dynamic check를 넣어줘야만 한다. 만약 intraprocedural analysis를 통해서 foo(10)의 range를 계산할 수 있었다면 이런 불필요한 dynamic check를 없앨 수도 있었을 것이다.

이 케이스의 교훈은 type inference를 열심히 할수록 불필요한 런타임 오버헤드를 줄일 수 있다는 것이다. 여기서는 subrange type을 예로 들었지만, statically typed language에서도 type inference의 부재 혹은 한계로 인해 불필요한 dynamic check가 들어가 있는 경우가 많이 있다. Java의 배열 경계 검사(array bound checking)도 subrange type과 동일하다. 물론 정확한 계산 값을 얻는 것이 외부적인 요인, 혹은 계산의 복잡성으로 인해 원천적으로 불가능한 경우가 많이 있지만, type inference의 발전은 프로그램의 성능 향상을 가져올 것이다.


참고 문서
1. Programming Languages Pragmatics Michael L Scott p342-343,7.2.4 Type Inference

Python 2.5 With Statement

Posted 2006. 10. 6. 20:18
어제 밤에 중국 마이크로소프트 리서치에 있는 후배와 엠에센으로 놀다가 새로 나온 파이썬 2.5(What's New in Python 2.5)의 특징에 대한 이야기를 하였다. 각자 주요하게 봤던 내용 중에 하나씩 이야기 했는데 나는 ctypes 패키지를 꼽았고, 후배는 with 문을 꼽았다. 이 글에서는 PEP 343에서 논의를 걸쳐 파이썬 2.5에 포함된 'with' 문을 살펴보려 한다.

with 문은 자원 관리를 용이하게 하기 위한 syntactic sugar로 다음과 같은 형태를 가진다.

with open('/etc/passwd', 'r') as f:
    for line in f:
        print line
        ... more processing code ...

위 예제는 /etc/passwd 파일을 읽기 모드(r)로 연 후에 f라는 파일 디스크립토로 처리한다. with 블록이 끝나면 자동으로 파일을 닫아준다.

lock = threading.Lock()
with lock:
    # Critical section of code

마찬가지로 위 예제는 Lock을 잡고 Critical Section을 처리한 후에 with 블록을 벗어나면 자동으로 락을 해제해준다. 즉 일반적으로 try/finally 구문에서 처리하던 것을 with 블록을 통해 좀 더 편리하게 하자는 것이다.

구현 원리는 생각보다 간단하다. with에 사용되는 객체는 파이썬 용어에 따르면 컨텍스트 매니저(Context Manager)를 구현해야 하는데, __enter__와 __exit__ 메쏘드를 가지면 된다. 컨텍스트 매니저 객체를 with 문에 사용해주면 with 블록에 들어갈 때 __enter__가 불리고, 빠져나올 때 __exit__가 불린다. finally와 마찬가지로 예외(exception)가 발생해도 __exit__가 불리는 것이 보장된다. 컨텍스트 매니저 구현의 편의를 위해서 contextlib 모듈도 추가 되었다.

사실 with 문은 파이썬이 처음으로 도입한 것은 아니고, C#에서는 using이라는 construct로 이미 사용되고 있었다. 내가 알기로 C#도 처음은 아닌 것 같고, 다른 고전적인 언어에서 이미 사용이 되어왔던 걸로 안다. (어떤 언어인지 확실히 기억은 안 나지만...)

try/finally 구문의 불편함을 생각해 봤을 때 편리한 기능인 것만은 분명한 것 같다.


PS: ETH Zürich에서 만든 "The Oberon Programming Language"도 With 문을 제공하는데, 파이썬과 의미가 다르다. Oberon2 Language Report를 읽어보면, With문은 일종의 Type Guard 역할을 한다.

If v is a variable parameter of record type or a pointer type, and if it is of static type T0, the statement

WITH v:T1 DO s1 | v:T2 DO s2 ELSE s3 END

has the following meaning: if the dynamic type of v is T1, then the statement sequence S1 is executed where v is regarded as if it had the static type T1; else if the dynamic type of v is T2, then S2 is executed where V is regarded as if it had the static type T2; else S3 is executed. T1 and T2 must be extensions of T0. If no type test is satisfied and if an else clause is missing the program is aborted.

Example
  WITH t:CenterTree DO i := t.width; c := t.subnode END
« PREV : 1 : ··· : 68 : 69 : 70 : 71 : 72 : 73 : 74 : ··· : 82 : NEXT »