SlideShare uma empresa Scribd logo
1 de 22
Baixar para ler offline
Haskell
Study
12. monoid
Before Monoid..
Monoid에 대해 이야기하기 전에, 아래의 함수들을 먼저 살펴봅시다.
*(곱셈), +(덧셈), ++(리스트 연결)
이 함수들의 공통적인 특성에는 무엇이 있을까요? 일단 다음의 두 가지 공통적인 특성을 가지고
있습니다.
•	 두 개의 인자를 받습니다.
•	 인자와 리턴 값이 같은 타입을 가집니다.
이건 굉장히 직관적이고 간단한 특성이죠. 그리고 조금 더 복잡한 공통 특성들도 있습니다.
Before Monoid..
항등원(Identity)
곱셈, 덧셈, 리스트 연결등의 연산자는 항등원(Identity)을 가집니다. 항등원이란 해당 연산자에
사용됐을 때 결과값이 다른 피 연산자와 동일하게 나오는 값을 말합니다. 예를 들어 곱셈의 항등원은 1
이죠.
1*3 == 3
5*1 == 5
1은 곱셈에 사용됐을 때 항상 다른 피연산자의 값이 그대로 결과값이 된다는 것을 알 수 있습니다.
마찬가지로 덧셈의 항등원은 0, 리스트 연결의 항등원은 [] 입니다.
[1,2,3] ++ [] = [1,2,3] , 26 + 0 = 26
[] ++ [4,5,6] = [4,5,6] , 0 + 31 = 31
Before Monoid..
결합성(associativity)
또다른 특성은 바로 결합성입니다. 셋 이상의 값에 대해 이 함수들을 연속해서 적용할 때, 적용하는
순서에 상관없이 결과값이 항상 일정한 것을 결합성(associativity)이라고 하죠.
(2*3)*4 == 2*(3*4)
(5+7)+9 == 5+(7+9)
([2,3] ++ [6,7,8]) ++ [9,10,11] == [2,3] ++ ([6,7,8] ++ [9,10,11])
앞에서 제시한 이런 모든 특성들을 만족하는 동작을 할 수 있는 타입들이 바로 Monoid입니다.
Monoid에 속한 타입에 대해서는 위와 같은 특성을 만족하는 함수를 사용할 수 있는 거죠.
Monoid
Monoid의 선언은 다음과 같습니다.
class Monoid m where
	mempty :: m
	mappend :: m -> m -> m
	mconcat :: [m] -> m
	mconcat = foldr mappend mempty
Monoid 타입 클래스는 Data.Monoid 모듈에 정의되어 있습니다. 함수를 하나씩 살펴봅시다. 우선
mempty 함수는 아까 앞에서 언급했던 항등원(identity)입니다. mempty 함수(인자를 받지 않기
때문에 사실상 값과 같죠)가 해당 타입에서 항등원 역할을 하는 값인 거죠.
Monoid
mappend 함수가 앞에서 이야기했던 함수들(+, *, ++ 등등)에 해당하는 함수입니다. 마지막은
mconcat 함수죠. 이 함수는 기본 정의가 있고(mconcat = foldr mappend mempty), 대부분의
경우 이 정의로 충분하기 때문에 크게 신경 쓸 필요는 없습니다. mconcat 함수는 값의 리스트에
연속해서 mappend 함수를 적용시켜 최종 하나의 결과 값을 도출하는 함수인 거죠. 그리고 monoid
의 함수들은 아래의 규칙을 만족해야합니다.
mempty `mappend` x = x 										(left identity)
y `mappend` mempty = y 										(right identity)
(x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)	(associativity)
이 세 가지 특성은 앞에서 이미 언급한 내용이죠. Monad 파트에서도 말했듯이 컴파일러가 이러한
특성까지 검사해주진 않기 때문에 Monoid를 만들 땐 주의해서 작성해야 합니다.
List Monoid
List는 Monoid에 속하는 타입입니다. List Monoid는 다음과 같이 정의되어 있습니다.
instance Monoid [a] where
	mempty = []
	mappend = (++)
