[하스켈] GADT(Generalized Algebraic Data Type) 사용하기 (2)

redneval의 이미지

이전 글보기

[하스켈] GADT(Generalized Algebraic Data Type) 사용하기 (1)



5. GADT 와 ADT 의 차이점 정리

(1) 사실은 ...

GADT 는 ADT 보다 확장된 자료형입니다.

그러므로 ADT 에서 가능한 것은 GADT 에서 모두 가능합니다.

예제 1은 사실 ADT 와 GADT 둘 다 가능한 예제입니다.

instance Show SKI where
    show (S) = "S"
    show (K) = "K"
    show (I) = "I"
    (:@) :: SKI -> SKI -> SKI

부분을 data SKI = S | K | I | SKI :@ SKI 로 바꾸면 완벽히 똑같이 작동됩니다.

그리고 앞에서 컴파일 오류가 발생했던 다음의 코드도

-- ADT 문법 (컴파일 오류 발생)
data ABC x = A a | B (a -> b)

다음과 같이 forall 을 이용하여 다음과 같이 바꾸면 제대로 동작합니다.

data ABC x = forall a. A a | forall a b. B (a -> b)

여기까지의 설명은 ADT 와 GADT 의 문법상의 차이점에 대해서 알아본 것이었습니다.

그러면 이제 본격적으로 GADT 만의 기능에 대해 알아봅시다.

(2) GADT 와 ADT 의 결정적인 차이점

GADT 와 ADT 의 차이점을 한마디로 말하면 다음과 같습니다.

GADT 는, 생성자의 반환형이 여러가지일 수 있다.

그렇기 때문에 GADT 의 형 내부에 또 다른 형 관련정보를 넣을 수 있습니다.

예를들어, 다음과 같이 내부의 자료가 Int 형인지 Char 형인지에 따라,

생성자의 반환형이 각각 T Int 와 T Char 이 되게 만들면,

-- t.hs 로 저장함.
module T where
data T x where
    IntT :: Int -> T Int
    CharT :: Char -> T Char

Int 인지 아니면 Char 인지에 대한 정보가 x 에 들어있으므로,

이를 이용하면 반환형이 여러가지인 함수를 정의할 수 있습니다.

eval :: T x -> x
eval e = case e of
    IntT i -> i
    CharT c -> c

Prelude> :l t.hs
[1 of 1] Compiling T                ( t.hs, interpreted )
Ok, modules loaded: T.
*T> eval (IntT 1)
1
*T> eval (CharT 'a')
'a'

그리고 GADT 의 기능을 사용한 경우,

하스켈의 형 유추(type inference)가 잘 이뤄지지 않기 때문에, 함수 선언은 반드시 해줘야합니다.



6. 예제 2 : 리스트? 튜플?

(1) 리스트와 튜플

리스트와 튜플에 대해서는 잘 아실겁니다. 간단하게 둘을 비교해보면 다음과 같습니다.

튜플에는, `다양한 형을 가진 자료'가 `고정된 수'만큼 있습니다.

리스트에는, `동일한 형을 가진 자료'가 `임의의 수'만큼 있습니다.

리스트에는 `동일한 형을 가진 자료'만 올 수 있다는 점은 (:) 생성자의 형을 보면 더 명확해집니다.

(이제서야 밝히지만 생성자도 함수의 한 종류이고, (알파벳이나 _ 가 아닌) 특수문자만으로 이름붙은 함수를 연산자라고 부릅니다.

그러므로 (:) 는 생성자이자 함수이고 연산자입니다. 물론, 보통은 연산자라고 부릅니다.

참조 : http://www.haskell.org/tutorial/functions.html , http://www.haskell.org/haskellwiki/Infix_operator)

Prelude> :t (:)
(:) :: forall a. a -> [a] -> [a]

리스트를 흉내내서 List 라는 자료형을 정의해봅시다.

(하스켈에서 리스트는 원래 모나드이지만, 본 문서에서 그건 신경쓰지 않겠습니다.)

-- ADT 문법
data List a =
    Nil
   | a :@ List a

-- GADT 문법
data List a where
    Nil :: List a
    (:@) :: a -> List a

*Main> :t (:@)
(:@) :: forall a. a -> List a

그러면 다음과 같이 (:@) 생성자의 형을 바꾸면 어떨까요?

(:@) :: forall a. a -> [ b ] -> [ a -> b ]

(2) 일반화된 리스트 (Generalized List)

리스트를 확장하여 GL 이라는 자료형을 만들어봅시다.

GL 는 `다양한 형을 가진 자료'가 `임의의 수'만큼 있는 자료형입니다.

-- list.hs 로 저장합니다.
module GL where
data GL x where
    Nil :: GL ()
    (:@) :: a -> GL b -> GL (a -> b)
infixr 5 :@

잘 정의됐나 확인해봅시다.

