forked from markhibberd/introduction-to-fp-in-scala
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathWriter.scala
162 lines (147 loc) · 4.3 KB
/
Writer.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package patterns
import intro._, EqualSyntax._
/*
* A writer data type that represents the pair of some
* writer content with the production of a value.
*/
case class Writer[W, A](log: W, value: A) {
def run: (W, A) =
(log, value)
/*
* Exercise 3.1:
*
* Implement map for Writer[W, A].
*
* The following laws must hold:
* 1) r.map(z => z) == r
* 2) r.map(z => f(g(z))) == r.map(g).map(f)
*
*/
def map[B](f: A => B): Writer[W, B] =
???
/*
* Exercise 3.2:
*
* Implement flatMap (a.k.a. bind, a.k.a. >>=).
*
* The following law must hold:
* r.flatMap(f).flatMap(g) == r.flatMap(z => f(z).flatMap(g))
*
*/
def flatMap[B](f: A => Writer[W, B])(implicit M: Monoid[W]): Writer[W, B] =
???
}
object Writer {
/*
* Exercise 3.3:
*
* Implement value (a.k.a. return, point, pure) given a
* Monoid for W.
*
* Hint: Try using Writer constructor.
*/
def value[W: Monoid, A](a: A): Writer[W, A] =
???
/*
* Exercise 3.4:
*
* Implement tell.
*
* Tell appends the writer content w and produces no value.
*
* Hint: Try using Writer constructor.
*/
def tell[W](w: W): Writer[W, Unit] =
???
/*
* Exercise 3.5:
*
* Sequence, a list of Readers, to a Reader of Lists.
*/
def sequence[W: Monoid, A](writers: List[Writer[W, A]]): Writer[W, List[A]] =
???
implicit def WriterMonad[W: Monoid]: Monad[Writer[W, ?]] =
new Monad[Writer[W, ?]] {
def point[A](a: => A): Writer[W, A] = value[W, A](a)
def bind[A, B](a: Writer[W, A])(f: A => Writer[W, B]): Writer[W, B] = a flatMap f
}
implicit def WriterEqual[W: Equal, A: Equal]: Equal[Writer[W, A]] =
Equal.from[Writer[W, A]]((a, b) => (a.log -> a.value) === (b.log -> b.value))
implicit def WriterMoniod[W: Monoid, A: Monoid]: Monoid[Writer[W, A]] =
new Monoid[Writer[W, A]] {
def identity: Writer[W, A] =
Writer.value[W, A](Monoid[A].identity)
def op(l: Writer[W, A], r: Writer[W, A]) =
Writer(Monoid[W].op(l.log, r.log), Monoid[A].op(l.value, r.value))
}
}
/*
* *Challenge* Exercise 3.6: Stocks + Stats.
*
* We have some stock prices over time, and we make a simple
* adjustment:
* Map across each ticker price and do an adjustment by adding
* 1000 cents to every value under 10000 and 10 cents to every
* value equal to or over 10000.
*
* However, while we compute this answer we also want to caculate
* summary statistics for our data, specifically, min, max, total,
* and count.
*
* Use the Writer data type to compute stats whilst we calculate
* our adjustments.
*
* Complete the implementation, some of the methods are provided
* fill in the remainder, to complete the spec.
*/
object WriterExample {
case class Stats(min: Int, max: Int, total: Int, count: Int)
case class Stock(ticker: String, date: String, cents: Int)
/**
* Implement our algorthim.
*
* Hint: Writer(W, A) and Writer.sequence will be useful here.
*/
def stocks(data: List[Stock]): (Stats, List[Stock]) =
???
/**
* A monoid for Stats.
*/
implicit def StatsMonoid: Monoid[Stats] =
new Monoid[Stats] {
def identity =
???
def op(l: Stats, r: Stats) =
???
}
def exampledata = List(
Stock("FAKE", "2012-01-01", 10000)
, Stock("FAKE", "2012-01-02", 10020)
, Stock("FAKE", "2012-01-03", 10022)
, Stock("FAKE", "2012-01-04", 10005)
, Stock("FAKE", "2012-01-05", 9911)
, Stock("FAKE", "2012-01-06", 6023)
, Stock("FAKE", "2012-01-07", 7019)
, Stock("CAKE", "2012-01-01", 1)
, Stock("CAKE", "2012-01-02", 2)
, Stock("CAKE", "2012-01-03", 3)
, Stock("CAKE", "2012-01-04", 4)
, Stock("CAKE", "2012-01-05", 5)
, Stock("CAKE", "2012-01-06", 6)
, Stock("CAKE", "2012-01-07", 7)
, Stock("BAKE", "2012-01-01", 99999)
, Stock("BAKE", "2012-01-02", 99999)
, Stock("BAKE", "2012-01-03", 99999)
, Stock("BAKE", "2012-01-04", 99999)
, Stock("BAKE", "2012-01-05", 99999)
, Stock("BAKE", "2012-01-06", 99999)
, Stock("BAKE", "2012-01-07", 99999)
, Stock("LAKE", "2012-01-01", 10012)
, Stock("LAKE", "2012-01-02", 7000)
, Stock("LAKE", "2012-01-03", 1234)
, Stock("LAKE", "2012-01-04", 10)
, Stock("LAKE", "2012-01-05", 6000)
, Stock("LAKE", "2012-01-06", 6099)
, Stock("LAKE", "2012-01-07", 5999)
)
}