임의 타입의 리스트에 대해 항등원 mempty는 빈 리스트이며, mappend는 ++ 연산자입니다.
앞에서 예제로 다뤘듯이 임의 타입의 리스트에 대해 ++ 연산자는 monoid의 특성을 모두 만족하는
함수이기 때문이죠. 이로부터 List Monoid에 대한 mconcat 함수는 그냥 concat과 같다는 것 역시
알 수 있습니다.
newtype
그렇다면 숫자 타입(Num에 속하는 타입들)은 어떤 Monoid일까요? 곱셈과 덧셈 모두 Monoid의
특성을 만족하기 때문에 둘 중 하나로 정할 수 없다는 문제가 생깁니다. 이를 위해 사용되는 키워드가
바로 newtype입니다. Monoid에 대한 이야기를 더 하기 전에 newtype에 대해 먼저 짚고 넘어가죠.
newtype 키워드는 이미 존재하는 타입을 기반으로 새로운 타입을 만들 때 사용합니다. 내부적으로는
이미 존재하는 타입과 완전히 동일하지만, 외부적으로는 호환이 되지 않게 만들어주는 거죠. 이는 타입
체크를 통한 함수의 잘못된 사용을 더 엄격하게 막아주는 역할을 합니다. 문법은 data를 사용할 때와
동일하지만, 필드로 단 하나의 타입만을 가질 수 있다는 차이점이 있습니다. 기존의 타입으로부터
새로운 타입을 만들어내는 것이기 때문이죠.
newtype Vector2D = Vector2D { getVector2D :: (Int, Int) }
newtype
newtype의 유용성을 확인하기 위해 아래의 예제 코드를 살펴봅시다.
type BookId = Int
type UserId = Int
getBookName :: BookId -> String
getBookName = getName . findBookById
위와 같은 코드가 있다고 합시다. BookId와 UserId는 모두 정수값이기 때문에 Int형으로 만들었고,
가독성을 위해 type 키워드로 서로 다른 2개의 이름을 만들었습니다. 그러나 이렇게 코드를 작성할
경우의 문제점은 getBookName의 인자로 UserId 타입의 값을 넘겨도 컴파일이 된다는 거죠. 이
경우 논리적으로 명백한 오류임에도 불구하고, 내부적으로 두 개의 값은 동일한 Int이므로 컴파일
시에 아무런 이상을 감지할 수 없다는 거죠.
newtype
newtype BookId = BookId Int
newtype UserId = UserId Int
getBookName :: BookId -> String
getBookName = getName . findBookById
반면 위와 같이 newtype을 이용할 경우 내부적으로는 동일한 Int지만 서로 완전히 다른 타입으로
취급되기 때문에 BookId와 UserId가 서로 호환되지 않게 됩니다. 좀 더 엄격한 타입 체크가
가능해지는 것이죠.
하지만 이건 그냥 data로 선언해도 되는 것 아니냐? 라는 생각이 들 수도 있습니다. data 키워드로
하나의 필드를 갖는 타입을 새로 선언해도 newtype과 같은 효과를 낼 수 있으니까요. 여기서
newtype의 장점은, data와는 다르게 런타임에 새로운 타입을 생성시키지는 않는다는 것입니다.
기존 타입을 그대로 사용하기 때문에 data보다 좀 더 효율이 좋은 코드를 생성하게 되는 거죠.
Monoid
다시 Monoid 이야기로 돌아와 봅시다. 곱셈과 덧셈 두 개의 Monoid를 위해 Haskell에서는
newtype 키워드를 이용해 만든 Product와 Sum이라는 두 개의 타입을 제공합니다. 이 두 타입은
내부적으로는 단순 숫자 타입과 완전 동일하지만, Monoid 타입클래스의 함수를 적용시켰을 때
하나는 곱셈, 하나는 덧셈으로 동작하게 되는 거죠. 이 두개의 타입은 Data.Monoid 모듈에 다음과
비슷한 형태로 정의되어 있습니다.
newtype Product a = Product { getProduct :: a }
	 deriving (Eq, Ord, Read, Show, Bounded)
newtype Sum a = Sum { getSum :: a }
	 deriving (Eq, Ord, Read, Show, Bounded)
Monoid
두 타입의 Monoid 타입 클래스에 대한 인스턴스는 아래와 같이 정의되어 있습니다.
instance Num a => Monoid (Product a) where
	mempty = Product 1
	 Product x `mappend` Product y = Product (x * y)
instance Num a => Monoid (Sum a) where
	mempty = Sum 0
	 Sum x `mappend` Sum y = Sum (x + y)
newtype 키워드를 이용해 Num 타입클래스에 속한 타입들에 대해 Sum으로서의 Monoid,
Product로서의 Monoid 둘 모두를 제공하고 있습니다. 같은 타입에 대해 두 개 이상의 타입 클래스
인스턴스가 필요할 때 이런 식으로 newtype 키워드를 유용하게 사용할 수 있죠.
Maybe Monoid
어떤 타입 a가 Monoid 타입 클래스에 속한다면 Maybe a 역시 Monoid 타입 클래스에 속합니다.
이는 아래와 같이 정의되어 있습니다.
instance Monoid a => Monoid (Maybe a) where
	mempty = Nothing
	 Nothing `mappend` m = m
	 m `mappend` Nothing = m
	 Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
Prelude> Nothing `mappend` Just "test"
Just "test"
Prelude> Just (Sum 3) `mappend` Just (Sum 4)
Just (Sum {getSum = 7})
Maybe Monoid
Maybe a타입이 모노이드이기 때문에 a타입이 Monoid일 경우 실패할 수 있는 연산을 연속적으로
수행할 때 굉장히 유용하게 사용될 수 있습니다. 어떤 실패할 수 있는 연산들의 결과값을 모두 합치는
연산이 필요하다면 mconcat 함수만 사용해도 바로 결과를 얻어낼 수 있죠.
하지만 Maybe의 내부에 속한 타입이 Monoid에 속하지 않는 경우는 어떻게 해야할까요? 이런
경우를 위해 First라는 타입이 정의되어 있습니다.
newtype First a = First { getFirst :: maybe a }
	 deriving (Eq, Ord, Read, Show)
