갈루아의 반서재

Type classes

하스켈 타입의 예를 들어보면, Bool, Char, 그리고 Integer 등이 있다. 그리고 하스켈은 type classes 도 가지고 있다. 타입 클래스는 해당 타입이 그 타입 클래스의 멤버이기 위해 그 타입이 갖추어야하는 작동을 특정한다. 

Num 은 Prelude 에서 정의된 많은 타입 클래스 중의 하나이다. :info Num 을 통해 살펴보면, 타입이 Num 이 되기 위해서는 덧셈, 뺄셈, 곱셈, 그리고 4가지 기능(negate, abs, signNum, fromInteger)을 지원해야 한다.  그리고 Prelude 는 Num 타입 클래스의 4가지 인스턴스를 정의하고 있는데, Int(word-size), Integer (unlimited size), Float 그리고 Double 이 그것이다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Prelude> :info Num
class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
  (*) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
        -- Defined in ‘GHC.Num’
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
Prelude>
 
cs

아래에서  negate 의 타입을 살펴보자.

1
2
3
Prelude> :type negate
negate :: Num a => a -> a
 
cs

negate 의 타입은 type variable 인 a 를 통해 특정되어 있다. a -> a 라는 부분이 말하는 것은 negate 는 인수로 전달받은 것과 같은 타입을 가진 값을 반환한다는 것이다. 즉, Int 를 주면 나도 Int 를 돌려주겠다는 식이다. 

그리고 Num a => 라는 부분은 class constraint 이다. 타입 a 는 타입 클래스 Num 의 인스턴스여야함을 특정하는 것이다. 

negate는 타입이 Num의 인스턴스인 어떤 값이라도 받아들인다. 그리고 같은 타입의 값을 반환한다. 

integer literals 은 어떤 타입을 갖는지 살펴보자.

1
2
3
4
5
Prelude> :type 3
3 :: Num a => a
 
Prelude> :type (-27)
(-27) :: Num a => a
cs

Num class constraint 를 갖는 Literals 의 경우, Num a => a 를 받아들이는 모든 함수에 의해 사용가능하다. 그럼 negate 3.4 은 유효하게 작동하는가?

1
2
3
4
5
6
7
8
9
Prelude> :type negate
negate :: Num a => a -> a
 
Prelude> :type 3.4
3.4 :: Fractional a => a
 
Prelude> negate 3.4
-3.4
it :: Fractional a => a
cs


하스켈의 타입 클래스는 계층구조를 지닌다. Prelude 의 경우 다음과 같은 계층구조를 가진다.

Base-classes


Num

Int, Integer, Float, Double

→ 

 Fractional

Float, Double

위의 이미지에 나와있는 관계 중 하나인 Num → Fractional 을 보자. 여기서 Num 에서 Fractional 으로 가는 화살표가 의미하는 것은 Fractional 이 Num 으로 사용가능하다는 이야기이다. 따라서 negate :: Num a => a -> a 와 5.0 :: Fractional a => a 이라면, negate 5.0 역시 유효하다는 이야기이다. 

negate :: Num a => a -> a 는 아래와 같이 여러 함수를 나타낸다. 

negate :: Integer -> Integer

negate :: Int -> Int

negate :: Float -> Float

negate :: Double -> Double

negate 는 다형 함수로, 다양한 형태의 값을 다룰 수 있다. 만약 함수의 타입이 type variables 을 가지고 있다면, 그 함수는 다형 함수라고 할 수 있다.