하스켈 타입 클래스(type class)는 오버로딩을 지원하기 위한 방법입니다. 여기서 오버로딩은 하나의 심볼(함수 혹은 연산자)를 타입에 따라 여러 의미로 사용하는 것을 의미합니다.대표적인 예로 하스켈의 Show
타입 클래스를 살펴보겠습니다. Show
타입 클래스는 show
라는 함수를 정의하고 있습니다. show
함수는 인자를 받아 그 인자를 String
으로 변환한 결과를 리턴합니다. 아래와 같이 다양한 인자 타입에 대해서 잘 동작하는 것을 확인할 수 있습니다.
> show 1
"1"
> show False
"False"
> show "Hello World"
"\"Hello World\""
Int
를 String
으로 변환하는 방법과 Bool
을 String
으로 변환하는 방법은 다를 수 밖에 없는데, show
는 어떻게 Int
나 Bool
, String
등 여러 타입의 인자를 모두 변환할 수 있는 것일까요? 궁금즘을 해결하기 위해 show
의 타입을 살펴보겠습니다.
> :t show
show :: Show a => a -> String
타입을 보면 일반적인 함수와 달리 a -> String
함수 타입 왼쪽에 Show a =>
라는 컨텍스트(context)가 추가되어 있는 것을 볼 수 있습니다. 여기서 =>
앞에 나오는 Show a
는 a
라는 타입이 Show
라는 타입 클래스를 구현한 타입이어야 한다는 뜻입니다.
Show
타입 클래스를 구현한다는 것이 어떤 의미인지를 알아보기 위해, Show
타입 클래스가 어떻게 정의되었는지 먼저 살펴보겠습니다. 다음은 Prelude
모듈에 정의된 Show
의 정의입니다. (간단한 설명을 위해 showsPrec
등 다른 함수들은 생략했습니다.)
class Show a where
show :: a -> String
위 정의의 의미는 타입 a
가 Show
라는 타입 클래스의 인스턴스가 되기 위해서는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
ghci
로 merge
함수의 타입을 확인해보면 아래와 같이 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
하스켈의 Prelude
는 Show
외에도 Eq
, Ord
, Read
, Enum
, Bounded
, Num
, Integral
,Floating
등 다양한 타입 클래스를 정의하고 있습니다. 이에 대한 설명은 다음 글에서 이어가도록 하겠습니다.
Pingback: 타입 클래스 기초 – 서광열의 하스켈 스쿨