@explorable-viz/fluid 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.spago/aff/v7.1.0/.editorconfig +13 -0
  2. package/.spago/aff/v7.1.0/.eslintrc.json +28 -0
  3. package/.spago/aff/v7.1.0/.gitignore +14 -0
  4. package/.spago/aff/v7.1.0/.tidyrc.json +10 -0
  5. package/.spago/argonaut-codecs/v9.1.0/.editorconfig +13 -0
  6. package/.spago/argonaut-codecs/v9.1.0/.gitignore +9 -0
  7. package/.spago/argonaut-codecs/v9.1.0/.tidyrc.json +10 -0
  8. package/.spago/arrays/v7.2.1/.eslintrc.json +26 -0
  9. package/.spago/arrays/v7.2.1/.gitignore +9 -0
  10. package/.spago/console/v6.1.0/.eslintrc.json +30 -0
  11. package/.spago/console/v6.1.0/.gitignore +9 -0
  12. package/.spago/debug/v6.0.2/.eslintrc.json +36 -0
  13. package/.spago/debug/v6.0.2/.gitignore +8 -0
  14. package/.spago/enums/v6.0.1/.gitignore +7 -0
  15. package/.spago/foreign-object/v4.1.0/.eslintrc.json +26 -0
  16. package/.spago/foreign-object/v4.1.0/.gitignore +8 -0
  17. package/.spago/free/v7.1.0/.gitignore +8 -0
  18. package/.spago/graphs/v8.1.0/.gitignore +7 -0
  19. package/.spago/js-uri/v3.1.0/.eslintrc.json +30 -0
  20. package/.spago/js-uri/v3.1.0/.gitignore +14 -0
  21. package/.spago/js-uri/v3.1.0/.tidyrc.json +10 -0
  22. package/.spago/node-buffer/v9.0.0/.eslintrc.json +26 -0
  23. package/.spago/node-buffer/v9.0.0/.gitignore +8 -0
  24. package/.spago/node-process/v11.2.0/.eslintrc.json +29 -0
  25. package/.spago/node-process/v11.2.0/.gitignore +8 -0
  26. package/.spago/node-streams/v9.0.0/.eslintrc.json +29 -0
  27. package/.spago/node-streams/v9.0.0/.gitignore +8 -0
  28. package/.spago/optparse/v6.0.0/.gitignore +8 -0
  29. package/.spago/optparse/v6.0.0/.npmrc +1 -0
  30. package/.spago/ordered-collections/v3.1.1/.gitignore +8 -0
  31. package/.spago/parallel/v7.0.0/.gitignore +7 -0
  32. package/.spago/parsing/v10.3.1/.editorconfig +13 -0
  33. package/.spago/parsing/v10.3.1/.gitignore +12 -0
  34. package/.spago/parsing/v10.3.1/.tidyoperators +231 -0
  35. package/.spago/parsing/v10.3.1/.tidyrc.json +10 -0
  36. package/.spago/prelude/v6.0.1/.eslintrc.json +26 -0
  37. package/.spago/prelude/v6.0.1/.gitignore +8 -0
  38. package/.spago/st/v6.2.0/.eslintrc.json +26 -0
  39. package/.spago/st/v6.2.0/.gitignore +8 -0
  40. package/.spago/strings/v6.0.1/.eslintrc.json +26 -0
  41. package/.spago/strings/v6.0.1/.gitignore +8 -0
  42. package/.spago/tailrec/v6.1.0/.gitignore +7 -0
  43. package/.spago/web-html/v4.1.0/.eslintrc.json +29 -0
  44. package/.spago/web-html/v4.1.0/.gitignore +8 -0
  45. package/.spago/web-xhr/v5.0.1/.eslintrc.json +29 -0
  46. package/.spago/web-xhr/v5.0.1/.gitignore +8 -0
  47. package/dist/fluid/fluid/lib/graphics.fld +130 -102
  48. package/dist/fluid/fluid/lib/matrix.fld +1 -1
  49. package/dist/fluid/fluid/lib/prelude.fld +50 -12
  50. package/dist/fluid/fluid/lib/stats.fld +6 -6
  51. package/dist/fluid/shared/fluid.mjs +21454 -26011
  52. package/dist/fluid/shared/load-figure.js +30116 -29574
  53. package/dist/fluid/shared/webtest-lib.js +3385 -3389
  54. package/package.json +1 -1
  55. package/website/article/.DS_Store +0 -0
  56. package/website/article/convolution/index.html +1 -1
  57. package/website/article/css/styles.css +4 -0
  58. package/website/article/css/view-styles.css +9 -2
  59. package/website/article/dataset/.DS_Store +0 -0
  60. package/website/article/energy-scatter/index.html +1 -1
  61. package/website/article/fluid/1805.02474v1-10.fld +21 -21
  62. package/website/article/fluid/bar-chart-line-chart.fld +7 -5
  63. package/website/article/fluid/convolution.fld +1 -1
  64. package/website/article/fluid/moving-average.fld +4 -4
  65. package/website/article/fluid/non-renewables.fld +7 -5
  66. package/website/article/index.html +19 -7
  67. package/website/article/methane/index.html +1 -1
  68. package/website/article/moving-average/index.html +1 -1
  69. package/website/article/non-renewables/index.html +1 -1
  70. package/website/article/renewables-linked/index.html +1 -1
  71. package/website/article/scigen-1805.02474v1-10/index.html +1 -1
  72. package/website/article/test.mjs +5 -5
  73. package/dist/fluid/fluid/lib/pi.fld +0 -1
  74. package/website/article/dataset/annual-temp-anomaly.json +0 -413
  75. package/website/article/dataset/ccra3-risks.json +0 -2015