위와 같이 First는 Maybe 타입을 newtype 키워드로 래핑(wrapping)한 타입이며, 이 타입의
모노이드는 양변이 모두 내부에 정당한 값을 가지고 있을 경우(Just value 인 경우) 이름과 같이 첫
번째 값(좌변값)을 결과값으로 취하게끔 동작합니다.
Maybe Monoid
instance Monoid (First a) where
	mempty = First Nothing
	 First (Just x) `mappend` _ = First (Just x)
	 First Nothing `mappend` x = x
위의 정의에서 볼 수 있듯이, 좌변에 값이 있다면 (First (Just x)) 우변의 값은 버리고 좌변값만
취하며, 좌변의 값이 없다면(First Nothing) 무조건 우변의 값을 취하는 방식으로 동작하죠.
내부에 존재하는 값이 Monoid에 속하지 않기 때문에 이러한 방식으로 연산 결과값을 합치는
것입니다. First는 연산 결과값 중에 단 하나라도 정당한 결과(Just x)가 존재하는지 확인하고
싶을 때 유용하게 사용할 수 있습니다.
Prelude> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10]
Just 9
Maybe Monoid
반대로 Last 타입도 있습니다. 이 타입 역시 하는 역할은 First와 동일하나, 좌변이 아니라 우변의 값을
결과값으로 취한다는 차이만 있습니다.
newtype Last a = Last { getLast :: maybe a }
	 deriving (Eq, Ord, Read, Show)
instance Monoid (Last a) where
	mempty = Last Nothing
	 _ `mappend` First (Just x) = First (Just x)
	 x `mappend` First Nothing = x
Prelude> getLast . mconcat . map First $ [Nothing, Just 9, Just 10]
Just 10
연습문제
숫자 타입에 Product, Sum이라는 두 가지 모노이드가 존재하듯이 Bool 타입에 대해서도 두 가지
모노이드가 존재합니다. 그 두가지는 바로 Any 모노이드와 All 모노이드인데, 아마 이름에서 이 둘이
어떤 식으로 mappend를 하는 지 짐작할 수 있으실 겁니다. Any, All 타입을 newtype 키워드를
통해 정의하고 해당 타입의 Monoid 타입 클래스에 대한 인스턴스를 정의해보세요. 물론 이름이
겹칠테니 MyAny, MyAll 등의 이름으로 정의하시면 됩니다.
* 참고 : mappend 함수는 중위 함수로 사용되는 일이 많아 <>라는 mappend와 동일한 역할을 하는
중위 연산자가 존재합니다. 이 함수를 사용하면 코드를 좀 더 간결하게 쓸 수 있습니다.
Prelude> [1,2,3] `mappend` [4,5,6]
[1,2,3,4,5,6]
Prelude> [1,2,3] <> [4,5,6]
[1,2,3,4,5,6]
Foldable
Monoid를 유용하게 활용할 수 있는 타입 클래스로 Foldable이라는 타입 클래스가 있습니다.
Foldable은 접을 수 있는(fold할 수 있는) 타입들이 속하는 타입 클래스로, Data.Foldable 모듈에
정의되어 있죠. 이 Data.Foldable 모듈의 함수는 Prelude 모듈의 함수들과 겹치는게 많기 때문에
되도록이면 qualified import하는게 좋습니다(import qualified Data.Foldable as F 등).
Foldable 타입 클래스 역시 foldl, foldr 등등의 함수를 제공합니다. 하지만 Prelude에 있는 함수와
타입 서명이 조금 다릅니다.
import qualifield Data.Foldable as F
foldr :: (a -> b -> b) -> b -> [a] -> b
F.foldr :: F.Foldable t => (a -> b -> b) -> b -> t a -> b
Foldable의 foldr 함수가 좀 더 일반적인 형태를 갖고 있음을 알 수 있습니다.
Foldable
Foldable은 리스트 뿐만 아니라 접을 수 있는 모든 종류의 타입에 대해 foldl, foldr 등의 함수를
제공합니다. Foldable을 유용하게 활용하는 예제를 한 번 살펴봅시다.
data Tree a = Empty | Node a (Tree a) (Tree a)
	 deriving (Show, Read, Eq)
