1
+ /** A blue sky sketch how one might evolve extensions to do type classes
2
+ * without the current gap between functional and OO patterns.
3
+ * Largely inspired by the way Rust does it.
4
+ *
5
+ * The main additions (to be worked out in detail) is a self type `This` and
6
+ * a mechanism that a trait can abstract over companions of classes that implement it.
7
+ * Companion types and methods are declared using `static` for now, just to
8
+ * pick some familiar notation.
9
+ *
10
+ * Ideas about `This` (still vague at the moment)
11
+ *
12
+ * - Treat it as an additional abstract type in a trait, prefixed by the name of the trait
13
+ * - An implementing (non-trait) class binds the `This` types of all the traits it implements
14
+ * to itself.
15
+ * - Later subclasses do not re-bind `This` types already bound by their superclasses.
16
+ * (so in that sense, `This`-binding is like trait parameterization, the first implementing
17
+ * classes determines the self type and the parameters of a trait)
18
+ * - Paramerized classes have parameterized `This` types (e.g. Functor below).
19
+ */
20
+ import Predef .{any2stringadd => _ , _ }
21
+ object blueSkyExtensions {
22
+
23
+ // Semigroup and Monoid
24
+
25
+ trait SemiGroup {
26
+ def + (that : This ): This
27
+ }
28
+
29
+ trait Monoid extends SemiGroup {
30
+ static def unit : This
31
+ }
32
+
33
+ extension IntMonoid for Int : Monoid {
34
+ static def unit = 0
35
+ def + (that : Int ) = this + that
36
+ }
37
+
38
+ extension StringMonoid for String : Monoid {
39
+ static def unit = " "
40
+ def + (that : Int ) = this ++ that
41
+ }
42
+
43
+ def sum [T : Monoid ](xs : List [T ]): T =
44
+ (instance[T , Monoid ].unit /: xs)(_ `add` _)
45
+
46
+ // Ord
47
+
48
+ trait Ord {
49
+ def compareTo (that : This ): Int
50
+ def < (that : This ) = compareTo < 0
51
+ def > (that : This ) = compareTo > 0
52
+ }
53
+
54
+ extension ListOrd [T : Ord ] for List [T ] : Ord {
55
+ def compareTo (that : List [T ]): Int = (this , that) match {
56
+ case (Nil , Nil ) => 0
57
+ case (Nil , _) => - 1
58
+ case (_, Nil ) => + 1
59
+ case (x :: xs, y :: ys) =>
60
+ val fst = x.compareTo(y)
61
+ if (fst != 0 ) fst else xs.compareTo(ys)
62
+ }
63
+ }
64
+
65
+ // Functor and Monad
66
+
67
+ trait Functor [A ] {
68
+ static def pure [A ]: This [A ]
69
+ def map [B ](f : A => B ): This [B ]
70
+ }
71
+
72
+ // Generically, `pure[A]{.map(f)}^n`
73
+ def develop [A , F [X ] : Functor [X ]](n : Int , f : A => A ): F [A ] =
74
+ if (n == 0 ) Functor .statics[F ].pure[A ]
75
+ else develop[A , F ](n - 1 , f).map(f)
76
+
77
+ trait Monad [A ] extends Functor [A ] {
78
+ def flatMap [B ](f : A => This [B ]): This [B ]
79
+ def map [B ](f : A => B ) = this .flatMap(f.andThen(pure))
80
+ }
81
+
82
+ extension ListMonad [T ] for List [T ] : Monad [T ] {
83
+ static def pure [A ] = Nil
84
+
85
+ def flatMap [B ](f : A => List [B ]): List [B ] = this match {
86
+ case x :: xs => f(x) ++ xs.flatMap(f)
87
+ case Nil => Nil
88
+ }
89
+ }
90
+
91
+ extension MonadFlatten [T [X ]: Monad [X ]] for T [T [A ]] {
92
+ def flatten : T [A ] = this .flatMap(identity)
93
+ }
94
+
95
+ // Iterables
96
+
97
+ trait MonoIterable [A ] {
98
+ static def empty : This [A ]
99
+ static def apply (xs : A * ): This [A ]
100
+
101
+ def filter (p : A => Boolean ): This [A ]
102
+ }
103
+
104
+ trait Iterable [A ] extends MonoIterable [A ] {
105
+ def map [B ](f : A => B ): This [B ]
106
+ def flatMap [B ](f : A => This [B ]): This [B ]
107
+ }
108
+
109
+ extension StringMonoIterable for String : MonoIterable [Char ] {
110
+ static type This [A ] = String
111
+ static def empty = " "
112
+ static def apply (xs : A * ) = xs.mkString
113
+
114
+ def filter (p : Char => Boolean ): String = ...
115
+ def map (f : Char => Char ): String = ...
116
+ }
117
+
118
+ extension StringIterable for String : Iterable [Char ] {
119
+ static type This [A ] = IndexedSeq [A ]
120
+
121
+ def map [B ](f : Char => B ): IndexedSeq [B ] = ...
122
+ def flatMap [B ](f : Char => IndexedSeq [B ]): IndexedSeq [B ] = ...
123
+ }
124
+
125
+ extension ListIterable [T ] for List [T ] : Iterable [T ] {
126
+ static type This [A ] = List [A ]
127
+ static def empty = Nil
128
+ static def apply (xs : A * ) = (xs /: Nil )(_ :: _)
129
+
130
+ def filter (p : T => Boolean ): List [T ] = ...
131
+ def map [B ](f : T => B ): List [B ] = ...
132
+ def flatMap [B ](f : T => List [B ]): List [B ] = ...
133
+ }
134
+ }
0 commit comments