1before:
2  this_module  = "std.list"
3  global_table = "_G"
4
5  exported_apis = { "append", "compare", "concat", "cons", "depair",
6                    "elems", "enpair", "filter", "flatten", "foldl",
7                    "foldr", "index_key", "index_value", "map",
8                    "map_with", "project", "relems", "rep", "reverse",
9                    "shape", "sub", "tail", "transpose", "zip_with" }
10
11  M = require (this_module)
12
13  List    = M {}
14  l       = List {"foo", "bar", "baz"}
15
16
17
18specify std.list:
19- context when required:
20  - context by name:
21    - it does not touch the global table:
22        expect (show_apis {added_to="_G", by="std.list"}).
23          to_equal {}
24    - it exports the documented apis:
25        t = {}
26        for k in pairs (M) do t[#t + 1] = k end
27        expect (t).to_contain.a_permutation_of (exported_apis)
28
29  - context via the std module:
30    - it does not touch the global table:
31        expect (show_apis {added_to=global_table, by="std"}).
32          to_equal {}
33
34- describe construction:
35  - context from List clone method:
36    - it constructs a new list:
37        l = List:clone {}
38        expect (l).not_to_be (List)
39        expect (prototype (l)).to_be "List"
40    - it reuses the List metatable:
41        l, m = List:clone {"l"}, List:clone {"m"}
42        expect (getmetatable (l)).to_be (getmetatable (m))
43    - it initialises List with constructor parameters:
44        m = List:clone {"foo", "bar", "baz"}
45        expect (m).to_equal (l)
46    - it serves as a prototype for new instances:
47        m = l:clone {}
48        expect (prototype (m)).to_be "List"
49        expect (m).to_equal (l)
50        expect (getmetatable (m)).to_be (getmetatable (l))
51
52  # List {args} is just syntactic sugar for List:clone {args}
53  - context from List object prototype:
54    - it constructs a new List:
55        l = List {}
56        expect (l).not_to_be (List)
57        expect (prototype (l)).to_be "List"
58    - it reuses the List metatable:
59        l, m = List {"l"}, List {"m"}
60        expect (getmetatable (l)).to_be (getmetatable (m))
61    - it initialises List with constructor parameters:
62        m = List {"foo", "bar", "baz"}
63        expect (m).to_equal (l)
64    - it serves as a prototype for new instances:
65        m = l {}
66        expect (prototype (m)).to_be "List"
67        expect (m).to_equal (l)
68        expect (getmetatable (m)).to_be (getmetatable (l))
69
70
71- describe metatable propagation:
72  - it reuses the metatable for List constructed objects:
73      m = List {"foo", "bar"}
74      expect (getmetatable (m)).to_be (getmetatable (l))
75
76
77- describe append:
78  - before:
79      f = M.append
80
81  - context with bad arguments:
82      badargs.diagnose (f, "std.list.append (List, any)")
83
84  - context as a module function:
85    - it returns a List object:
86        expect (prototype (f (l, "quux"))).to_be "List"
87    - it works for an empty List:
88        expect (f (List {}, "quux")).to_equal (List {"quux"})
89    - it appends an item to a List:
90        expect (f (l, "quux")).
91          to_equal (List {"foo", "bar", "baz", "quux"})
92
93  - context as an object method:
94    - before:
95        f = l.append
96
97    - it returns a List object:
98        expect (prototype (f (l, "quux"))).to_be "List"
99    - it works for an empty List:
100        expect (f (List {}, "quux")).to_equal (List {"quux"})
101    - it appends an item to a List:
102        expect (f (l, "quux")).
103          to_equal (List {"foo", "bar", "baz", "quux"})
104
105  - context as a List metamethod:
106    - it returns a List object:
107        expect (prototype (l + "quux")).to_be "List"
108    - it works for an empty list:
109        expect (List {} + "quux").to_equal (List {"quux"})
110    - it appends an item to a list:
111        expect (l + "quux").
112          to_equal (List {"foo", "bar", "baz", "quux"})
113
114
115- describe compare:
116  - before:
117      a, b = List {"foo", "bar"}, List {"foo", "baz"}
118
119      f = M.compare
120
121  - context with bad arguments:
122      badargs.diagnose (f, "std.list.compare (List, List|table)")
123
124  - context as a module function:
125    - it returns -1 when the first list is less than the second:
126        expect (f (a, {"foo", "baz"})).to_be (-1)
127        expect (f (a, List {"foo", "baz"})).to_be (-1)
128    - it returns -1 when the second list has additional elements:
129        expect (f (List {"foo"}, {"foo", "bar"})).to_be (-1)
130        expect (f (List {"foo"}, List {"foo", "bar"})).to_be (-1)
131    - it returns 0 when two lists are the same:
132        expect (f (a, {"foo", "bar"})).to_be (0)
133        expect (f (a, List {"foo", "bar"})).to_be (0)
134    - it returns +1 when the first list is greater than the second:
135        expect (f (a, {"baz", "quux"})).to_be (1)
136        expect (f (a, List {"baz", "quux"})).to_be (1)
137    - it returns +1 when the first list has additional elements:
138        expect (f (a, {"foo"})).to_be (1)
139        expect (f (a, List {"foo"})).to_be (1)
140    - it compares numerically when both arguments can be coerced:
141        a, b = List {"1", "2", "3"}, List {"1", "2", "10"}
142        expect (f (a, b)).to_be (-1)
143
144  - context as an object method:
145    - before:
146        f = a.compare
147
148    - it returns -1 when the first list is less than the second:
149        expect (f (a, {"foo", "baz"})).to_be (-1)
150        expect (f (a, List {"foo", "baz"})).to_be (-1)
151    - it returns -1 when the second list has additional elements: |
152        b = List {"foo"}
153        expect (f (b, {"foo", "bar"})).to_be (-1)
154        expect (List {"foo"}:compare (List {"foo", "bar"})).to_be (-1)
155    - it returns 0 when two lists are the same:
156        expect (f (a, {"foo", "bar"})).to_be (0)
157        expect (f (a, List {"foo", "bar"})).to_be (0)
158    - it returns +1 when the first list is greater than the second:
159        expect (f (a, {"baz", "quux"})).to_be (1)
160        expect (f (a, List {"baz", "quux"})).to_be (1)
161    - it returns +1 when the first list has additional elements:
162        expect (f (a, {"foo"})).to_be (1)
163        expect (f (a, List {"foo"})).to_be (1)
164    - it compares numerically when both arguments can be coerced:
165        a, b = List {"1", "2", "3"}, List {"1", "2", "10"}
166        expect (f (a, b)).to_be (-1)
167
168  - context as a '<' List metamethod:
169    - it succeeds when the first list is less than the second:
170        expect (a < b).to_be (true)
171    - it fails when the first list is not less than the second:
172        expect (a < a).to_be (false)
173        expect (b < a).to_be (false)
174    - it compares numerically when both arguments can be coerced:
175        a, b = List {"1", "2", "3"}, List {"1", "2", "10"}
176        expect (a < b).to_be (true)
177
178  - context as a '>' List metamethod:
179    - it succeeds when the first list is greater than the second:
180        expect (b > a).to_be (true)
181    - it fails when the first list is not greater than the second:
182        expect (b > b).to_be (false)
183        expect (a > b).to_be (false)
184    - it compares numerically when both arguments can be coerced:
185        a, b = List {"1", "2", "3"}, List {"1", "2", "10"}
186        expect (a > b).to_be (false)
187
188  - context as a '<=' List metamethod:
189    - it succeeds when the first list is less than or equal to the second:
190        expect (a <= b).to_be (true)
191        expect (a <= a).to_be (true)
192    - it fails when the first list is not less than or equal to the second:
193        expect (b <= a).to_be (false)
194    - it compares numerically when both arguments can be coerced:
195        a, b = List {"1", "2", "3"}, List {"1", "2", "10"}
196        expect (a <= b).to_be (true)
197
198  - context as a '>=' List metamethod:
199    - it succeeds when the first list is greater than or equal to the second:
200        expect (b >= a).to_be (true)
201        expect (b >= b).to_be (true)
202    - it fails when the first list is not greater than or equal to the second:
203        expect (a >= b).to_be (false)
204    - it compares numerically when both arguments can be coerced:
205        a, b = List {"1", "2", "3"}, List {"1", "2", "10"}
206        expect (a >= b).to_be (false)
207
208
209- describe concat:
210  - before:
211      l = List {"foo", "bar"}
212
213      f = M.concat
214
215  - context with bad arguments:
216      badargs.diagnose (f, "std.list.concat (List, List|table*)")
217
218  - context as a module function:
219    - it returns a List object:
220        expect (prototype (f (l, l))).to_be "List"
221    - it works for an empty List:
222        expect (f (List {}, {"baz"})).to_equal (List {"baz"})
223        expect (f (List {}, List {"baz"})).to_equal (List {"baz"})
224    - it concatenates Lists:
225        expect (f (l, {"baz", "quux"})).
226          to_equal (List {"foo", "bar", "baz", "quux"})
227        expect (f (l, List {"baz", "quux"})).
228          to_equal (List {"foo", "bar", "baz", "quux"})
229        expect (f (l, {"baz"}, {"quux"})).
230          to_equal (List {"foo", "bar", "baz", "quux"})
231        expect (f (l, List {"baz"}, List {"quux"})).
232          to_equal (List {"foo", "bar", "baz", "quux"})
233
234  - context as an object method:
235    - before:
236        f = l.concat
237
238    - it returns a List object:
239        expect (prototype (f (l, l))).to_be "List"
240    - it works for an empty List:
241        expect (f (List {}, {"baz"})).to_equal (List {"baz"})
242        expect (f (List {}, List {"baz"})).to_equal (List {"baz"})
243    - it concatenates Lists:
244        expect (f (l, {"baz", "quux"})).
245          to_equal (List {"foo", "bar", "baz", "quux"})
246        expect (f (l, List {"baz", "quux"})).
247          to_equal (List {"foo", "bar", "baz", "quux"})
248        expect (f (l, {"baz"}, {"quux"})).
249          to_equal (List {"foo", "bar", "baz", "quux"})
250        expect (f (l, List {"baz"}, List {"quux"})).
251          to_equal (List {"foo", "bar", "baz", "quux"})
252
253  # Beware that .. operations are right associative
254  - context as a List metamethod:
255    - it returns a List object:
256        expect (prototype (l .. List {"baz"})).to_be "List"
257    - it works for an empty List:
258        expect (List {} .. {"baz"}).to_equal (List {"baz"})
259        expect (List {} .. List {"baz"}).to_equal (List {"baz"})
260    - it concatenates Lists:
261        expect (l .. {"baz", "quux"}).
262          to_equal (List {"foo", "bar", "baz", "quux"})
263        expect (l .. List {"baz", "quux"}).
264          to_equal (List {"foo", "bar", "baz", "quux"})
265        expect ({"baz"} .. {"quux"} .. l).
266          to_equal (List {"baz", "quux", "foo", "bar"})
267        expect (l .. List {"baz"} .. List {"quux"}).
268          to_equal (List {"foo", "bar", "baz", "quux"})
269
270
271- describe cons:
272  - before:
273      f = M.cons
274
275  - context with bad arguments:
276      badargs.diagnose (f, "std.list.cons (List, any)")
277
278  - context as a module function:
279    - it returns a List object:
280        expect (prototype (f (l, "x"))).to_be "List"
281    - it prepends an item to a List:
282        expect (f (l, "x")).to_equal (List {"x", "foo", "bar", "baz"})
283    - it works for empty Lists:
284        expect (f (List {}, "x")).to_equal (List {"x"})
285
286  - context as an object method:
287    - before:
288        f = l.cons
289
290    - it returns a List object:
291        expect (prototype (f (l, "x"))).to_be "List"
292    - it prepends an item to a List:
293        expect (f (l, "x")).to_equal (List {"x", "foo", "bar", "baz"})
294    - it works for empty Lists:
295        expect (f (List {}, "x")).to_equal (List {"x"})
296
297
298- describe depair:
299  - before:
300      l = List {List {1, "first"}, List {2, "second"}, List {"third", 4}}
301      t = {"first", "second", third = 4}
302
303  - context as a module function:
304    - before:
305        f = M.depair
306
307    - it writes a deprecation warning:
308        setdebug { deprecate = "nil" }
309        expect (capture (f, {l})).to_contain_error "was deprecated"
310        setdebug { deprecate = false }
311        expect (capture (f, {l})).not_to_contain_error "was deprecated"
312
313    - it returns a primitive table:
314        expect (prototype (f (l))).to_be "table"
315    - it works with an empty List:
316        l = List {}
317        expect (f (l)).to_equal {}
318    - it is the inverse of enpair:
319        expect (f (l)).to_equal (t)
320
321  - context as an object method:
322    - before:
323        f = l.depair
324
325    - it writes a deprecation warning:
326        setdebug { deprecate = "nil" }
327        expect (capture (f, {l})).to_contain_error "was deprecated"
328        setdebug { deprecate = false }
329        expect (capture (f, {l})).not_to_contain_error "was deprecated"
330
331    - it returns a primitive table:
332        expect (prototype (f (l))).to_be "table"
333    - it works with an empty List:
334        expect (f (List {})).to_equal {}
335    - it is the inverse of enpair:
336        expect (f (l)).to_equal (t)
337
338
339- describe elems:
340  - context as a module function:
341    - before:
342        f = M.elems
343
344    - it writes a deprecation warning:
345        setdebug { deprecate = "nil" }
346        expect (capture (f, {{}})).to_contain_error "was deprecated"
347        setdebug { deprecate = false }
348        expect (capture (f, {{}})).not_to_contain_error "was deprecated"
349
350    - it is an iterator over List members:
351        t = {}
352        for e in f (l) do table.insert (t, e) end
353        expect (t).to_equal {"foo", "bar", "baz"}
354    - it works for an empty List:
355        t = {}
356        for e in f (List {}) do table.insert (t, e) end
357        expect (t).to_equal {}
358
359  - context as an object method:
360    - before:
361        f = l.elems
362
363    - it writes a deprecation warning:
364        setdebug { deprecate = "nil" }
365        expect (capture (f, {l})).to_contain_error "was deprecated"
366        setdebug { deprecate = false }
367        expect (capture (f, {l})).not_to_contain_error "was deprecated"
368
369    - it is an iterator over List members:
370        t = {}
371        for e in l:elems () do table.insert (t, e) end
372        expect (t).to_equal {"foo", "bar", "baz"}
373    - it works for an empty List:
374        t, l = {}, List {}
375        for e in l:elems () do table.insert (t, e) end
376        expect (t).to_equal {}
377
378
379- describe enpair:
380  - before:
381     t = {"first", "second", third = 4}
382     f = M.enpair
383
384  - it writes a deprecation warning:
385      setdebug { deprecate = "nil" }
386      expect (capture (f, {t})).to_contain_error "was deprecated"
387      setdebug { deprecate = false }
388      expect (capture (f, {t})).not_to_contain_error "was deprecated"
389
390  - context as a module function:
391    - it returns a List object:
392        expect (prototype (f (t))).to_be "List"
393    - it works for an empty table:
394        expect (f {}).to_equal (List {})
395    - it turns a table into a List of pairs:
396        expect (f (t)).
397          to_equal (List {List {1, "first"}, List {2, "second"}, List {"third", 4}})
398
399
400- describe filter:
401  - before:
402      l = List {"foo", "bar", "baz", "quux"}
403      p = function (e) return (e:match "a" ~= nil) end
404
405  - context as a module function:
406    - before:
407        f = M.filter
408
409    - it writes a deprecation warning:
410        setdebug { deprecate = "nil" }
411        expect (capture (f, {p, l})).to_contain_error "was deprecated"
412        setdebug { deprecate = false }
413        expect (capture (f, {p, l})).not_to_contain_error "was deprecated"
414
415    - it returns a List object:
416        expect (prototype (f (p, l))).to_be "List"
417    - it works for an empty List:
418        expect (f (p, List {})).to_equal (List {})
419    - it filters a List according to a predicate:
420        expect (f (p, l)).to_equal (List {"bar", "baz"})
421
422  - context as an object method:
423    - before:
424        f = l.filter
425
426    - it writes a deprecation warning:
427        setdebug { deprecate = "nil" }
428        expect (capture (f, {l, p})).to_contain_error "was deprecated"
429        setdebug { deprecate = false }
430        expect (capture (f, {l, p})).not_to_contain_error "was deprecated"
431
432    - it returns a List object:
433        expect (prototype (f (l, p))).to_be "List"
434    - it works for an empty List:
435        expect (f (List {}, p)).to_equal (List {})
436    - it filters a List according to a predicate:
437        expect (f (l, p)).to_equal (List {"bar", "baz"})
438
439
440- describe flatten:
441  - before:
442      l = List {List {List {"one"}}, "two", List {List {"three"}, "four"}}
443
444  - context as a module function:
445    - before:
446        f = M.flatten
447
448    - it writes a deprecation warning:
449        setdebug { deprecate = "nil" }
450        expect (capture (f, {l})).to_contain_error "was deprecated"
451        setdebug { deprecate = false }
452        expect (capture (f, {l})).not_to_contain_error "was deprecated"
453
454    - it returns a List object:
455        expect (prototype (f (l))).to_be "List"
456    - it works for an empty List:
457        l = List {}
458        expect (f (l)).to_equal (List {})
459    - it flattens a List:
460        expect (f (l)).
461          to_equal (List {"one", "two", "three", "four"})
462
463  - context as an object method:
464    - before:
465        f = l.flatten
466
467    - it writes a deprecation warning:
468        setdebug { deprecate = "nil" }
469        expect (capture (f, {l})).to_contain_error "was deprecated"
470        setdebug { deprecate = false }
471        expect (capture (f, {l})).not_to_contain_error "was deprecated"
472
473    - it returns a List object:
474        expect (prototype (f (l))).to_be "List"
475    - it works for an empty List:
476        l = List {}
477        expect (f (l)).to_equal (List {})
478    - it flattens a List:
479        expect (f (l)).
480          to_equal (List {"one", "two", "three", "four"})
481
482
483- describe foldl:
484  - before:
485      op = require "std.operator"
486      l = List {3, 4}
487
488  - context as a module function:
489    - before:
490        f = M.foldl
491
492    - it writes a deprecation warning:
493        setdebug { deprecate = "nil" }
494        expect (capture (f, {op.sum, 1, l})).
495         to_contain_error "was deprecated"
496        setdebug { deprecate = false }
497        expect (capture (f, {op.sum, 1, l})).
498         not_to_contain_error "was deprecated"
499
500    - context with a table:
501      - it works with an empty table:
502          expect (f (op.sum, 10000, {})).to_be (10000)
503      - it folds a binary function through a table:
504          expect (f (op.sum, 10000, {1, 10, 100})).to_be (10111)
505      - it folds from left to right:
506          expect (f (op.pow, 2, {3, 4})).to_be ((2 ^ 3) ^ 4)
507
508    - context with a List:
509      - it works with an empty List:
510          expect (f (op.sum, 10000, List {})).to_be (10000)
511      - it folds a binary function through a List:
512          expect (f (op.sum, 10000, List {1, 10, 100})).
513            to_be (10111)
514      - it folds from left to right:
515          expect (f (op.pow, 2, List {3, 4})).to_be ((2 ^ 3) ^ 4)
516
517  - context as an object method:
518    - before:
519        f = l.foldl
520
521    - it writes a deprecation warning:
522        setdebug { deprecate = "nil" }
523        expect (capture (f, {l, op.sum, 1})).
524          to_contain_error "was deprecated"
525        setdebug { deprecate = false }
526        expect (capture (f, {l, op.sum, 1})).
527          not_to_contain_error "was deprecated"
528
529    - it works with an empty List:
530        l = List {}
531        expect (f (l, op.sum, 2)).to_be (2)
532    - it folds a binary function through a List:
533        expect (f (l, op.sum, 2)).to_be (9)
534    - it folds from left to right:
535        expect (f (l, op.pow, 2)).to_be ((2 ^ 3) ^ 4)
536
537
538- describe foldr:
539  - before:
540      op = require "std.operator"
541      l = List {10000, 100}
542
543  - context as a module function:
544    - before:
545        f = M.foldr
546
547    - it writes a deprecation warning:
548        setdebug { deprecate = "nil" }
549        expect (capture (f, {op.sum, 1, {10}})).
550         to_contain_error "was deprecated"
551        setdebug { deprecate = false }
552        expect (capture (f, {op.sum, 1, {10}})).
553         not_to_contain_error "was deprecated"
554
555    - context with a table:
556      - it works with an empty table:
557          expect (f (op.sum, 10000, {})).to_be (10000)
558      - it folds a binary function through a table:
559          expect (f (op.sum, 10000, {1, 10, 100})).to_be (10111)
560      - it folds from right to left:
561          expect (f (op.quot, 10, {10000, 100})).to_be (10000 / (100 / 10))
562
563    - context with a List:
564      - it works with an empty List:
565          expect (f (op.sum, 10000, List {})).to_be (10000)
566      - it folds a binary function through a List:
567          expect (f (op.sum, 10000, List {1, 10, 100})).
568            to_be (10111)
569      - it folds from right to left:
570          expect (f (op.quot, 10, List {10000, 100})).
571            to_be (10000 / (100 / 10))
572
573  - context as an object method:
574    - before:
575        f = l.foldr
576
577    - it writes a deprecation warning:
578        setdebug { deprecate = "nil" }
579        expect (capture (f, {l, op.sum, 1})).
580          to_contain_error "was deprecated"
581        setdebug { deprecate = false }
582        expect (capture (f, {l, op.sum, 1})).
583          not_to_contain_error "was deprecated"
584
585    - it works with an empty List:
586        l = List {}
587        expect (f (l, op.sum, 10)).to_be (10)
588    - it folds a binary function through a List:
589        expect (f (l, op.sum, 10)).to_be (10110)
590    - it folds from right to left:
591        expect (f (l, op.quot, 10)).to_be (10000 / (100 / 10))
592
593
594- describe index_key:
595  - context as a module function:
596    - before:
597        f = M.index_key
598
599    - it writes a deprecation warning:
600        setdebug { deprecate = "nil" }
601        expect (capture (f, {1, List {{1}}})).
602          to_contain_error "was deprecated"
603        setdebug { deprecate = false }
604        expect (capture (f, {1, List {{1}}})).
605          not_to_contain_error "was deprecated"
606
607    - it makes a map of matched table field values to table List offsets:
608        l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}}
609        t = f ("a", l)
610        expect (t).to_equal {b = 1, x = 3}
611        for k, v in pairs (t) do
612          expect (k).to_equal (l[v]["a"])
613        end
614    - it captures only the last matching List offset:
615        l = List {{a = "b"}, {a = "x"}, {a = "b"}}
616        t = f ("a", l)
617        expect (t.b).not_to_be (1)
618        expect (t.x).to_be (2)
619        expect (t.b).to_be (3)
620    - it produces incomplete indices when faced with repeated matching table values:
621        l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}}
622        expect (f (1, l)).to_equal {1, 3}
623        expect (f (2, l)).to_equal {3, 1}
624        expect (f (3, l)).to_equal {nil, nil, 3}
625
626  - context as an object method:
627    - before:
628        f = l.index_key
629
630    - it writes a deprecation warning:
631        setdebug { deprecate = "nil" }
632        expect (capture (f, {l, 1})).to_contain_error "was deprecated"
633        setdebug { deprecate = false }
634        expect (capture (f, {l, 1})).not_to_contain_error "was deprecated"
635
636    - it makes a map of matched table field values to table List offsets:
637        l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}}
638        t = l:index_key "a"
639        expect (t).to_equal {b = 1, x = 3}
640        for k, v in pairs (t) do
641          expect (k).to_equal (l[v]["a"])
642        end
643    - it captures only the last matching List offset:
644        l = List {{a = "b"}, {a = "x"}, {a = "b"}}
645        t = l:index_key "a"
646        expect (t.b).not_to_be (1)
647        expect (t.x).to_be (2)
648        expect (t.b).to_be (3)
649    - it produces incomplete indices when faced with repeated matching table values:
650        l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}}
651        expect (l:index_key (1)).to_equal {1, 3}
652        expect (l:index_key (2)).to_equal {3, 1}
653        expect (l:index_key (3)).to_equal {nil, nil, 3}
654
655
656- describe index_value:
657  - context as a module function:
658    - before:
659        f = M.index_value
660
661    - it writes a deprecation warning:
662        setdebug { deprecate = "nil" }
663        expect (capture (f, {1, List {{1}}})).
664          to_contain_error "was deprecated"
665        setdebug { deprecate = false }
666        expect (capture (f, {1, List {{1}}})).
667          not_to_contain_error "was deprecated"
668
669    - it makes a table of matched table field values to table List references:
670        l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}}
671        t = f ("a", l)
672        expect (t).to_equal {b = l[1], x = l[3]}
673        for k, v in pairs (t) do
674          expect (k).to_equal (v["a"])
675        end
676    - it captures only the last matching List offset:
677        l = List {{a = "b"}, {a = "x"}, {a = "b"}}
678        t = f ("a", l)
679        expect (t.b).not_to_be (l[1])
680        expect (t.x).to_be (l[2])
681        expect (t.b).to_be (l[3])
682    - it produces incomplete indices when faced with repeated matching table values:
683        l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}}
684        expect (f (1, l)).to_equal {l[1], l[3]}
685        expect (f (2, l)).to_equal {l[3], l[1]}
686        expect (f (3, l)).to_equal {nil, nil, l[3]}
687
688  - context as an object method:
689    - before:
690        l = List {{1}}
691
692        f = l.index_value
693
694    - it writes a deprecation warning:
695        setdebug { deprecate = "nil" }
696        expect (capture (f, {l, 1})).to_contain_error "was deprecated"
697        setdebug { deprecate = false }
698        expect (capture (f, {l, 1})).not_to_contain_error "was deprecated"
699
700    - it makes a table of matched table field values to table List references:
701        l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}}
702        t = l:index_value "a"
703        expect (t).to_equal {b = l[1], x = l[3]}
704        for k, v in pairs (t) do
705          expect (k).to_equal (v["a"])
706        end
707    - it captures only the last matching List offset:
708        l = List {{a = "b"}, {a = "x"}, {a = "b"}}
709        t = l:index_value "a"
710        expect (t.b).not_to_be (l[1])
711        expect (t.x).to_be (l[2])
712        expect (t.b).to_be (l[3])
713    - it produces incomplete indices when faced with repeated matching table values:
714        l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}}
715        expect (l:index_value (1)).to_equal {l[1], l[3]}
716        expect (l:index_value (2)).to_equal {l[3], l[1]}
717        expect (l:index_value (3)).to_equal {nil, nil, l[3]}
718
719
720- describe map:
721  - before:
722      l = List {1, 2, 3, 4, 5}
723      sq = function (n) return n * n end
724
725  - context as a module function:
726    - before:
727        f, badarg = init (M, this_module, "map")
728
729    - it writes a deprecation warning:
730        setdebug { deprecate = "nil" }
731        expect (capture (f, {sq, l})).to_contain_error "was deprecated"
732        setdebug { deprecate = false }
733        expect (capture (f, {sq, l})).not_to_contain_error "was deprecated"
734
735    - it returns a List object:
736        expect (prototype (f (sq, l))).to_be "List"
737    - it works for an empty List:
738        expect (f (sq, List {})).to_equal (List {})
739    - it creates a new List:
740        o = l
741        m = f (sq, l)
742        expect (l).to_equal (o)
743        expect (m).not_to_equal (o)
744        expect (l).to_equal (List {1, 2, 3, 4, 5})
745    - it maps a function over a List:
746        expect (f (sq, l)).to_equal (List {1, 4, 9, 16, 25})
747
748  - context as an object method:
749    - before:
750        f = l.map
751
752    - it writes a deprecation warning:
753        setdebug { deprecate = "nil" }
754        expect (capture (f, {l, sq})).to_contain_error "was deprecated"
755        setdebug { deprecate = false }
756        expect (capture (f, {l, sq})).not_to_contain_error "was deprecated"
757
758    - it returns a List object:
759        m = f (l, sq)
760        expect (prototype (m)).to_be "List"
761    - it works for an empty List:
762        expect (f (List {}, sq)).to_equal (List {})
763    - it creates a new List:
764        o = l
765        m = f (l, sq)
766        expect (l).to_equal (o)
767        expect (m).not_to_equal (o)
768        expect (l).to_equal (List {1, 2, 3, 4, 5})
769    - it maps a function over a List:
770        expect (f (l, sq)).to_equal (List {1, 4, 9, 16, 25})
771
772
773- describe map_with:
774  - before:
775      l = List {List {1, 2, 3}, List {4, 5}}
776      fn = function (...) return select ("#", ...) end
777
778  - context as a module function:
779    - before:
780        f = M.map_with
781
782    - it writes a deprecation warning:
783        setdebug { deprecate = "nil" }
784        expect (capture (f, {fn, l})).to_contain_error "was deprecated"
785        setdebug { deprecate = false }
786        expect (capture (f, {fn, l})).not_to_contain_error "was deprecated"
787
788    - it returns a List object:
789        m = f (fn, l)
790        expect (prototype (m)).to_be "List"
791    - it creates a new List:
792        o = l
793        m = f (fn, l)
794        expect (l).to_equal (o)
795        expect (m).not_to_equal (o)
796        expect (l).to_equal (List {List {1, 2, 3}, List {4, 5}})
797    - it maps a function over a List:
798        expect (f (fn, l)).to_equal (List {3, 2})
799    - it works for an empty List:
800        l = List {}
801        expect (f (fn, l)).to_equal (List {})
802
803  - context as an object method:
804    - before:
805        f = l.map_with
806
807    - it writes a deprecation warning:
808        setdebug { deprecate = "nil" }
809        expect (capture (f, {l, fn})).to_contain_error "was deprecated"
810        setdebug { deprecate = false }
811        expect (capture (f, {l, fn})).not_to_contain_error "was deprecated"
812
813    - it returns a List object:
814        m = f (l, fn)
815        expect (prototype (m)).to_be "List"
816    - it creates a new List:
817        o = l
818        m = f (l, fn)
819        expect (l).to_equal (o)
820        expect (m).not_to_equal (o)
821        expect (l).to_equal (List {List {1, 2, 3}, List {4, 5}})
822    - it maps a function over a List:
823        expect (f (l, fn)).to_equal (List {3, 2})
824    - it works for an empty List:
825        l = List {}
826        expect (f (l, fn)).to_equal (List {})
827
828
829- describe project:
830  - before:
831      l = List {
832        {first = false, second = true, third = true},
833        {first = 1, second = 2, third = 3},
834        {first = "1st", second = "2nd", third = "3rd"},
835      }
836
837  - context as a module function:
838    - before:
839        f = M.project
840
841    - it writes a deprecation warning:
842        setdebug { deprecate = "nil" }
843        expect (capture (f, {"third", l})).to_contain_error "was deprecated"
844        setdebug { deprecate = false }
845        expect (capture (f, {"third", l})).not_to_contain_error "was deprecated"
846
847    - it returns a List object:
848        expect (prototype (f ("third", l))).to_be "List"
849    - it works with an empty List:
850        expect (f ("third", List {})).to_equal (List {})
851    - it projects a List of fields from a List of tables:
852        expect (f ("third", l)).to_equal (List {true, 3, "3rd"})
853    - it projects fields with a falsey value correctly:
854        expect (f ("first", l)).to_equal (List {false, 1, "1st"})
855
856  - context as an object method:
857    - before:
858        f = l.project
859
860    - it writes a deprecation warning:
861        setdebug { deprecate = "nil" }
862        expect (capture (f, {l, "third"})).to_contain_error "was deprecated"
863        setdebug { deprecate = false }
864        expect (capture (f, {l, "third"})).not_to_contain_error "was deprecated"
865
866    - it returns a List object:
867        expect (prototype (f (l, "third"))).to_be "List"
868    - it works with an empty List:
869        expect (f (List {}, "third")).to_equal (List {})
870    - it projects a List of fields from a List of tables:
871        expect (f (l, "third")).to_equal (List {true, 3, "3rd"})
872    - it projects fields with a falsey value correctly:
873        expect (f (l, "first")).to_equal (List {false, 1, "1st"})
874
875
876- describe relems:
877  - context as a module function:
878    - before:
879        f = M.relems
880
881    - it writes a deprecation warning:
882        setdebug { deprecate = "nil" }
883        expect (capture (f, {l})).to_contain_error "was deprecated"
884        setdebug { deprecate = false }
885        expect (capture (f, {l})).not_to_contain_error "was deprecated"
886
887    - it is a reverse iterator over List members:
888        t = {}
889        for e in f (l) do table.insert (t, e) end
890        expect (t).to_equal {"baz", "bar", "foo"}
891    - it works for an empty List:
892        t = {}
893        for e in f (List {}) do table.insert (t, e) end
894        expect (t).to_equal {}
895
896  - context as an object method:
897    - before:
898        f = l.relems
899
900    - it writes a deprecation warning:
901        setdebug { deprecate = "nil" }
902        expect (capture (f, {l})).to_contain_error "was deprecated"
903        setdebug { deprecate = false }
904        expect (capture (f, {l})).not_to_contain_error "was deprecated"
905
906    - it is a reverse iterator over List members:
907        t = {}
908        for e in l:relems () do table.insert (t, e) end
909        expect (t).to_equal {"baz", "bar", "foo"}
910    - it works for an empty List:
911        t, l = {}, List {}
912        for e in l:relems () do table.insert (t, e) end
913        expect (t).to_equal {}
914
915
916- describe rep:
917  - before:
918      l = List {"foo", "bar"}
919
920      f = M.rep
921
922  - context with bad arguments:
923      badargs.diagnose (f, "std.list.rep (List, int)")
924
925  - context as a module function:
926    - it returns a List object:
927        expect (prototype (f (l, 3))).to_be "List"
928    - it works for an empty List:
929        expect (f (List {}, 99)).to_equal (List {})
930    - it repeats the contents of a List:
931        expect (f (l, 3)).
932          to_equal (List {"foo", "bar", "foo", "bar", "foo", "bar"})
933
934  - context as an object method:
935    - before:
936        f = l.rep
937
938    - it returns a List object:
939        expect (prototype (f (l, 3))).to_be "List"
940    - it works for an empty List:
941        expect (f (List {}, 99)).to_equal (List {})
942    - it repeats the contents of a List:
943        expect (f (l, 3)).
944          to_equal (List {"foo", "bar", "foo", "bar", "foo", "bar"})
945
946
947- describe reverse:
948  - before:
949      l = List {"foo", "bar", "baz", "quux"}
950
951  - context as a module function:
952    - before:
953        f = M.reverse
954
955    - it writes a deprecation warning:
956        setdebug { deprecate = "nil" }
957        expect (capture (f, {{}})).to_contain_error "was deprecated"
958        setdebug { deprecate = false }
959        expect (capture (f, {{}})).not_to_contain_error "was deprecated"
960
961    - it returns a List object:
962        expect (prototype (f (l))).to_be "List"
963    - it works for an empty List:
964        l = List {}
965        expect (f (l)).to_equal (List {})
966    - it makes a new reversed List:
967        m = l
968        expect (f (l)).
969          to_equal (List {"quux", "baz", "bar", "foo"})
970        expect (l).to_equal (List {"foo", "bar", "baz", "quux"})
971        expect (l).to_be (m)
972
973  - context as an object method:
974    - before:
975        f = l.reverse
976
977    - it writes a deprecation warning:
978        setdebug { deprecate = "nil" }
979        expect (capture (f, {l})).to_contain_error "was deprecated"
980        setdebug { deprecate = false }
981        expect (capture (f, {l})).not_to_contain_error "was deprecated"
982
983    - it returns a List object:
984        expect (prototype (f (l))).to_be "List"
985    - it works for an empty List:
986        expect (f (List {})).to_equal (List {})
987    - it makes a new reversed List:
988        m = l
989        expect (f (l)).
990          to_equal (List {"quux", "baz", "bar", "foo"})
991        expect (l).to_equal (List {"foo", "bar", "baz", "quux"})
992        expect (l).to_be (m)
993
994
995- describe shape:
996  - before:
997      l = List {1, 2, 3, 4, 5, 6}
998
999  - context as a module function:
1000    - before:
1001        f = M.shape
1002
1003    - it writes a deprecation warning:
1004        setdebug { deprecate = "nil" }
1005        expect (capture (f, {{0}, l})).to_contain_error "was deprecated"
1006        setdebug { deprecate = false }
1007        expect (capture (f, {{0}, l})).not_to_contain_error "was deprecated"
1008
1009    - it returns a List object:
1010        expect (prototype (f ({2, 3}, l))).to_be "List"
1011    - it works for an empty List:
1012        expect (f ({0}, List {})).to_equal (List {})
1013    - it returns the result in a new List object:
1014        expect (f ({2, 3}, l)).not_to_be (l)
1015    - it does not perturb the argument List:
1016        f ({2, 3}, l)
1017        expect (l).to_equal (List {1, 2, 3, 4, 5, 6})
1018    - it reshapes a List according to given dimensions:
1019        expect (f ({2, 3}, l)).
1020          to_equal (List {List {1, 2, 3}, List {4, 5, 6}})
1021        expect (f ({3, 2}, l)).
1022          to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}})
1023    - it treats 0-valued dimensions as an indefinite number:
1024        expect (f ({2, 0}, l)).
1025          to_equal (List {List {1, 2, 3}, List {4, 5, 6}})
1026        expect (f ({0, 2}, l)).
1027          to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}})
1028
1029  - context as an object method:
1030    - before:
1031        f = l.shape
1032
1033    - it writes a deprecation warning:
1034        setdebug { deprecate = "nil" }
1035        expect (capture (f, {l, {0}})).to_contain_error "was deprecated"
1036        setdebug { deprecate = false }
1037        expect (capture (f, {l, {0}})).not_to_contain_error "was deprecated"
1038
1039    - it returns a List object:
1040        expect (prototype (f (l, {2, 3}))).to_be "List"
1041    - it works for an empty List:
1042        expect (f (List {}, {0})).to_equal (List {})
1043    - it returns the result in a new List object:
1044        expect (f (l, {2, 3})):not_to_be (l)
1045    - it does not perturb the argument List:
1046        f (l, {2, 3})
1047        expect (l).to_equal (List {1, 2, 3, 4, 5, 6})
1048    - it reshapes a List according to given dimensions:
1049        expect (f (l, {2, 3})).
1050          to_equal (List {List {1, 2, 3}, List {4, 5, 6}})
1051        expect (f (l, {3, 2})).
1052          to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}})
1053    - it treats 0-valued dimensions as an indefinite number:
1054        expect (f (l, {2, 0})).
1055          to_equal (List {List {1, 2, 3}, List {4, 5, 6}})
1056        expect (f (l, {0, 2})).
1057          to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}})
1058
1059
1060- describe sub:
1061  - before:
1062      l = List {1, 2, 3, 4, 5, 6, 7}
1063
1064      f = M.sub
1065
1066  - context with bad arguments:
1067      badargs.diagnose (f, "std.list.sub (List, ?int, ?int)")
1068
1069  - context as a module function:
1070    - it returns a List object:
1071        expect (prototype (f (l, 1, 1))).to_be "List"
1072    - it makes a List from a subrange of another List:
1073        expect (f (l, 2, 5)).to_equal (List {2, 3, 4, 5})
1074    - it truncates the result if 'to' argument is too large:
1075        expect (f (l, 5, 10)).to_equal (List {5, 6, 7})
1076    - it defaults 'to' to the end of the List:
1077        expect (f (l, 5)).to_equal (List {5, 6, 7})
1078    - it defaults 'from' to the beginning of the List:
1079        expect (f (l)).to_equal (l)
1080    - it returns an empty List when 'from' is greater than 'to':
1081        expect (f (l, 2, 1)).to_equal (List {})
1082    - it counts from the end of the List for a negative 'from' argument:
1083        expect (f (l, -3)).to_equal (List {5, 6, 7})
1084    - it counts from the end of the List for a negative 'to' argument:
1085        expect (f (l, -5, -2)).to_equal (List {3, 4, 5, 6})
1086
1087  - context as an object method:
1088    - before:
1089        f = l.sub
1090
1091    - it returns a List object:
1092        expect (prototype (f (l, 1, 1))).to_be "List"
1093    - it makes a List from a subrange of another List:
1094        expect (f (l, 2, 5)).to_equal (List {2, 3, 4, 5})
1095    - it truncates the result if 'to' argument is too large:
1096        expect (f (l, 5, 10)).to_equal (List {5, 6, 7})
1097    - it defaults 'to' to the end of the List:
1098        expect (f (l, 5)).to_equal (List {5, 6, 7})
1099    - it defaults 'from' to the beginning of the List:
1100        expect (f (l)).to_equal (l)
1101    - it returns an empty List when 'from' is greater than 'to':
1102        expect (f (l, 2, 1)).to_equal (List {})
1103    - it counts from the end of the List for a negative 'from' argument:
1104        expect (f (l, -3)).to_equal (List {5, 6, 7})
1105    - it counts from the end of the List for a negative 'to' argument:
1106        expect (f (l, -5, -2)).to_equal (List {3, 4, 5, 6})
1107
1108
1109- describe tail:
1110  - before:
1111      l = List {1, 2, 3, 4, 5, 6, 7}
1112
1113      f = M.tail
1114
1115  - context with bad arguments:
1116      badargs.diagnose (f, "std.list.tail (List)")
1117
1118  - context as a module function:
1119    - it returns a List object:
1120        expect (prototype (f (l))).to_be "List"
1121    - it makes a new List with the first element removed:
1122        expect (f (l)).to_equal (List {2, 3, 4, 5, 6, 7})
1123    - it works for an empty List:
1124        expect (f (List {})).to_equal (List {})
1125    - it returns an empty List when passed a List with one element:
1126        expect (f (List {1})).to_equal (List {})
1127
1128  - context as an object method:
1129    - before:
1130        f = l.tail
1131
1132    - it returns a List object:
1133        expect (prototype (f (l))).to_be "List"
1134    - it makes a new List with the first element removed:
1135        expect (f (l)).to_equal (List {2, 3, 4, 5, 6, 7})
1136    - it works for an empty List:
1137        expect (f (List {})).to_equal (List {})
1138    - it returns an empty List when passed a List with one element:
1139        expect (f (List {1})).to_equal (List {})
1140
1141
1142- describe transpose:
1143  - before:
1144      l = List {List {1, 2}, List {3, 4}, List {5, 6}}
1145
1146  - context as a module function:
1147    - before:
1148        f = M.transpose
1149
1150    - it writes a deprecation warning:
1151        setdebug { deprecate = "nil" }
1152        expect (capture (f, {l})).to_contain_error "was deprecated"
1153        setdebug { deprecate = false }
1154        expect (capture (f, {l})).not_to_contain_error "was deprecated"
1155
1156    - it returns a List object:
1157        expect (prototype (f (l))).to_be "List"
1158    - it works for an empty List:
1159        expect (f (List {})).to_equal (List {})
1160    - it returns the result in a new List object:
1161        expect (f (l)).not_to_be (l)
1162    - it does not perturb the argument List:
1163        m = f (l)
1164        expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}})
1165    - it transposes rows and columns:
1166        expect (f (l)).to_equal (List {List {1, 3, 5}, List {2, 4, 6}})
1167
1168  - context as an object method:
1169    - before:
1170        f = l.transpose
1171
1172    - it writes a deprecation warning:
1173        setdebug { deprecate = "nil" }
1174        expect (capture (f, {l})).to_contain_error "was deprecated"
1175        setdebug { deprecate = false }
1176        expect (capture (f, {l})).not_to_contain_error "was deprecated"
1177
1178    - it returns a List object:
1179        expect (prototype (f (l))).to_be "List"
1180    - it works for an empty List:
1181        expect (f (List {})).to_equal (List {})
1182    - it returns the result in a new List object:
1183        expect (f (l)).not_to_be (l)
1184    - it does not perturb the argument List:
1185        m = f (l)
1186        expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}})
1187    - it transposes rows and columns:
1188        expect (f (l)).
1189          to_equal (List {List {1, 3, 5}, List {2, 4, 6}})
1190
1191
1192- describe zip_with:
1193  - before:
1194      l = List {List {1, 2}, List {3, 4}, List {5}}
1195      fn = function (...) return tonumber (table.concat {...}) end
1196
1197  - context as a module function:
1198    - before:
1199        f = M.zip_with
1200
1201    - it writes a deprecation warning:
1202        setdebug { deprecate = "nil" }
1203        expect (capture (f, {l, fn})).to_contain_error "was deprecated"
1204        setdebug { deprecate = false }
1205        expect (capture (f, {l, fn})).not_to_contain_error "was deprecated"
1206
1207    - it returns a List object:
1208        expect (prototype (f (l, fn))).to_be "List"
1209    - it works for an empty List:
1210        expect (f (List {}, fn)).to_equal (List {})
1211    - it returns the result in a new List object:
1212        expect (f (l, fn)):not_to_be (l)
1213    - it does not perturb the argument List:
1214        m = f (l, fn)
1215        expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5}})
1216    - it combines column entries with a function:
1217        expect (f (l, fn)).to_equal (List {135, 24})
1218
1219  - context as an object method:
1220    - before:
1221        f = l.zip_with
1222
1223    - it writes a deprecation warning:
1224        setdebug { deprecate = "nil" }
1225        expect (capture (f, {l, fn})).to_contain_error "was deprecated"
1226        setdebug { deprecate = false }
1227        expect (capture (f, {l, fn})).not_to_contain_error "was deprecated"
1228
1229    - it returns a List object:
1230        expect (prototype (f (l, fn))).to_be "List"
1231    - it works for an empty List:
1232        expect (f (List {}, fn)).to_equal (List {})
1233    - it returns the result in a new List object:
1234        expect (f (l, fn)):not_to_be (l)
1235    - it does not perturb the argument List:
1236        m = f (l, fn)
1237        expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5}})
1238    - it combines column entries with a function:
1239        expect (f (l, fn)).to_equal (List {135, 24})
1240