@@ -1,18 +1,26 @@
1
+ # type Colour = Str
2
+ # type Colours = List Colour
3
+ # type Cat = Str
4
+
5
+ # Group has location (0, 0) because it doesn't interfere with positioning of its children.
6
+ # GraphicsElement -> Point
1
7
  def coords(Group(gs)): Point(0, 0)
2
8
  def coords(Rect(x, y, _, _, _)): Point(x, y)
3
9
  def coords(String(x, y, _, _, _)): Point(x, y)
4
- def coords(Viewport(x, y, _, _, _, _, _, _, _)): Point(x, y);
10
+ def coords(Viewport(x, y, _, _, _, _, _, _, _)): Point(x, y)
5
11
 
12
+ # GraphicsElement -> Float
6
13
  def get_x(g):
7
- def Point(x, _): coords(g);
8
-
9
- x;
14
+ def Point(x, _): coords(g)
15
+ x
10
16
 
17
+ # GraphicsElement -> Float
11
18
  def get_y(g):
12
- def Point(_, y): coords(g);
13
-
14
- x;
19
+ def Point(_, y): coords(g)
20
+ y
15
21
 
22
+ # Want some kind of typeclass mechanism plus record accessors/updaters.
23
+ # Float -> GraphicsElement -> GraphicsElement
16
24
  def set_x(x, Group(gs)):
17
25
  error("Group has immutable coordinates")
18
26
  def set_x(x, Rect(_, y, w, h, fill)):
@@ -20,11 +28,16 @@ def set_x(x, Rect(_, y, w, h, fill)):
20
28
  def set_x(x, String(_, y, str, anchor, baseline)):
21
29
  String(x, y, str, anchor, baseline)
22
30
  def set_x(x, Viewport(_, y, w, h, fill, margin, scale, translate, g)):
23
- Viewport(x, y, w, h, fill, margin, scale, translate, g);
31
+ Viewport(x, y, w, h, fill, margin, scale, translate, g)
24
32
 
33
+ # (Point, Point) -> Point
25
34
  def dimensions2((Point(x1, y1), Point(x2, y2))):
26
- Point(max(x1, x2), max(y1, y2));
35
+ Point(max(x1, x2), max(y1, y2))
27
36
 
37
+ # For Group, dimensions are relative to implicit coords of (0, 0), since a Group's children are effectively
38
+ # positioned relative to parent of Group. For Polymarker, will probably have to ignore the markers themselves,
39
+ # since they are scale-invariant.
40
+ # GraphicsElement -> Point
28
41
  def dimensions(Group(gs)):