아주 간단한 이진 트리 데이터 타입입니다. 이 트리를 Foldable 타입 클래스에 속하게 만들면 트리의
전체 요소를 순회하며 어떤 연산을 수행하기 굉장히 쉬워집니다. Foldable 타입 클래스가 제공하는
함수는 굉장히 많지만 foldMap을 제외한 나머지 함수들은 foldMap을 이용한 기본 정의가 있기
때문에 이 한 가지 함수만 정의하면 됩니다. 그리고 foldMap 함수는 다음과 같은 타입을 갖고
있습니다.
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
Foldable
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
이 정의를 잘 보면 foldMap에서 적용할 함수는 a -> m 타입이라는 것을 알 수 있습니다. 즉, tree의
각 요소에 함수를 적용했을 때 결과는 모노이드에 속한 타입의 값이어야 한다는 거죠. 이 타입 서명을
이용해 앞서 정의한 Tree타입에 대한 foldMap 함수를 다음과 같이 정의할 수 있습니다.
instance F.Foldable Tree where
	 foldMap f Empty = mempty
	 foldMap f (Node x l r) = F.foldMap f l `mappend`
							 f x			 `mappend`
							 F.foldMap f r
Foldable
이렇게 정의한 foldMap 함수를 다음의 테스트용 Tree에 적용해봅시다.
testTree = Node 5
			(Node 3
				(Node 1 Empty Empty)
				(Node 6 Empty Empty)
			)
			(Node 9
				(Node 8 Empty Empty)
				(Node 10 Empty Empty)
			)
5
3 9
1 6 8 10
Foldable
Prelude> F.foldl (+) 0 testTree
42
Prelude> F.foldl (*) 1 testTree
64800
Prelude> F.foldMap (x -> [x]) testTree
[1,3,6,5,8,9,10]
위와 같이 트리의 모든 원소를 더한 값, 곱한 값을
Foldable의 foldl 함수를 이용해 아주 쉽게 구할 수
있습니다.
또, 맨 아래 예제처럼 주어진 트리를 리스트
로 바꾸는 것 역시 아주 쉽게 할 수 있죠.
5
3 9
1 6 8 10

Mais conteúdo relacionado

Mais procurados

Mais procurados (20)

Haskell study 4
Haskell study 4Haskell study 4
Haskell study 4
 
Haskell study 1
Haskell study 1Haskell study 1
Haskell study 1
 
Haskell study 3
Haskell study 3Haskell study 3
Haskell study 3
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
 
State Monad
State MonadState Monad
State Monad
 
Operator overloading C++
Operator overloading C++Operator overloading C++
Operator overloading C++
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Ad hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and Cats
 
Javapolymorphism
JavapolymorphismJavapolymorphism
Javapolymorphism
 
The Lambda Calculus and The JavaScript
The Lambda Calculus and The JavaScriptThe Lambda Calculus and The JavaScript
The Lambda Calculus and The JavaScript
 
This pointer
This pointerThis pointer
This pointer
 
Left and Right Folds - Comparison of a mathematical definition and a programm...
Left and Right Folds- Comparison of a mathematical definition and a programm...Left and Right Folds- Comparison of a mathematical definition and a programm...
Left and Right Folds - Comparison of a mathematical definition and a programm...
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Notions de base de JavaScript
Notions de base de JavaScriptNotions de base de JavaScript
Notions de base de JavaScript
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
Chap3 programmation modulaire en python
Chap3 programmation modulaire en pythonChap3 programmation modulaire en python
Chap3 programmation modulaire en python
 
From Scala Monadic Effects to Unison Algebraic Effects
From Scala Monadic Effects to Unison Algebraic EffectsFrom Scala Monadic Effects to Unison Algebraic Effects
From Scala Monadic Effects to Unison Algebraic Effects
 
Cracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 ExamsCracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 Exams
 

Destaque

Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)
Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)
Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)
Fabiola Eyholzer
 

Destaque (10)

Interacción web
Interacción   webInteracción   web
Interacción web
 
Menu Last Light Lodge Tuatapere NZ
Menu Last Light Lodge Tuatapere NZMenu Last Light Lodge Tuatapere NZ
Menu Last Light Lodge Tuatapere NZ
 
Nuevas tendencias en_la_gestin_educativa
Nuevas tendencias en_la_gestin_educativaNuevas tendencias en_la_gestin_educativa
Nuevas tendencias en_la_gestin_educativa
 
하스켈로 알고리즘 문제 풀기 2
하스켈로 알고리즘 문제 풀기 2하스켈로 알고리즘 문제 풀기 2
하스켈로 알고리즘 문제 풀기 2
 
The GIG Economy
The GIG EconomyThe GIG Economy
The GIG Economy
 
Single and multy crystalline solar panels
Single and multy crystalline solar panelsSingle and multy crystalline solar panels
Single and multy crystalline solar panels
 
Desequilibrio electrolitico
Desequilibrio electroliticoDesequilibrio electrolitico
Desequilibrio electrolitico
 
NEFROLOGIA CLINICA: Nefritis tubulointersticial aguda
NEFROLOGIA CLINICA: Nefritis tubulointersticial agudaNEFROLOGIA CLINICA: Nefritis tubulointersticial aguda
NEFROLOGIA CLINICA: Nefritis tubulointersticial aguda
 
