갈루아의 반서재

하스켈은 정적 타입 시스템이다. 모든 표현식의 타입이 컴파일 당시에 이미 알려져있다. 예를 들어 불린 타입을 어떤 숫자로 나눌려고 하면, 컴파일되지 않는다. 프로그램이 충돌하기 전에 컴파일 당시에 이런 오류가 발견되는 것이 좋다. 하스켈의 모든 것은 타입을 가지며, 따라서 컴파일러는 컴파일하기 전에 프로그램에 대해 어느 정도 판단을 할 수 있다. 자바나 파스칼과는 다르게 하스켈은 타입추론(type inference)을 가진다. 만약 숫자를 쓰면, 굳이 하스켈에게 그것이 숫자라고 말할 필요가 없다는 것이다. 하스켈에 있어 타입 시스템을 이해하는 것은 매우 중요하다.

타입은 모든 표현식이 가지는 일종의 라벨이다. 표현식이 어떤 카테고리에 속하는지 말해주는 셈이다. 표현식이라는 것은 True 같은 불린이나 "hello" 같은 문자열을 말한다. 

일부 표현식의 타입에 대해 GHCI 를 이용해 살펴보자. :t  명령어를 통해 확인이 가능하다. 

  1. Prelude> :t 'a'
  2. 'a' :: Char
  3. Prelude> :t True
  4. True :: Bool
  5. Prelude> :"HELLO!"
  6. "HELLO!" :: [Char]
  7. Prelude> :(True, 'a')
  8. (True, 'a') :: (BoolChar)
  9. Prelude> :4 == 5
  10. 4 == 5 :: Bool

표현식에 앞에 :t 을 두고 실행하면, 표현식 :: 타입이 출력된다. 여기서 :: 는 "has type of" 로 읽으면 된다. 명시적인 타입은 항상 첫문자가 대문자로 표시된다. 

위의 예에서, 'a' 는 보다시피 Char 타입으로 표시되는데, 이것이 character를 뜻한다는 것을 알아차리는 것은 어렵지 않다. True 는 Bool 타입이다. 이것도 쉽다. 

그러면 "HELLO!" 의 경우 [Char]로 표시되는 것은 왜인가? 아시다시피 [] 는 리스트를 나타낸다. 그러므로 이는 문자의 리스트라고 보면 될 것이다. 

리스트와는 다르게 각각의 튜플은 각각의 타입을 가진다. 그러므로 (True, 'a') 의 경우 (Bool, Char) 타입을 가지고, ('a','b','c')의 경우 (Char, Char, Char) 타입을 가진다. 

마지막으로 4 == 5 는 항상 False 이므로 Bool 타입이다.

함수 역시 타입을 가진다. 함수를 작성할 때 명시적으로 타입 선언을 할지를 선택할 수 있다. 매우 짧은 함수를 제외하고는 일반적으로 선언을 하는 것이 유용하다. 

  1. Prelude> :{
  2. Prelude| removeNonUppercase :: [Char] -> [Char]
  3. Prelude| removeNonUppercase st = [ c | c <- st, c `elem['A'..'Z']]
  4. Prelude| :}
  5. Prelude> removeNonUppercase "SHKTJKddhkdkdkdkdk"
  6. "SHKTJK"

removeNonUppercase는 문자열을 받아 문자열을 반환하는 [Char] -> [Char]의 타입을 갖는다. [Char] 타입은 문자열과 동의어이므로 removeNonUppercase :: String -> String 와 같이 작성하는 편이 더 분명하다. 그리고 이 경우 컴파일러가 추론이 가능하므로 굳이 타입 선언을 하지 않아도 된다.  

그러면 인수가 여러 개인 경우에는 어떻게 작성하는지 살펴보자. 다음의 예를 보자.

  1. Prelude> :{
  2. Prelude| addThree :: Int -> Int -> Int -> Int
  3. Prelude| addThree x y z = x + y + z
  4. Prelude| :}
  5. Prelude> addThree 3737 383839 393939393939
  6. 393939781515
  7. Prelude>

인수는 -> 기호로 분리되며, 인수와 반환형 사이의 구별은 없다. 반환형은 함수 선언의 가장 마지막 아이템이고, 앞선 3개의 아이템은 이니수이다. 왜 -> 로 구분하는지에 대해서는 추후 살펴본다. 몇 가지 유용한 예를 더 살펴보자. 

Int 는 정수를 의미한다. 7 은 가능하지만 7.2 는 부적합하다. Int 는 상한, 하한이 있는데, 보통 32비트 컴퓨터의 경우 최대값은 2147483647 이고 최소값은 -2147483648 이다.

정수를 나타내는 또 다른 표현으로 er 이 있다. int 와의 주요한 차이는 er 의 경우에는 상한, 하한이 없어 정말로 큰 수를 다룰 수 있다는 것이다. 하지만 Int 가 효율면에서는 뛰어나다.

  1. Prelude> :{
  2. Prelude| factorial :: Integer -> Integer
  3. Prelude| factorial n = product [1..n]
  4. Prelude| :}
  5. Prelude> factorial 50
  6. 30414093201713378043612608166064768844377641568960512000000000000

Float 는 단일정밀도를 가진 부동소수점이다. 

  1. Prelude> :{
  2. Prelude| circumference :: Float -> Float
  3. Prelude| circumference r = 2*pi*r
  4. Prelude| :}
  5. Prelude> circumference 4.0
  6. 25.132742

Double 는 배정밀도를 가진 실수 부동소수점이다. 

  1. Prelude> :{
  2. Prelude| circumference' :: Double -> Double
  3. Prelude| circumference' r = 2*pi*r
  4. Prelude| :}
  5. Prelude> circumference' 4.0
  6. 25.132741228718345

Bool은 True 와 False 만을 가진 불린값이다. 

Char는 따옴표로 표시되는 문자를 말한다. 그리고 문자 리스트는 문자열이다. 

Tuples 은 구성요소의 타입과 길이에 따라 결정되는 형으로, 이론상으로는 무수히 많은 종류의 튜플이 있을 수 있다.