29
42
  foldl(curry(dimensions2), Point(0, 0), map(coords_op, gs))
30
43
  def dimensions(Polyline(ps, _, _)):
@@ -32,25 +45,28 @@ def dimensions(Polyline(ps, _, _)):
32
45
  def dimensions(Rect(_, _, w, h, _)): Point(w, h)
33
46
  def dimensions(String(_, _, _, _, _)): Point(0, 0)
34
47
  def dimensions(Viewport(_, _, w, h, _, _, _, _, _)): Point(w, h)
48
+
35
49
  def coords_op(g):
36
50
  def (Point(x, y), Point(w, h)):
37
- prod(coords, dimensions, g);
38
-
39
- Point(x + w, y + h);
51
+ prod(coords, dimensions, g)
52
+ Point(x + w, y + h)
40
53
 
54
+ # GraphicsElement -> Float
41
55
  def width(g):
42
- def Point(w, _): dimensions(g);
43
-
44
- w;
56
+ def Point(w, _): dimensions(g)
57
+ w
45
58
 
59
+ # GraphicsElement -> Float
46
60
  def height(g):
47
- def Point(_, h): dimensions(g);
48
-
49
- h;
61
+ def Point(_, h): dimensions(g)
62
+ h
50
63
 
64
+ # Float -> Float -> List GraphicsElement -> List GraphicsElement
51
65
  def spaceRight(z, sep, gs):
52
- zipWith(set_x, iterate(length(gs), (+)(sep), z), gs);
66
+ zipWith(set_x, iterate(length(gs), (+)(sep), z), gs)
53
67
 
68
+ # Bake colour decisions into the library for the time being. Provide two palettes, so we can have two
69
+ # different sets of categorical values (e.g. countries and energy types). Palettes from colorbrewer2.org
54
70
  def colours1: [
55
71
  "#66c2a5",
56
72
  "#a6d854",
@@ -60,7 +76,7 @@ def colours1: [
60
76
  "#b3b3b3",
61
77
  "#8da0cb",
62
78
  "#e78ac3"
63
- ];
79
+ ]
64
80
 
65
81
  def colours2: [
66
82
  "#e41a1c",
@@ -71,176 +87,188 @@ def colours2: [
71
87
  "#ffff33",
72
88
  "#a65628",
73
89
  "#f781bf"
74
- ];
90
+ ]
75
91
 
92
+ # Compositionality principle: child coords/dimensions are always expressed directly using parent reference
93
+ # frame, to avoid depending on content of child, and so are not themselves scaled. Polyline can't be scaled
94
+ # directly because it inherits its frame of reference from its parent. For Viewport, margin will shrink the
95
+ # available area, possibly to zero, at which point nothing will be rendered.
96
+ # Float -> GraphicsElement -> GraphicsElement
76
97
  def scaleToWidth(w, Rect(x, y, _, h, fill)):
77
98
  Rect(x, y, w, h, fill)
78
99
  def scaleToWidth(w, Viewport(x, y, w0, h, fill, margin, Scale(x_scale, y_scale), translate, g)):
79
- def scale:
80
- Scale((x_scale * w) / w0, y_scale);
81
-
82
- Viewport(x, y, w, h, fill, margin, scale, translate, g);
100
+ def scale: Scale((x_scale * w) / w0, y_scale)
101
+ Viewport(x, y, w, h, fill, margin, scale, translate, g)
83
102
 
103
+ # Float -> List GraphicsElement -> List GraphicsElement
84
104
  def stackRight(sep, gs):
85
- map(scaleToWidth(1 - sep), spaceRight(sep / 2, 1, gs));
105
+ map(scaleToWidth(1 - sep), spaceRight(sep / 2, 1, gs))
86
106
 
107
+ # Float -> List GraphicsElement -> GraphicsElement
87
108
  def groupRight(sep, gs):
