프로그래밍에서 활용되는 다형성은 크게 두 가지로 분류할 수 있다.
인자 다형성이란 넘어오는 인자의 타입이나 내용에 관계없이 일관된 동작을 하는 경우를 뜻한다. 대표적인 예로 넘어온 값을 그대로 돌려주는 항등함수를 들 수 있다.
f x = x
:type f
f "hello"
f 153
f 3.141592
다른 언어에서도 인자 다형성을 지원한다. 예를 들어 C++ 에서 항등함수 f를 정의하는 방법: http://cpp.sh/8avlb (C++ template 사용)
Java, C# 등에서도 제너릭(generic)이라는 기능을 통해 인자 다형성 기능을 지원 (Java 등에서 제너릭은 C++의 템플릿과 비슷한 문법을 사용하지만 기능이 미묘하게 다르다)
C++, Java, C# 등에서 인자 다형성을 활용하기 위한 문법이 Haskell과 같은 함수형 언어들보다 복잡한 이유는, 처음에는 이걸 필요없다고 생각해서 또는 만들기 어렵다고 생각해서 인자 다형성 없이 언어를 구현해 사용하다가 나중에 보니 없으면 안되겠다 싶어서 추가하느라 문법이 복잡해져 버린 것이다.
인자 다형성은 함수 정의에서 뿐 아니라 리스트와 같은 데이타 구조 정의에서도 매우 유용하다. 지금까지는 원소가 정수인 리스트만 다루었지만 경우에 따라서는 문자열의 리스트도 필요할 것이고 또 부동소수점수의 리스트도 필요할 것이고 또 다른 타입의 리스트도 필요할 것이다.
-- 타입마다 새로운 리스트 타입을 계속 정의하는 것은 좀 아니다
data ListI = NilI | ConsI Int ListI deriving Show
data ListS = NilS | ConsS String ListS deriving Show
data ListD = NilD | ConsD Double ListD deriving Show
-- ...
-- 인자 다형성을 활용한 일반적인 리스트 정의
-- (이거 하나면 위에것들 다 커버한다!)
data List a = Nil | Cons a (List a) deriving Show
x1, x2 :: Int
x1 = 1
x2 = 2
:type (Cons x1 (Cons x2 Nil))
s1, s2 :: String
s1 = "hello"
s2 = "world"
:type (Cons s1 (Cons s2 Nil))
전에 작성했던 append
함수도 사실은 polymorphic 함수이다.
똑같은 등식으로 된 함수 정의가 polymorphic 리스트 타입 (List a)
에 대해서도 그대로 동작한다.
append Nil ys = ys
append (Cons x xs) ys = Cons x (append xs ys)
:type append
append (Cons 1 (Cons 2 Nil)) (Cons 3 (Cons 4 Nil))
append (Cons "hello" (Cons "world" Nil)) (Cons "hi" (Cons "sky" Nil))
리스트와 마찬가지로 이진트리도 polymophic하게 정의할 수 있다. 리스트에 대한 append
를 polymorphic 타입을 갖는 함수로 정의될 수 있는 것과 마찬가지로, 이전에 정의한 sumTree
, height
같은 함수들도 똑같은 등식으로 polymorphic 이진트리에 대해서 정의할 수 있다.
data Tree a = Null | Node a (Tree a) (Tree a) deriving Show
:info []
1 : []
1 : 2 : 3 : 4 : []
[1]
[1,2,3,4]
[1] == 1:[]
[3,4,5] == 3:(4:(5:[]))
[1,2] ++ [3,4,5]
["aaa","bbb","ccc"]
plusList [] ys = []
plusList xs [] = []
plusList (x:xs) (y:ys) = (x+y) : plusList xs ys
plusList [1,2,3] [1,2,3,4,5]
plusList [1,2,3,4,5] [1,2,3]
다른 함수를 인자로 받거나 돌려주는 함수
plusList' = zipWith (+)
plusList [1,2] [1,2,3]
plusList' [1,2] [1,2,3]
multList = zipWith (*)
multList [1,2,3] [4,5,6,7]
zipWith' f [] ys = []
zipWith' f xs [] = []
zipWith' f (x:xs) (y:ys) = (f x y) : zipWith' f xs ys
multList' = zipWith' (*)
multList' [1,2,3] [4,5,6,7]
-- 합성함수
-- compose f g = \x -> f(g x)
compose f g x = f(g x)
:type compose
f x = x + 1
g x = x * 2
h = compose f g
f(g 3)
h 3
fact 0 = 1
fact n = n * fact (n - 1)
-- 짧게 쓰기 위해 위 코드는 n은 양수라고 가정했는데
-- n이 음수인지 검사하여 에러를 내 주거나 하는 것이 더 바람직하다
fact 3
fact 5
fact 20 :: Int
fact 21 :: Int
fact 21 :: Integer
fib n
이 n 번째 피보나치 수열의 수를 생성 (0번부터 시작한다고 가정)
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
-- 짧게 쓰기 위해 위 코드의 n은 양수라고 가정했는데
-- n이 음수인지 검사하여 에러를 내 주거나 하는 것이 더 바람직하다
fib 0
fib 1
fib 2
fib 3
fib 4
fib 5
fib 6
fib 7
fib 8
fib 9
map fib [0,1,2,3,4,5,6,7,8,9]
:type map
map (\x -> x*2) [0,1,2,3,4,5,6,7,8,9]
map (*2) [0,1,2,3,4]
이 환경에서는 텍스트 형식 외에 이미지 형식, HTML, 간단한 LaTeX 수식 등을 출력할 수 있는 기능도 제공된다.
import IHaskell.Display
Display [html "<strong style='color:red'>hello</strong> <u style='color:blue'>world</u>"]
Display [html "$(\\lambda x.\\lambda y.x)$"]
더 많은 기능이 IHaskell 예제 노트북에 소개되어 있다.
http://nbviewer.jupyter.org/github/gibiansky/IHaskell/blob/master/notebooks/IHaskell.ipynb