1
+ package fpspeedrun
2
+
3
+ import scala .language .higherKinds
4
+ import syntax .semigroup ._
5
+
6
+ trait Monoid [T ] extends SemiGroup [T ] {
7
+ def empty : T
8
+ }
9
+
10
+ object Monoid {
11
+ def apply [T ](implicit i : Monoid [T ]) = i
12
+
13
+ object Laws {
14
+ def identity [T : Monoid ](x : T ): Boolean = {
15
+ (Monoid [T ].empty |+| x) == x &&
16
+ (x |+| Monoid [T ].empty) == x
17
+ }
18
+
19
+ def associativity [T : Monoid ](x : T , y : T , z : T ): Boolean = {
20
+ ((x |+| y) |+| z) == (x |+| (y |+| z))
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Функция, похожая на SemiGroup#combineList, но для Monoid.
26
+ * В отличие от предыдущей, возвращает не Option[T], а сам T.
27
+ * Для пустого списка, возвращает "единицу" в понятиях моноида.
28
+ */
29
+ def foldList [T : Monoid ](list : List [T ]): T = {
30
+ list.foldLeft(Monoid [T ].empty) {
31
+ case (sum, next) => sum |+| next
32
+ }
33
+ }
34
+
35
+ /**
36
+ * #foldList, но с крутым синтаксисом и возможностью через параметр типа передавать
37
+ * желаемое поведение.
38
+ * Паттерн для синтаксиса называется: partial type application
39
+ */
40
+ def foldListVia [U [_]] = new FoldListVia [U ]
41
+ class FoldListVia [U [_]] {
42
+ def apply [T ](list : List [T ])(implicit iso : Iso [T , U [T ]], monoid : Monoid [U [T ]]): T = {
43
+ val r = list.foldLeft(monoid.empty) {
44
+ (acc, next) => acc |+| iso.wrap(next)
45
+ }
46
+ iso.unwrap(r)
47
+ }
48
+ }
49
+
50
+ // monoid instances
51
+ implicit val defaultMonoidInt : Monoid [Int ] = new Monoid [Int ] {
52
+ override def empty : Int = 0
53
+ override def combine (x : Int , y : Int ): Int = x+ y
54
+ }
55
+ implicit val sumMonoidInt : Monoid [Sum [Int ]] = new Monoid [Sum [Int ]] {
56
+ override def empty : Sum [Int ] = Sum (0 )
57
+ override def combine (x : Sum [Int ], y : Sum [Int ]): Sum [Int ] = Sum (x.x + y.x)
58
+ }
59
+ implicit val prodMonoidInt : Monoid [Prod [Int ]] = new Monoid [Prod [Int ]] {
60
+ override def empty : Prod [Int ] = Prod (1 )
61
+ override def combine (x : Prod [Int ], y : Prod [Int ]): Prod [Int ] = Prod (x.x * y.x)
62
+ }
63
+ }
0 commit comments