88
- Viewport(0, 0, length(gs), maximum(map(height, gs)), "none", 0, Scale(1, 1), Translate(0, 0), Group(stackRight(sep, gs)));
109
+ Viewport(0, 0, length(gs), maximum(map(height, gs)), "none", 0, Scale(1, 1), Translate(0, 0), Group(stackRight(sep, gs)))
89
110
 
111
+ # Heuristic saying how often to place a tick on an axis of length n.
112
+ # Float -> Float
90
113
  def tickEvery(n):
91
- def m:
92
- floor(logBase(10, n));
114
+ def m: floor(logBase(10, n))
93
115
 
94
116
  if n <= 2 * 10 ** m: 2 * 10 ** (m - 1)
95
- else: 10 ** m;
117
+ else: 10 ** m
96
118
 
97
119
  def axisStrokeWidth: 0.5
98
120
  def axisColour: "black"
99
121
  def backgroundColour: "white"
100
122
  def defaultMargin: 24
101
123
  def markerRadius: 3.5
102
- def tickLength: 4;
124
+ def tickLength: 4
103
125
 
126
+ # Helpers for axis functions.
127
+ # Orient -> Colour -> Float -> GraphicsElement
104
128
  def tick(Horiz, colour, len):
105
129
  Line(Point(0, 0), Point(0, 0 - len), colour, axisStrokeWidth)
106
130
  def tick(Vert, colour, len):
107
- Line(Point(0, 0), Point(0 - len, 0), colour, axisStrokeWidth);
131
+ Line(Point(0, 0), Point(0 - len, 0), colour, axisStrokeWidth)
108
132
 
133
+ # Orient -> Float -> Float -> Str -> GraphicsElement
109
134
  def label(Horiz, x, distance, str):
110
135
  String(x, (0 - distance) - 4, str, "middle", "hanging")
111
136
  def label(Vert, x, distance, str):
112
- String(0 - distance, x, str, "end", "central");
137
+ String(0 - distance, x, str, "end", "central")
113
138
 
139
+ # Orient -> Colour -> Float -> Str -> GraphicsElement
114
140
  def labelledTick(orient, colour, len, str):
115
141
  Group([
116
142
  tick(orient, colour, len),
117
143
  label(orient, 0, len, str)
118
- ]);
144
+ ])
119
145
 
146
+ # Orient -> Float -> Float -> Point
120
147
  def mkPoint(Horiz, x, y): Point(y, x)
121
- def mkPoint(Vert, x, y): Point(x, y);
148
+ def mkPoint(Vert, x, y): Point(x, y)
122
149
 
150
+ # x is position of this axis on the other axis. Returns axis and position of last tick.
151
+ # Orient -> Float -> Float -> Float -> GraphicsElement
123
152
  def axis(orient, x, start, end):
124
- def tickSp:
125
- tickEvery(end - start)
126
- def firstTick:
127
- ceilingToNearest(start, tickSp)
128
- def lastTick:
129
- ceilingToNearest(end, tickSp)
130
- def n:
131
- floor((end - firstTick) / tickSp) + 1
132
- def ys:
133
- iterate(n, (+)(tickSp), firstTick)
153
+ def tickSp: tickEvery(end - start)
154
+ def firstTick: ceilingToNearest(start, tickSp)
155
+ def lastTick: ceilingToNearest(end, tickSp)
156
+ def n: floor((end - firstTick) / tickSp) + 1
157
+ def ys: iterate(n, (+)(tickSp), firstTick)
134
158
  def ys:
135
- match firstTick > start:
136
- case True: start :| ys
137
- case False: ys
159
+ if firstTick > start: start :| ys
160
+ else: ys
138
161
  def ys:
139
- match lastTick > end:
140
- case True:
141
- concat2(ys, [lastTick])
142
- case False: ys
143
- def ps:
144
- map(mkPoint(orient, x), ys)
162
+ if lastTick > end: concat2(ys, [lastTick])
163
+ else: ys
164
+ def ps: map(mkPoint(orient, x), ys)
145
165
  def ax:
146
166
  Group([
147
167
  Line(head(ps), last(ps), axisColour, axisStrokeWidth),
148
168
  Polymarkers(ps, flip(map, ys, compose(labelledTick(orient, axisColour, tickLength), numToStr)))
149
- ]);
169
+ ])
150
170
 