NEFROLOGIA CLINICA: Hipotiroidismo y enfermedad renal cronica
NEFROLOGIA CLINICA: Hipotiroidismo y enfermedad renal cronicaNEFROLOGIA CLINICA: Hipotiroidismo y enfermedad renal cronica
NEFROLOGIA CLINICA: Hipotiroidismo y enfermedad renal cronica
 
Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)
Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)
Infographic: 10 Things HR Does Not Want To Hear From Agile (Agile HR)
 

Semelhante a Haskell study 12

Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
익성 조
 
파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304
Yong Joon Moon
 
Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4
현찬 양
 

Semelhante a Haskell study 12 (20)

C++11
C++11C++11
C++11
 
8.다중메서드
8.다중메서드8.다중메서드
8.다중메서드
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
Scala nested function generic function
Scala nested function generic functionScala nested function generic function
Scala nested function generic function
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
 
Light Tutorial Python
Light Tutorial PythonLight Tutorial Python
Light Tutorial Python
 
파이썬 심화
파이썬 심화파이썬 심화
파이썬 심화
 
파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304
 
Effective c++chapter1 and2
Effective c++chapter1 and2Effective c++chapter1 and2
Effective c++chapter1 and2
 
Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4
 
Apply교육
Apply교육Apply교육
Apply교육
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java
 
파이썬 둘째날
파이썬 둘째날파이썬 둘째날
파이썬 둘째날
 
Start IoT with JavaScript - 4.객체1
Start IoT with JavaScript - 4.객체1Start IoT with JavaScript - 4.객체1
Start IoT with JavaScript - 4.객체1
 
c++11
c++11c++11
c++11
 
C++11(최지웅)
C++11(최지웅)C++11(최지웅)
C++11(최지웅)
 
객체지향 단어가 의미하는 것
객체지향 단어가 의미하는 것객체지향 단어가 의미하는 것
객체지향 단어가 의미하는 것
 
Monad programming
Monad programmingMonad programming
Monad programming
 
자바 8 학습
자바 8 학습자바 8 학습
자바 8 학습
 

Mais de Nam Hyeonuk

Mais de Nam Hyeonuk (17)

Next 게임 실전 프로젝트 슬라이드
Next 게임 실전 프로젝트 슬라이드Next 게임 실전 프로젝트 슬라이드
Next 게임 실전 프로젝트 슬라이드
 
Haskell study 11
Haskell study 11Haskell study 11
Haskell study 11
 
Haskell study 10
Haskell study 10Haskell study 10
Haskell study 10
 
Haskell study 7
Haskell study 7Haskell study 7
Haskell study 7
 
Haskell study 0
Haskell study 0Haskell study 0
Haskell study 0
 
Multi thread
Multi threadMulti thread
Multi thread
 
Memory & object pooling
Memory & object poolingMemory & object pooling
Memory & object pooling
 
Database
DatabaseDatabase
Database
 
Exception&log
Exception&logException&log
Exception&log
 
Iocp advanced
Iocp advancedIocp advanced
Iocp advanced
 
Iocp 기본 구조 이해
Iocp 기본 구조 이해Iocp 기본 구조 이해
Iocp 기본 구조 이해
 
Tcp ip & io model
Tcp ip & io modelTcp ip & io model
Tcp ip & io model
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약
 
구문과 의미론(정적 의미론까지)
구문과 의미론(정적 의미론까지)구문과 의미론(정적 의미론까지)
구문과 의미론(정적 의미론까지)
 
Gpg 1.1
Gpg 1.1Gpg 1.1
Gpg 1.1
 
Stl vector, list, map
Stl vector, list, mapStl vector, list, map
Stl vector, list, map
 
Age Of Empires II : Age Of Kings Postmotem
Age Of Empires II : Age Of Kings PostmotemAge Of Empires II : Age Of Kings Postmotem
Age Of Empires II : Age Of Kings Postmotem
 