Prelude> :l list.hs
[1 of 1] Compiling GL               ( list.hs, interpreted )
Ok, modules loaded: GL.
*GL> :t (1 :@ 'a' :@ "string" :@ Nil)
(1 :@ 'a' :@ "string" :@ Nil) :: forall t. (Num t) => GL (t -> Char -> [Char] -> ())

head, tail 과 비슷한 일을 하는 함수도 만들어 봅시다.

headGL :: GL (a -> b) -> a
headGL (x :@ y) = x
 
tailGL :: GL (a -> b) -> GL b
tailGL (x :@ y) = y

실행결과는 다음과 같습니다.

*GL> headGL (1 :@ 'a' :@ "string" :@ Nil)
1
*GL> :t tailGL (1 :@ 'a' :@ "string" :@ Nil)
tailGL (1 :@ 'a' :@ "string" :@ Nil) :: GL (Char -> [Char] -> ())

하지만 일반화되었다고 무조건 좋은 것만은 아닙니다.

만약, 리스트를 GL 자료형으로 바꾸는 함수를 구현해야한다고 생각해봅시다.

다음과 같이 하면 될까요?

convertGL :: [a] -> GL t
convertGL (x :@ xs) = x :@ (convertGL xs)

불행히도 컴파일 오류를 내버립니다.

GL 의 정의와 충돌을 일으키기 때문이데, 다음과 같이 GL 의 정의를 바꾸면,

-- list2.hs 로 저장합니다.
module GL where
data GL where
    Nil :: GL
    (:@) :: a -> GL -> GL
infixr 5 :@
 
convert :: [a] -> GL
convert [] = Nil
convert (x:xs) = x :@ (convert xs)

다음과 같이 잘 되는 것 같이 보이지만,

Prelude> :l list2.hs
[1 of 1] Compiling GL               ( list2.hs, interpreted )
Ok, modules loaded: GL.
*GL> :t convert [1,2,3]
convert [1,2,3] :: GL
*GL> :t 'a' :@ (convert [1,2,3]) 
'a' :@ (convert [1,2,3]) :: GL

이번에는 headGL 함수에서 오류가 발생합니다.

-- 컴파일 오류 발생
headGL :: GL -> a
headGL (x :@ y) = x

(3) GADT 의 이용

앞에서 언급했듯이, GADT 의 형 내부에 또 다른 형 관련정보를 넣을 수 있습니다.

그리고 이러한 형 관련정보는, 하스켈의 형 시스템에 의해서, 자료와 함수의 형을 확인하기 위해서 활용됩니다.

그렇기 때문에 GADT 를 이용하면, 오히려 함수를 정의하는데 걸림돌로 작용합니다.

리스트에 다양한 자료형을 넣고 싶다면, 다음과 같이 사용하는 편이 더 낫습니다.

module T where
data T =
    BoolT  Bool
    | IntT  Int
    | CharT  Char
 
instance Show T where
    show (BoolT x) = show x
    show (IntT x) = show x
    show (CharT x) = show x
 
int2t :: [Int] -> [T]
int2t [] = []
int2t (x:xs) = (IntT x) : (int2t xs)
 
grepInt :: [T] -> [Int]
grepInt [] = []
grepInt (x:xs) = case x of
    IntT x' -> x' : (grepInt xs)
    _ -> grepInt xs



7. 정리

GADT 는, 생성자의 반환형이 여러가지일 수 있습니다.

이러한 특성과 함께 함수의 다형성(Polymorphism)을 같이 이용하면, 여러가지 반환형을 갖는 함수를 만들어낼 수 있습니다.

이러한 반환형은, 하스켈 컴파일러에 의해서, 함수나 각종 연산의 형을 확인하는데 사용됩니다.

그러므로 하스켈에서 GADT 는, 하스켈 언어가 `강형(strong typing)'이라는 특성과 맞물려서,

하스켈의 형 시스템을 피하기 위한 수단으로 사용되기란 힘들고,

컴파일 시점에 컴파일러에게 더 많은 형 관련정보를 넘겨주기 위한 용도로 사용하는 것이 자연스럽습니다.


이것으로 GADT 에 대한 설명은 끝났습니다.

하스켈 위키(http://www.haskell.org/haskellwiki/GADT)의 예제를 찾아서 GADT 가 어떻게 사용되었는지도 살펴보시기 바랍니다.


---------------------------------------------------------------------------------------------------
Copyright (c) redneval@kldp.org, 2009.
GNU 자유 문서 라이선스 1.3판 또는 자유 소프트웨어 재단에서 발행한 이후 판의 규정에 따라,
본 문서를 복제하거나 개작 및배포할 수 있습니다.
본 문서에는 변경불가부분과 앞 표지구절 및 뒷 표지구절이 없습니다.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts.
---------------------------------------------------------------------------------------------------

Forums: 

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.