151
- (ax, lastTick);
171
+ (ax, lastTick)
152
172
 
173
+ # x is position of this axis on the other axis.
174
+ # Orient -> Float -> List Cat -> GraphicsElement
153
175
  def catAxis(orient, x, catValues):
154
- def ys:
155
- iterate(length(catValues) + 1, (+)(1), 0)
156
- def ps:
157
- map(mkPoint(orient, x), ys);
176
+ def ys: iterate(length(catValues) + 1, (+)(1), 0)
177
+ def ps: map(mkPoint(orient, x), ys)
158
178
 
159
179
  Group([
160
180
  Line(head(ps), last(ps), axisColour, axisStrokeWidth),
161
181
  Polymarkers(tail(ps), map(const(tick(orient, axisColour, tickLength)), catValues)),
162
- Polymarkers(flip(map, tail(ps), (lambda Point(x, y): Point(x - 0.5, y))), map(label(orient, -0.5, 0), catValues))
163
- ]);
182
+ Polymarkers(flip(map, tail(ps), lambda Point(x, y): Point(x - 0.5, y)), map(label(orient, -0.5, 0), catValues))
183
+ ])
164
184
 
185
+ # Float -> Float -> Float -> Float -> List GraphicsElement -> GraphicsElement
165
186
  def viewport(x_start, x_finish, y_finish, margin, gs):
166
- Viewport(0, 0, x_finish - x_start, y_finish, backgroundColour, margin, Scale(1, 1), Translate(0 - x_start, 0), Group(gs));
187
+ Viewport(0, 0, x_finish - x_start, y_finish, backgroundColour, margin, Scale(1, 1), Translate(0 - x_start, 0), Group(gs))
167
188
 
189
+ # Plot a map of x values to lists of (categorical value, y value) pairs. Importantly, assume all data is uniform
190
+ # (categorical keys are the same for each x value and are ordered the same each time).
191
+ # Bool -> Colours -> Float -> List (Float, List (Cat, Float)) -> GraphicsElement
168
192
  def lineChart(withAxes, colours, x_start, data):
169
193
  def xs: map(fst, data)
170
- def nCat:
171
- length(snd(head(data)));
194
+ def nCat: length(snd(head(data)))
172
195
 
196
+ # (Int, Colour) -> GraphicsElement
173
197
  def plot((n, colour)):
174
198
  def ps:
175
- map((lambda (x, kvs): Point(x, snd(nth(n, kvs)))), data);
199
+ map(lambda (x, kvs): Point(x, snd(nth(n, kvs))), data)
176
200
 
177
201
  Group([
178
202
  Polyline(ps, colour, 1),
179
203
  Polymarkers(ps, repeat(length(ps), Circle(0, 0, markerRadius, colour)))
180
- ]);
204
+ ])
181
205
 
206
+ # List GraphicsElement
182
207
  def lines:
183
208
  zipWith(curry(plot), iterate(nCat, (+)(1), 0), colours)
184
209
  def x_finish: last(xs)
185
210
  def y_finish:
186
- maximum(flip(map, data, (lambda (_, kvs): maximum(map(snd, kvs)))));
187
-
188
- match withAxes:
189
- case True:
190
- def (x_axis, x_finish):
191
- axis(Horiz, 0, x_start, x_finish)
192
- def (y_axis, y_finish'):
193
- axis(Vert, x_start, 0, y_finish);
211
+ maximum(flip(map, data, lambda (_, kvs): maximum(map(snd, kvs))))
194
212
 
195
- viewport(x_start, x_finish, y_finish', defaultMargin, x_axis :| y_axis :| lines)
196
- case False:
197
- viewport(x_start, x_finish, y_finish, 0, lines);
213
+ if withAxes:
214
+ def (x_axis, x_finish): axis(Horiz, 0, x_start, x_finish)
215
+ def (y_axis, y_finish_): axis(Vert, x_start, 0, y_finish)
216
+ viewport(x_start, x_finish, y_finish_, defaultMargin, x_axis :| y_axis :| lines)
217
+ else:
218
+ viewport(x_start, x_finish, y_finish, 0, lines)
198
219
 
220
+ # Plot a chart of categorical values on the x-axis and renderings of the corresponding a-value on the y-axis.
221
+ # (Colours -> List a -> GraphicsElement) -> Bool -> Colours -> Float -> List (Cat, a) -> GraphicsElement
199
222
  def categoricalChart(plotValue, withAxes, colours, sep, data):
200
223
  def gs:
201
224
  stackRight(sep, plotValue(colours, map(snd, data)))
202
225
  def w: length(gs)
203
- def h:
204
- maximum(map(height, gs));
205
-
206
- match withAxes:
207
- case True:
208
- def x_axis:
209
- catAxis(Horiz, 0, map(fst, data))
210
- def (y_axis, h'): axis(Vert, 0, 0, h);
226
+ def h: maximum(map(height, gs))
211
227
 
212
- viewport(0, w, h', defaultMargin, concat2(gs, [x_axis, y_axis]))
213
- case False:
214
- viewport(0, w, h, 0, gs);
228
+ if withAxes:
229
+ def x_axis: catAxis(Horiz, 0, map(fst, data))
230
+ def (y_axis, h_): axis(Vert, 0, 0, h)
231
+ viewport(0, w, h_, defaultMargin, concat2(gs, [x_axis, y_axis]))
232
+ else:
233
+ viewport(0, w, h, 0, gs)
215
234
 
235
+ # Colours -> List a -> GraphicsElement
216
236
  def rects(colours, ns):
217
- zipWith((lambda colour, n: Rect(0, 0, 1, n, colour)), colours, ns);
237
+ zipWith(lambda colour, n: Rect(0, 0, 1, n, colour), colours, ns)
218
238
 
239
+ # First component of data (categorical value) currently ignored; values just mapped positionally to colors.
240
+ # Can we use Group instead of Viewport here?
241
+ # Colours -> List (a, Num) -> GraphicsElement
219
242
  def stackedBar(colours, ns):
220
243
  def heights: map(snd, ns)
221
- def subtotals:
222
- scanl1((+), 0, heights)
223
- def dims:
224
- zip(0 :| subtotals, heights)
244
+ def subtotals: scanl1((+), 0, heights)
245
+ def dims: zip(0 :| subtotals, heights)
225
246
  def rects:
226
- map((lambda ((y, height), colour): Rect(0, y, 1, height, colour)), zip(dims, colours));
247
+ map(lambda ((y, height), colour): Rect(0, y, 1, height, colour), zip(dims, colours))
227
248
 
228
- Viewport(0, 0, 1, last(subtotals), "none", 0, Scale(1, 1), Translate(0, 0), Group(rects));
249
+ Viewport(0, 0, 1, last(subtotals), "none", 0, Scale(1, 1), Translate(0, 0), Group(rects))
229
250
 
251
+ # Bool -> Colours -> Float -> List (a, Float) -> GraphicsElement
230
252
  def barChart:
231
- categoricalChart(rects);
253
+ categoricalChart(rects)
232
254
 
255
+ # For each categorical value of type a, plot a bar chart for the corresponding b-indexed data.
256
+ # Bool -> Colours -> Float -> List (a, List (b, Float)) -> GraphicsElement
233
257
  def groupedBarChart:
234
- categoricalChart(compose(map, flip(barChart(False), 0)));
258
+ categoricalChart(compose(map, flip(barChart(False), 0)))
235
259
 
260
+ # See stackedBar for strong (unjustified) assumption about uniformity of data.
261
+ # Bool -> Colours -> Num -> List (a, List (b, Num)) -> GraphicsElement
236
262
  def stackedBarChart:
237
- categoricalChart(compose(map, stackedBar));
263
+ categoricalChart(compose(map, stackedBar))
238
264
 
265
+ # Bit of a hack, but how text fits into our model is a bit unclear at the moment.
266
+ # Str -> GraphicsElement -> GraphicsElement
239
267
  def caption(str, Viewport(x, y, w, h, fill, margin, scale, translate, g)):
240
- def g':
268
+ def g_:
241
269
  Group([
242
270
  String(x + w / 2, -2, str, "middle", "hanging"),
243
271
  Viewport(0, 0, w, h, fill, margin, scale, translate, g)
244
- ]);
272
+ ])
245
273
 
246
- Viewport(x, y, w, h, backgroundColour, defaultMargin / 2 + 4, Scale(1, 1), Translate(0, 0), g');
274
+ Viewport(x, y, w, h, backgroundColour, defaultMargin / 2 + 4, Scale(1, 1), Translate(0, 0), g_)
@@ -36,5 +36,5 @@ def matMul(a, b):
36
36
  if not(n == i):
37
37
  error("Dimensions don't line up")
38
38
  else:
39
- @doc("""Intermediate matrix""")
39
+ @doc(f"""Intermediate matrix""")
40
40
  [| sum([a!(i_, k) * b!(k, j_) for k in enumFromTo(1, n)]) for (i_, j_) in (m, j) |]
@@ -1,12 +1,15 @@
1
1
  # "Num" throughout means (Int + Float).
2
2
 
3
+ # To resolve in new semantics: expected dependencies don't arise for and/or unless they make a fresh value.
3
4
  # Bool -> Bool
4
5
  def and(False, y): False
5
- def and(True, y): y
6
+ def and(True, True): True
7
+ def and(True, False): False
6
8
 
7
9
  # Bool -> Bool
8
10
  def or(True, y): True
9
- def or(False, y): y
11
+ def or(False, True): True
12
+ def or(False, False): False
10
13
 
11
14
  # Bool -> Bool
12
15
  def not(True): False
@@ -19,6 +22,10 @@ def compare(x, y):
19
22
  if x < y: LT
20
23
  else: EQ
21
24
 
25
+ # (b -> b -> c) -> (a -> b) -> a -> a -> c
26
+ def on(binOp, prop, x, y):
27
+ binOp(prop(x), prop(y))
28
+
22
29
  # Num -> Num
23
30
  def negate: (-)(0)
24
31
 
@@ -83,20 +90,23 @@ def elem(x, y :| xs):
83
90
  x == y |or| elem(x, xs)
84
91
 
85
92
  # (a -> Bool) -> List a -> Option a
86
- def find(p, []): error("not found")
93
+ def find(p, []): None
87
94
  def find(p, x :| xs):
88
95
  if p(x): Some(x)
89
96
  else: find(p, xs)
90
97
 
91
98
  # String -> String -> List Dict -> Option Dict
92
- def findWithKey(fname, fval, d):
93
- find((lambda y: y[fname] == fval), d)
99
+ def findWithKey(k, v, rs):
100
+ find(lambda y: y[k] == v, rs)
94
101
 
95
102
  # Option a -> a
96
- def fromSome(None):
97
- error("Expected Some!")
103
+ def fromSome(None): error("Expected Some!")
98
104
  def fromSome(Some(x)): x
99
105
 
106
+ # Option a -> a -> a
107
+ def fromOption(None, y): y
108
+ def fromOption(Some(x), _): x
109
+
100
110
  # (a -> Bool) -> List a -> List a
101
111
  def filter(p, []): []
102
112
  def filter(p, x :| xs):
@@ -113,6 +123,30 @@ def filterMap(p, x :| xs):
113
123
  case Some(y):
114
124
  y :| filterMap(f, xs)
115
125
 
126
+ # Equivalent to but more efficient than length ∘ filter(p)
127
+ def count_if(p):
128
+ def f(n, x):
129
+ if p(x): n + 1
130
+ else: n
131
+ foldl(f, 0)
132
+
133
+ # Split list into longest initial segment whose elements satisfy a predicate, and the rest.
134
+ # (a -> Bool) -> List a -> { init :: List a, rest :: List a }
135
+ def span(p, Nil): { init: Nil, rest: Nil }
136
+ def span(p, x :| xs_):
137
+ if p(x):
138
+ def { init: ys, rest: zs }: span(p, xs_)
139
+ { init: x :| ys, rest: zs }
140
+ else:
141
+ { init: Nil, rest: x :| xs_ }
142
+
143
+ # Each returned list is non-empty.
144
+ # (a -> a -> Bool) -> List a -> List (List a)
145
+ def groupBy(eq, Nil): Nil
146
+ def groupBy(eq, x :| xs):
147
+ def { init: ys, rest: zs }: span(eq(x), xs)
148
+ (x :| ys) :| groupBy(eq, zs)
149
+
116
150
  # (a -> b -> a) -> a -> List b -> a
117
151
  def foldl(op, z, []): z
118
152
  def foldl(op, z, x :| xs):
@@ -135,9 +169,7 @@ def foldr1(op, x :| y :| xs):
135
169
  def scanl1(op, z, xs):
136
170
  def go(x, continue, acc):
137
171
  def next: op(acc, x)
138
-
139
172
  next :| continue(next)
140
-
141
173
  foldr(go, const([]), xs, z)
142
174
 
143
175
  # (a -> b -> a) -> a -> List b -> List a
@@ -177,8 +209,13 @@ def iterate(n, f, z):
177
209
  else:
178
210
  z :| map(f, iterate(n - 1, f, z))
179
211
 
180
- # List Int -> Int
181
- def sum: foldr((+), 0)
212
+ # List Num -> Num
213
+ def sum: foldl((+), 0)
214
+
215
+ # List Num -> Num
216
+ def mean(xs):
217
+ def (s, n): foldl(lambda (s, n), x: (s + x, n + 1), (0, 0), xs)
218
+ s / n
182
219
 
183
220
  # List a -> a
184
221
  def last([x]): x
@@ -286,7 +323,8 @@ def abs(x, y):
286
323
  def nub(xs):
287
324
  def nub_([], _): []
288
325
  def nub_(x :| xs, ys):
289
- if x |elem| ys: nub_(xs, ys)
326
+ if x |elem| ys:
327
+ nub_(xs, ys)
290
328
  else:
291
329
  x :| nub_(xs, x :| ys)
292
330
 
@@ -19,7 +19,7 @@ def mergesort(xs):
19
19
 
20
20
  def findQuantile(q, p, xs):
21
21
  def rank:
22
- (p / q) * (length(xs) - 1);
22
+ (p / q) * (length(xs) - 1)
23
23
 
24
24
  if rank == floor(rank):
25
25
  nth(rank, xs)
@@ -36,7 +36,7 @@ def accumBins(data, Nil): []
36
36
  def accumBins(data, [l]): []
37
37
  def accumBins(data, l :| r :| es):
38
38
  def (ge, le):
39
- splitOn((lambda x: x <= r), data)
39
+ splitOn(lambda x: x <= r, data)
40
40
  (le, r - l) :| accumBins(ge, r :| es)
41
41
 
42
42
  def cut(xs, nbins):
@@ -46,7 +46,7 @@ def cut(xs, nbins):
46
46
  def edges:
47
47
  [low + x * binwidth for x in enumFromTo(0, nbins)]
48
48
 
49
- accumBins(xs, edges);
49
+ accumBins(xs, edges)
50
50
 
51
51
  def qcut(xs, qs):
52
52
  def (low, high):
@@ -57,13 +57,13 @@ def qcut(xs, qs):
57
57
  accumBins(xs, edges)
58
58
 
59
59
  def likelihoodLE(xs, target):
60
- length(filter((lambda x: x <= target), xs)) / length(xs)
60
+ length(filter(lambda x: x <= target, xs)) / length(xs)
61
61
 
62
62
  def likelihoodGE(xs, target):
63
- length(filter((lambda x: x >= target), xs)) / length(xs)
63
+ length(filter(lambda x: x >= target, xs)) / length(xs)
64
64
 
65
65
  def likelihoodMap(table, prob):
66
- fromSome(find((lambda x: x.prob <= prob), table)).msg
66
+ fromSome(find(lambda x: x.prob <= prob, table)).msg
67
67
 
68
68
  def mkPercent(num):
69
69
  numToStr(num * 100) ++ "%"