Haskell study 12

  • 2. Before Monoid.. Monoid에 대해 이야기하기 전에, 아래의 함수들을 먼저 살펴봅시다. *(곱셈), +(덧셈), ++(리스트 연결) 이 함수들의 공통적인 특성에는 무엇이 있을까요? 일단 다음의 두 가지 공통적인 특성을 가지고 있습니다. • 두 개의 인자를 받습니다. • 인자와 리턴 값이 같은 타입을 가집니다. 이건 굉장히 직관적이고 간단한 특성이죠. 그리고 조금 더 복잡한 공통 특성들도 있습니다.
  • 3. Before Monoid.. 항등원(Identity) 곱셈, 덧셈, 리스트 연결등의 연산자는 항등원(Identity)을 가집니다. 항등원이란 해당 연산자에 사용됐을 때 결과값이 다른 피 연산자와 동일하게 나오는 값을 말합니다. 예를 들어 곱셈의 항등원은 1 이죠. 1*3 == 3 5*1 == 5 1은 곱셈에 사용됐을 때 항상 다른 피연산자의 값이 그대로 결과값이 된다는 것을 알 수 있습니다. 마찬가지로 덧셈의 항등원은 0, 리스트 연결의 항등원은 [] 입니다. [1,2,3] ++ [] = [1,2,3] , 26 + 0 = 26 [] ++ [4,5,6] = [4,5,6] , 0 + 31 = 31
  • 4. Before Monoid.. 결합성(associativity) 또다른 특성은 바로 결합성입니다. 셋 이상의 값에 대해 이 함수들을 연속해서 적용할 때, 적용하는 순서에 상관없이 결과값이 항상 일정한 것을 결합성(associativity)이라고 하죠. (2*3)*4 == 2*(3*4) (5+7)+9 == 5+(7+9) ([2,3] ++ [6,7,8]) ++ [9,10,11] == [2,3] ++ ([6,7,8] ++ [9,10,11]) 앞에서 제시한 이런 모든 특성들을 만족하는 동작을 할 수 있는 타입들이 바로 Monoid입니다. Monoid에 속한 타입에 대해서는 위와 같은 특성을 만족하는 함수를 사용할 수 있는 거죠.
  • 5. Monoid Monoid의 선언은 다음과 같습니다. class Monoid m where mempty :: m mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty Monoid 타입 클래스는 Data.Monoid 모듈에 정의되어 있습니다. 함수를 하나씩 살펴봅시다. 우선 mempty 함수는 아까 앞에서 언급했던 항등원(identity)입니다. mempty 함수(인자를 받지 않기 때문에 사실상 값과 같죠)가 해당 타입에서 항등원 역할을 하는 값인 거죠.
  • 6. Monoid mappend 함수가 앞에서 이야기했던 함수들(+, *, ++ 등등)에 해당하는 함수입니다. 마지막은 mconcat 함수죠. 이 함수는 기본 정의가 있고(mconcat = foldr mappend mempty), 대부분의 경우 이 정의로 충분하기 때문에 크게 신경 쓸 필요는 없습니다. mconcat 함수는 값의 리스트에 연속해서 mappend 함수를 적용시켜 최종 하나의 결과 값을 도출하는 함수인 거죠. 그리고 monoid 의 함수들은 아래의 규칙을 만족해야합니다. mempty `mappend` x = x (left identity) y `mappend` mempty = y (right identity) (x `mappend` y) `mappend` z = x `mappend` (y `mappend` z) (associativity) 이 세 가지 특성은 앞에서 이미 언급한 내용이죠. Monad 파트에서도 말했듯이 컴파일러가 이러한 특성까지 검사해주진 않기 때문에 Monoid를 만들 땐 주의해서 작성해야 합니다.
  • 7. List Monoid List는 Monoid에 속하는 타입입니다. List Monoid는 다음과 같이 정의되어 있습니다. instance Monoid [a] where mempty = [] mappend = (++) 임의 타입의 리스트에 대해 항등원 mempty는 빈 리스트이며, mappend는 ++ 연산자입니다. 앞에서 예제로 다뤘듯이 임의 타입의 리스트에 대해 ++ 연산자는 monoid의 특성을 모두 만족하는 함수이기 때문이죠. 이로부터 List Monoid에 대한 mconcat 함수는 그냥 concat과 같다는 것 역시 알 수 있습니다.
  • 8. newtype 그렇다면 숫자 타입(Num에 속하는 타입들)은 어떤 Monoid일까요? 곱셈과 덧셈 모두 Monoid의 특성을 만족하기 때문에 둘 중 하나로 정할 수 없다는 문제가 생깁니다. 이를 위해 사용되는 키워드가 바로 newtype입니다. Monoid에 대한 이야기를 더 하기 전에 newtype에 대해 먼저 짚고 넘어가죠. newtype 키워드는 이미 존재하는 타입을 기반으로 새로운 타입을 만들 때 사용합니다. 내부적으로는 이미 존재하는 타입과 완전히 동일하지만, 외부적으로는 호환이 되지 않게 만들어주는 거죠. 이는 타입 체크를 통한 함수의 잘못된 사용을 더 엄격하게 막아주는 역할을 합니다. 문법은 data를 사용할 때와 동일하지만, 필드로 단 하나의 타입만을 가질 수 있다는 차이점이 있습니다. 기존의 타입으로부터 새로운 타입을 만들어내는 것이기 때문이죠. newtype Vector2D = Vector2D { getVector2D :: (Int, Int) }
  • 9. newtype newtype의 유용성을 확인하기 위해 아래의 예제 코드를 살펴봅시다. type BookId = Int type UserId = Int getBookName :: BookId -> String getBookName = getName . findBookById 위와 같은 코드가 있다고 합시다. BookId와 UserId는 모두 정수값이기 때문에 Int형으로 만들었고, 가독성을 위해 type 키워드로 서로 다른 2개의 이름을 만들었습니다. 그러나 이렇게 코드를 작성할 경우의 문제점은 getBookName의 인자로 UserId 타입의 값을 넘겨도 컴파일이 된다는 거죠. 이 경우 논리적으로 명백한 오류임에도 불구하고, 내부적으로 두 개의 값은 동일한 Int이므로 컴파일 시에 아무런 이상을 감지할 수 없다는 거죠.
  • 10. newtype newtype BookId = BookId Int newtype UserId = UserId Int getBookName :: BookId -> String getBookName = getName . findBookById 반면 위와 같이 newtype을 이용할 경우 내부적으로는 동일한 Int지만 서로 완전히 다른 타입으로 취급되기 때문에 BookId와 UserId가 서로 호환되지 않게 됩니다. 좀 더 엄격한 타입 체크가 가능해지는 것이죠. 하지만 이건 그냥 data로 선언해도 되는 것 아니냐? 라는 생각이 들 수도 있습니다. data 키워드로 하나의 필드를 갖는 타입을 새로 선언해도 newtype과 같은 효과를 낼 수 있으니까요. 여기서 newtype의 장점은, data와는 다르게 런타임에 새로운 타입을 생성시키지는 않는다는 것입니다. 기존 타입을 그대로 사용하기 때문에 data보다 좀 더 효율이 좋은 코드를 생성하게 되는 거죠.
  • 11. Monoid 다시 Monoid 이야기로 돌아와 봅시다. 곱셈과 덧셈 두 개의 Monoid를 위해 Haskell에서는 newtype 키워드를 이용해 만든 Product와 Sum이라는 두 개의 타입을 제공합니다. 이 두 타입은 내부적으로는 단순 숫자 타입과 완전 동일하지만, Monoid 타입클래스의 함수를 적용시켰을 때 하나는 곱셈, 하나는 덧셈으로 동작하게 되는 거죠. 이 두개의 타입은 Data.Monoid 모듈에 다음과 비슷한 형태로 정의되어 있습니다. newtype Product a = Product { getProduct :: a } deriving (Eq, Ord, Read, Show, Bounded) newtype Sum a = Sum { getSum :: a } deriving (Eq, Ord, Read, Show, Bounded)
  • 12. Monoid 두 타입의 Monoid 타입 클래스에 대한 인스턴스는 아래와 같이 정의되어 있습니다. instance Num a => Monoid (Product a) where mempty = Product 1 Product x `mappend` Product y = Product (x * y) instance Num a => Monoid (Sum a) where mempty = Sum 0 Sum x `mappend` Sum y = Sum (x + y) newtype 키워드를 이용해 Num 타입클래스에 속한 타입들에 대해 Sum으로서의 Monoid, Product로서의 Monoid 둘 모두를 제공하고 있습니다. 같은 타입에 대해 두 개 이상의 타입 클래스 인스턴스가 필요할 때 이런 식으로 newtype 키워드를 유용하게 사용할 수 있죠.
  • 13. Maybe Monoid 어떤 타입 a가 Monoid 타입 클래스에 속한다면 Maybe a 역시 Monoid 타입 클래스에 속합니다. 이는 아래와 같이 정의되어 있습니다. instance Monoid a => Monoid (Maybe a) where mempty = Nothing Nothing `mappend` m = m m `mappend` Nothing = m Just m1 `mappend` Just m2 = Just (m1 `mappend` m2) Prelude> Nothing `mappend` Just "test" Just "test" Prelude> Just (Sum 3) `mappend` Just (Sum 4) Just (Sum {getSum = 7})
  • 14. Maybe Monoid Maybe a타입이 모노이드이기 때문에 a타입이 Monoid일 경우 실패할 수 있는 연산을 연속적으로 수행할 때 굉장히 유용하게 사용될 수 있습니다. 어떤 실패할 수 있는 연산들의 결과값을 모두 합치는 연산이 필요하다면 mconcat 함수만 사용해도 바로 결과를 얻어낼 수 있죠. 하지만 Maybe의 내부에 속한 타입이 Monoid에 속하지 않는 경우는 어떻게 해야할까요? 이런 경우를 위해 First라는 타입이 정의되어 있습니다. newtype First a = First { getFirst :: maybe a } deriving (Eq, Ord, Read, Show) 위와 같이 First는 Maybe 타입을 newtype 키워드로 래핑(wrapping)한 타입이며, 이 타입의 모노이드는 양변이 모두 내부에 정당한 값을 가지고 있을 경우(Just value 인 경우) 이름과 같이 첫 번째 값(좌변값)을 결과값으로 취하게끔 동작합니다.
  • 15. Maybe Monoid instance Monoid (First a) where mempty = First Nothing First (Just x) `mappend` _ = First (Just x) First Nothing `mappend` x = x 위의 정의에서 볼 수 있듯이, 좌변에 값이 있다면 (First (Just x)) 우변의 값은 버리고 좌변값만 취하며, 좌변의 값이 없다면(First Nothing) 무조건 우변의 값을 취하는 방식으로 동작하죠. 내부에 존재하는 값이 Monoid에 속하지 않기 때문에 이러한 방식으로 연산 결과값을 합치는 것입니다. First는 연산 결과값 중에 단 하나라도 정당한 결과(Just x)가 존재하는지 확인하고 싶을 때 유용하게 사용할 수 있습니다. Prelude> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10] Just 9
  • 16. Maybe Monoid 반대로 Last 타입도 있습니다. 이 타입 역시 하는 역할은 First와 동일하나, 좌변이 아니라 우변의 값을 결과값으로 취한다는 차이만 있습니다. newtype Last a = Last { getLast :: maybe a } deriving (Eq, Ord, Read, Show) instance Monoid (Last a) where mempty = Last Nothing _ `mappend` First (Just x) = First (Just x) x `mappend` First Nothing = x Prelude> getLast . mconcat . map First $ [Nothing, Just 9, Just 10] Just 10
  • 17. 연습문제 숫자 타입에 Product, Sum이라는 두 가지 모노이드가 존재하듯이 Bool 타입에 대해서도 두 가지 모노이드가 존재합니다. 그 두가지는 바로 Any 모노이드와 All 모노이드인데, 아마 이름에서 이 둘이 어떤 식으로 mappend를 하는 지 짐작할 수 있으실 겁니다. Any, All 타입을 newtype 키워드를 통해 정의하고 해당 타입의 Monoid 타입 클래스에 대한 인스턴스를 정의해보세요. 물론 이름이 겹칠테니 MyAny, MyAll 등의 이름으로 정의하시면 됩니다. * 참고 : mappend 함수는 중위 함수로 사용되는 일이 많아 <>라는 mappend와 동일한 역할을 하는 중위 연산자가 존재합니다. 이 함수를 사용하면 코드를 좀 더 간결하게 쓸 수 있습니다. Prelude> [1,2,3] `mappend` [4,5,6] [1,2,3,4,5,6] Prelude> [1,2,3] <> [4,5,6] [1,2,3,4,5,6]
  • 18. Foldable Monoid를 유용하게 활용할 수 있는 타입 클래스로 Foldable이라는 타입 클래스가 있습니다. Foldable은 접을 수 있는(fold할 수 있는) 타입들이 속하는 타입 클래스로, Data.Foldable 모듈에 정의되어 있죠. 이 Data.Foldable 모듈의 함수는 Prelude 모듈의 함수들과 겹치는게 많기 때문에 되도록이면 qualified import하는게 좋습니다(import qualified Data.Foldable as F 등). Foldable 타입 클래스 역시 foldl, foldr 등등의 함수를 제공합니다. 하지만 Prelude에 있는 함수와 타입 서명이 조금 다릅니다. import qualifield Data.Foldable as F foldr :: (a -> b -> b) -> b -> [a] -> b F.foldr :: F.Foldable t => (a -> b -> b) -> b -> t a -> b Foldable의 foldr 함수가 좀 더 일반적인 형태를 갖고 있음을 알 수 있습니다.
  • 19. Foldable Foldable은 리스트 뿐만 아니라 접을 수 있는 모든 종류의 타입에 대해 foldl, foldr 등의 함수를 제공합니다. Foldable을 유용하게 활용하는 예제를 한 번 살펴봅시다. data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) 아주 간단한 이진 트리 데이터 타입입니다. 이 트리를 Foldable 타입 클래스에 속하게 만들면 트리의 전체 요소를 순회하며 어떤 연산을 수행하기 굉장히 쉬워집니다. Foldable 타입 클래스가 제공하는 함수는 굉장히 많지만 foldMap을 제외한 나머지 함수들은 foldMap을 이용한 기본 정의가 있기 때문에 이 한 가지 함수만 정의하면 됩니다. 그리고 foldMap 함수는 다음과 같은 타입을 갖고 있습니다. foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
  • 20. Foldable foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m 이 정의를 잘 보면 foldMap에서 적용할 함수는 a -> m 타입이라는 것을 알 수 있습니다. 즉, tree의 각 요소에 함수를 적용했을 때 결과는 모노이드에 속한 타입의 값이어야 한다는 거죠. 이 타입 서명을 이용해 앞서 정의한 Tree타입에 대한 foldMap 함수를 다음과 같이 정의할 수 있습니다. instance F.Foldable Tree where foldMap f Empty = mempty foldMap f (Node x l r) = F.foldMap f l `mappend` f x `mappend` F.foldMap f r
  • 21. Foldable 이렇게 정의한 foldMap 함수를 다음의 테스트용 Tree에 적용해봅시다. testTree = Node 5 (Node 3 (Node 1 Empty Empty) (Node 6 Empty Empty) ) (Node 9 (Node 8 Empty Empty) (Node 10 Empty Empty) ) 5 3 9 1 6 8 10
  • 22. Foldable Prelude> F.foldl (+) 0 testTree 42 Prelude> F.foldl (*) 1 testTree 64800 Prelude> F.foldMap (x -> [x]) testTree [1,3,6,5,8,9,10] 위와 같이 트리의 모든 원소를 더한 값, 곱한 값을 Foldable의 foldl 함수를 이용해 아주 쉽게 구할 수 있습니다. 또, 맨 아래 예제처럼 주어진 트리를 리스트 로 바꾸는 것 역시 아주 쉽게 할 수 있죠. 5 3 9 1 6 8 10