Static Type Checking

2004년 12월에 Bruce Eckel의 Strong Typing vs. Strong Testing을 읽고 다음과 같은 글을 쓴 적이 있다.

컴파일 타임에 최대한 많은 에러 체크를 해주는 Strong Typing이 프로그래머의 생산성 향상에 큰 도움을 준다고 많은프로그래머들이 믿는다. Eckel은 이런 관점에 의문을 던지며 Weak Typing 언어라도 Unit Testing을 강화하면프로그래머의 생산성 향상에 도움을 주며 크고 복잡한 소프트웨어를 충분히 설계할 수 있다고 역설한다.

StrongTyping 언어의 컴파일 타임 에러 체크라는 것도 결국 소프트웨어 테스트의 일부분일 뿐이고 이것만으로 프로그램의 정확성을보장할 수 없다. 또 Weak Typing 언어는 컴파일 타임의 에러 체크를 하지 않는 대신 런타임 에러 체크가 강화 되었다.결국 Strong이든 Weak이든 프로그램의 정확성을 보장하기 위해서는 많은 Unit Test Case를 만들어야하고, WeakTyping 언어가 오히려 빨리 정확한 Unit Test Case를 만들기 유리할 수도 있단 거다.

일례로 파이썬과같이 빠른 속도로 프로토타이핑 할 수 있는 언어의 경우 개발 시간을 단축하고 단축된 시간을 이용해 더 많은 Unit TestCase를 짜면 컴파일 타임 에러 체크가 없다는 단점에도 불구하고 프로그램의 기능을 좀 더 명확히 검증할 수 있다는 것이다.

일단 Eckel도 지적했듯이 제목을 “Static Checking vs Strong Testing”으로 바로 잡아야 한다. 컴파일 타임에 타입 검사를 하느냐 아니냐가 기준이 되기 때문이다.

그 당시에는 Eckel의 말이 그럴듯한 논리라고 생각했으나 2년의 시간이 지나고 나서 내 생각에도 많은 변화가 있었다. 특히 과제 연구로 프로그램 분석(program analysis) 쪽을 공부하면서 Eckel의 이런 주장에 대해 프로그래밍 언어 학계는 냉담하다는 사실을 알았다.

프로그래밍 언어 연구는 거의 대부분 좀 더 정교한 타입 시스템을 도입해서 프로그램을 실행해 보기 전에 여러가지 특성(property)을 보장하는데 목표를 두고 있다. 일례로 Race Free Java의 경우 타입 시스템으로 레이스 컨디션(race condition)이 없음을 컴파일 타임에 보장한다.

이런 학계의 경향과는 정반대로 일부 스크립트 언어의 성공으로 상당수의 개발자는 정적 타입 검사가 없는 언어가 생산성이 좋기 때문에 절약한 시간으로 더 많이 테스트할 수 있고 정적 타입 검사하는 프로그래밍 언어만큼 안정적일 수 있다고 생각하고 있다. 실제로 Eckel이 정적 타입 검사 없이도 성공적으로 크고 복잡한 프로그램을 만들 수 있다는 증거로 든 것이 파이썬의 성공이다.

하지만 이런 주장에는 상당한 무리가 있어 보인다. 정적 타입 검사가 모든 가능성을 점검하는 연역적인 검사인데 비해서, 테스트는 귀납적인 접근 방식이기 때문이다. 모든 가능한 타입에 대해서 테스트 케이스를 작성한다는 것은 비효율적이고, 컴포넌트 통합(integration)에 문제를 일으킬 여지가 많다.

또한 정적 타입 검사가 없으면 런타임에 상당한 오버헤드가 생긴다.예를 들어 파이썬과 동적 타입 체크 언어(사실상 untyped 언어)는 메쏘드를 콜 할 때마다 해당 메쏘드가 해당 오브젝트에 정의되어있는지를 확인하고 아니면 런타임 에러를 발생시켜야한다. C/C++의 경우 바이너리를 링크할 때 이미 알 수 있었을 문제이다. 이런 차이는 수행 속도에 큰 영향을 미친다.

파이썬의 성공은 정적 타입 검사의 부재 때문에 가능했던 것이 아니라 정적 타입 검사의 부재에도 불구하고, 다른 여러가지 요소가 복합되어서 이루어진 것이라고 결론을 내는 것이 맞는 것 같다.

HTTP Content-Type Header

웹서핑을 하다보면 종종 한글이 깨진채로 나오는 경우가 있다. 이때 수동으로 인코딩을 UTF-8이나 한국어(EUC-KR) 등으로 바꾸면 페이지가 올바르게 표시되기도 한다.

이 문제는 HTTP 서버 보내는 HTTP Content-Type Header와 관련이 있다.

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 7188
Date: Mon, 18 Feb 2002 22:06:32 GMT
Last-Modified: Fri, 10 Aug 2001 17:32:25 GMT

여기서 Content-Type에 명시적으로 문자셋(charset)을 줄 경우 브라우저는 해당 문자셋으로 페이지를 보여주면 된다. 하지만 다음과 같이 문자셋이 생략되어 오는 경우가 종종 있다.

HTTP/1.1 200 OK
Content-type: text/html

HTTP 규격(specification)에 따르면 문자셋이 생략되었을 경우 디폴트 인코딩은 ISO-8859-1 (Latin1)이 되어야 한다. 하지만 대부분의 웹브라우저는 이 표준을 따르기보다는 각자의 방법으로 해당 페이지가 어떤 인코딩을 사용하고 있는지 추측하는 방식을 사용한다.

한  예로 대표적인 오픈 소스 브라우저인 파이어폭스(Firefox)의 경우 내부적으로 인코딩 디텍션 모듈을 가지고 있는데, [A composite approach to language/encoding detection]란 글에 보면 이 알고리즘을 자세히 설명하고 있다.  소스 코드 mozilla/intl/chardet 디렉토리에서 그 구현을 볼 수 있다(저 글이 작성된 후에 구현이 조금 바뀌었다고 한다).

이 모듈은 원래 독립적으로 있다가 모질라 소스 트리에 통합되었는데, 브라우저 뿐만 아니라 웹 어플리케이션에서도 유용하게 쓰이기 때문에 자바(jchardet)와 파이썬으로도 포팅이 되어있다.

Null Object

자바로 알고리즘을 작성하다보면 정말 귀찮은 일 중에 하나가 널 검사(null check)이다. 일반적으로 간단한 연결 리스트(linked list)를 traverse하는 프로그램을 짠다고 해도 매번 널인지 검사하는 코드가 들어가야 한다.

Null Object는 널 검사를 줄이는 패턴이다.  예를 들어 x를 연결 리스트에서 찾는 알고리즘이라면 nullObject의 값을 x로 설정하고, 다음과 같이 루프를 돌면 특별히 널을 따로 검사할 필요가 없다.

head -> Node -> Node -> Node -> NullObject (Node)

Node n = head
nullObject.value = x

while (true) {
    if (n.value == x) return n != nullObject
    n = n.next
}

널 검사 오버헤드만 없애도 20-30%의 속도 향상이 있다고 하는데, 다른 곳에서도 응용할 수 있을 것이다.