타입 클래스 기초

하스켈 타입 클래스(type class)오버로딩을 지원하기 위한 방법입니다. 여기서 오버로딩은 하나의 심볼(함수 혹은 연산자)를 타입에 따라 여러 의미로 사용하는 것을 의미합니다.대표적인 예로 하스켈의 Show 타입 클래스를 살펴보겠습니다. Show 타입 클래스는 show라는 함수를 정의하고 있습니다. show 함수는 인자를 받아 그 인자를 String으로 변환한 결과를 리턴합니다. 아래와 같이 다양한 인자 타입에 대해서 잘 동작하는 것을 확인할 수 있습니다.

> show 1
"1"
> show False
"False"
> show "Hello World"
"\"Hello World\""

IntString으로 변환하는 방법과 BoolString으로 변환하는 방법은 다를 수 밖에 없는데, show는 어떻게 IntBool, String등 여러 타입의 인자를 모두 변환할 수 있는 것일까요? 궁금즘을 해결하기 위해 show의 타입을 살펴보겠습니다.

> :t show
show :: Show a => a -> String

타입을 보면 일반적인 함수와 달리 a -> String 함수 타입 왼쪽에 Show a =>라는 컨텍스트(context)가 추가되어 있는 것을 볼 수 있습니다. 여기서 => 앞에 나오는 Show aa라는 타입이 Show라는 타입 클래스를 구현한 타입이어야 한다는 뜻입니다.

Show 타입 클래스를 구현한다는 것이 어떤 의미인지를 알아보기 위해, Show 타입 클래스가 어떻게 정의되었는지 먼저 살펴보겠습니다. 다음은 Prelude 모듈에 정의된 Show의 정의입니다. (간단한 설명을 위해 showsPrec등 다른 함수들은 생략했습니다.)

class  Show a  where
   show      :: a   -> String

위 정의의 의미는 타입 aShow라는 타입 클래스의 인스턴스가 되기 위해서는a -> String이라는 타입을 가진 show 함수를 제공해야 한다는 뜻입니다. 여기서 Show타입 클래스는 Show 타입 클래스를 구현하는 타입들이 갖춰야 하는 일종의 인터페이스라고 이해할 수 있습니다.

우리가 Int, Bool, String 등의 인자에 대해 show 함수를 호출할 수 있는 이유는 각각의 타입에 대해 Show 타입 클래스 인스턴스가 미리 정의되어 있기 때문입니다. 인스턴스 정의는 다음과 같이 instance 키워드를 사용합니다.

instance Show Int where
    show = ...

instance Show Bool where
    show = ...

instance Show String where
    show = ...

이처럼 show라는 함수는 실제로는 각 타입별로 구현이 다르지만, 우리는 타입별로 구분할 필요 없이 show라는 하나의 심볼만 사용하면 하스켈이 알아서 인자 타입에 맞는 실제show 함수 구현을 찾아서 불러주는 것입니다.

앞서 설명한 것처럼 이렇게 하나의 심볼을 타입에 따라 서로 다른 의미로 사용하는 방법을오버로딩이라고 부르고, 하스켈의 타입 클래스는 오버로딩을 구현하기 위한 방법입니다.

이해를 돕기 위해 직접 타입 클래스를 하나 만들어 보겠습니다. 아래에 정의한 Mergeable타입 클래스는 두 개의 인자를 받아서 하나로 합치는 함수 merge를 정의하고 있습니다.

class Mergeable a where
  merge :: a -> a -> a

ghcimerge 함수의 타입을 확인해보면 아래와 같이 Mergeable a라는 컨텍스트가 함수 타입 왼쪽에 표시되는 것을 볼 수 있습니다.

> :t merge
merge :: Mergeable a => a -> a -> a

이제 merge 함수를 Int 타입과 List 타입에 대해 정의해 보겠습니다.

instance Mergeable Int where
  merge x y = x + y

instance Mergeable [a] where
  merge x y = x ++ y

Int 타입의 merge 함수는 두 인자의 합을 계산하도록 했고, List 타입의 merge 함수는 두 인자를 병합하도록 하였습니다. ghci를 이용해 테스트해보면 아래와 같이 인자 타입에 따라 서로 다른 merge 함수가 호출되는 것을 확인할 수 있습니다.

> merge (1 :: Int) (2 :: Int)
3
> merge [1,2,3] [4,5,6]
[1,2,3,4,5,6]

추가로 Bool 같이 우리가 인스턴스를 정의하지 않은 타입에 대해서는 에러가 나는 것도 볼 수 있습니다.

> merge False True

<interactive>:23:1:
    No instance for (Mergeable Bool) arising from a use of ‘merge’
    In the expression: merge False True
    In an equation for ‘it’: it = merge False True

하스켈의 PreludeShow 외에도 Eq, Ord, Read, Enum, Bounded, Num, Integral,Floating 등 다양한 타입 클래스를 정의하고 있습니다. 이에 대한 설명은 다음 글에서 이어가도록 하겠습니다.

One thought on “타입 클래스 기초

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s