타입 시스템은 프로그램에 어떤 약속을 강제할까
정수와 문자열은 모두 메모리에 놓이지만 허용되는 연산과 해석 방식은 다르다. 타입 시스템은 값과 표현식을 분류하고, 어떤 연산과 조합을 허용할지 규칙으로 정하는 체계다. 숫자를 기대하는 함수에 파일 객체를 전달하는 것처럼 잘못된 사용을 발견하고, 함수가 무엇을 받아 무엇을 돌려주는지 설명하는 언어가 된다.
타입 시스템은 값과 연산의 허용 관계를 규칙으로 검사한다.
타입은 크기 이상의 정보다
타입을 단순히 메모리 몇 바이트인지로만 이해하면 함수 타입이나 제네릭을 설명하기 어렵다. bool -> int라는 타입은 불리언 인자를 받아 정수를 반환한다는 관계를 나타낸다. 리스트 타입은 원소에 어떤 규칙을 적용할 수 있는지 알려 준다. 사용자 식별자와 주문 식별자를 별도 타입으로 구분하면 물리적 표현이 같은 정수여도 실수로 섞는 일을 줄일 수 있다.
타입 규칙은 주어진 환경에서 표현식이 어떤 타입을 갖는지 판단한다. 환경에는 변수와 함수의 타입이 기록된다. 정수 덧셈이 정수 두 개를 요구한다면 양쪽 피연산자가 그 조건을 만족하는지 확인하고 결과 타입을 정한다. 조건문에서는 조건이 불리언인지, 두 갈래 결과를 어떻게 합칠지 등의 규칙이 필요하다. 언어마다 같은 모양의 코드에 다른 규칙을 적용할 수 있다.
적지 않은 타입도 추론할 수 있다
fun x -> if x then 1 else 0이라는 함수의 타입을 생각해 보자. 조건 위치에 있는 x는 불리언이어야 하고 두 결과는 정수다. 따라서 함수는 bool -> int로 추론할 수 있다. 프로그래머가 모든 위치에 타입을 직접 적지 않아도 정적 타입 검사가 가능하다. Cornell의 타입 추론 자료는 타입 사이의 제약을 만들고 통합해 이런 관계를 찾는 방법을 설명한다.
이 과정에서 x를 동시에 문자열로만 허용되는 연산에 사용하면 제약이 충돌할 수 있다. 검사기는 프로그램을 실제 입력으로 실행해 보는 대신 가능한 사용 조건을 비교한다. 타입 변수와 다형성을 지원하면 특정 타입 하나로 고정하지 않고 여러 타입에 공통으로 성립하는 관계를 표현할 수도 있다. 추론의 범위와 필요한 주석은 타입 시스템의 복잡도에 따라 달라진다.
안전성을 말할 때 필요한 조건
타입 시스템의 건전성은 정해진 언어 모델과 규칙 아래에서 타입 검사에 통과한 프로그램이 특정한 타입 오류에 빠지지 않도록 한다는 성질이다. 어떤 오류를 포함하는지, 외부 코드와 강제 형 변환을 어떻게 다루는지가 전제다. 타입 검사를 통과하면 프로그램이 절대로 충돌하지 않거나 원하는 답을 낸다는 뜻은 아니다. 무한 반복, 잘못된 계산식, 네트워크 장애는 별도의 문제다.
검사기는 안전한 프로그램을 일부 거절할 수도 있다. 두 조건의 복잡한 관계를 사람이 알 수 있어도 검사 규칙이 그 증명을 표현하지 못하면 허용하지 않는 것이다. 반대로 Any 같은 탈출구나 검사되지 않는 외부 경계는 보장을 약하게 만들 수 있다. 개발자는 표현력, 자동 분석 비용, 오류 발견 범위 사이의 선택을 이해해야 한다.
실무에서 타입은 문서 역할과 변경 검증 역할도 한다. 함수 반환값의 구조를 바꾸면 영향을 받는 사용 지점을 자동으로 찾는 데 도움이 된다. 하지만 타입이 허용하는 정수라 해도 음수 가격이나 범위를 벗어난 날짜가 올바른 데이터는 아니다. 타입으로 표현할 수 있는 불변 조건은 적극적으로 담고, 나머지 입력 검증과 테스트를 함께 설계할 때 약속의 범위가 명확해진다.