일전에 scala function 관련 post에서 Monoid에 관하여 기제한 적이 있다. 이를 보면 Cats의 Monoid and Semigroup은 이해하기 쉽다. 같은 내용이니 말이다. 다만 Cats에서 이를 어떻게 사용자 코드에 적용하는 지만 알면 된다.
Monoid & Semigroup
Monoid의 정의는 어떤 집합의 원소들에 대한 결합법칙을 만족하는 항등원과 이항연산이 있으며, 이 이항연산의 결과 또한 그 집합의 원소이다.
그래서 Monoid정의시 다음과 같이 type class를 정의 했었다.
Cats에서는 zero를 empty, op를 combine이라 부른다. 그래서 다음과 같이 정의 한다.
그런데 Cats 에서 어떠한 유형에 대하여는 항등원을 가질 수 없는 유형이 있기 때문에 다음과 같이 type class를 두었다.
operator associativity & identity law
Monoid 만들어 보기
Boolean type에 대한 and 연산자에 대한 monoid와 or연산자에 대한 monoid를 만들어 보자
Set 에 대한 union 연산은 결합법칙이 성립하므로 다음과 같이 정의 할 수 있다.
하지만 차집합 연산인 intersect 는 결합법칙이 성립하지 않는다.
그리고 empty 도 정의 할 수 없다.
그러나 대칭차집합인 경우 즉 (A - B) uion (B - A ) 는 결합법칙이 성립한다. 즉 (A - B) - C == A - (B - C) 이 성립한다. 따라서 다음과 같이 정의 할 수 있다.
Cats Monoid type class
Monoid 와 Semigroup 의 type class 는 cats.kernel.Monoid, cats.kernel.Semigroup에 정의 되어 있고 cats package에 type alias 로 선언 되어 있다.
위의 code는 cats pacakge object 에서 cats.kernel에 선언된 type class와 object에 대한 alias를 제공하고 있는 부분이다.
Cats Kernel은 Cats의 작은 하위 project로 Cats의 full toolbox가 필요없는 가벼운 몇몇 핵심 type class들을 제공한다. 하지만 이들은 모두 cats pacakge에 alias 로 선언되어 있고 대부분은 이곳에 정의 되어 있다.
Cats Monoid instances
cats.instance에 해당하는 유형별로 type class instance 가 있으며 해당하는 type의 암시자가 있다면 그 암시자를 반환 하는 apply method가 Monoid object에 있다.
Cats Monoid interface syntax
combine method는 Semigroup의 method이며 Semigroup interface syntax에 |+| method를 제공하는 SemigroupOpt 생성 암시자가 있다. 따라서 다음과 같이 사용하면 된다.
type parameter 생성자를 같는 Option같은 type 인 경우는 해당하는 type과 type parameter에 해당하는 type 의 instance가 scope안에 있으면 된다.
exercise
Monoid를 이용하여 List에 담긴 항목의 add 연산자를 작성해 보자
Monoid를 이용한 위의 List[Option] 의 add연산를 보면 List의 항목에 Some, None이 같이 있기 때문에 이는 최종 List[Option] 유형이 된다. 만약 한가지 즉 List(Some(1), Some(2))처럼 되어 있다면 이는 List[Some]이 되어 type dispatch가 되어 compile error가 발생한다. 왜냐면 Monoid의 type parameter가 invariant 이기 때문이다.
Map 과 Tuple에 대한 예
Map안에 combine 연산의 대상 type Int이므로 cats.instances.map._ 뿐 아니라 cats.instances.int._ 도 import를 해야 한다.
Tuple 의 원소 유형이 String, Int 이므로 이에 대한 combine 연산이 된다. 따라서 import cats.instances.int._ 뿐아니라 import cats.instances.string._ 도 해야 한다.
다음은 Custom class 의 Monoid 을 작성한 예 이다.
Summary
Semigroup 은 combine 이라는 method를 가지며 이는 결합법칙이 성립 해야 했다.
Monoid는 Semigroup을 extends 하며 combine method에 대한 항등원을 제공하는 empty method를 가진다.
Semigroup과 Monoid를 사용하기 위해서는 3가지를 import해야 한다. type class, instances, |+| 연산자를 사용시 interface syntax를 import 해야 했다.
특정 type에 대한 combine 연산를 위해서는 해당하는 type instance를 scope에 사용할 수 있게 import 해야 한다. Map[String,Int]라면 cats.instances.map._ 뿐아니라 cats.instances.int._ 또한 있어야 한다.
Foldable에서 fold나 , foldRight, foleLeft 등에서 쉽게 |+| 연산자를 사용할 수 있었다.