1before: 2 base = require "std.base" 3 4 this_module = "std.functional" 5 global_table = "_G" 6 7 exported_apis = { "bind", "callable", "case", "collect", "compose", 8 "cond", "curry", "eval", "filter", "fold", "foldl", 9 "foldr", "id", "lambda", "map", "map_with", 10 "memoize", "nop", "op", "reduce", "zip", "zip_with" } 11 12 M = require (this_module) 13 14 15specify std.functional: 16- context when required: 17 - context by name: 18 - it does not touch the global table: 19 expect (show_apis {added_to=global_table, by=this_module}). 20 to_equal {} 21 - it exports the documented apis: 22 t = {} 23 for k in pairs (M) do t[#t + 1] = k end 24 expect (t).to_contain.a_permutation_of (exported_apis) 25 26 - context via the std module: 27 - it does not touch the global table: 28 expect (show_apis {added_to=global_table, by="std"}). 29 to_equal {} 30 31 32- describe bind: 33 - before: 34 op = require "std.operator" 35 36 f = M.bind 37 38 - it writes an argument passing deprecation warning: 39 setdebug { deprecate = "nil" } 40 expect (capture (f, {nop, M, "bind"})). 41 to_contain_error "was deprecated" 42 setdebug { deprecate = false } 43 expect (capture (f, {nop, M, "bind"})). 44 not_to_contain_error "was deprecated" 45 46 - context with bad arguments: 47 badargs.diagnose (f, "std.functional.bind (function, ?any*)") 48 49 - it does not affect normal operation if no arguments are bound: 50 expect (f (math.min, {}) (2, 3, 4)).to_be (2) 51 - it takes the extra arguments into account: 52 expect (f (math.min, {1, 0}) (2, 3, 4)).to_be (0) 53 - it appends final call arguments: 54 expect (f (math.max, {2, 3}) (4, 5, 1)).to_be (5) 55 - it does not require all arguments in final call: 56 div = function (a, b) return a / b end 57 expect (f (div, {100}) (25)).to_be (4) 58 - it supports out of order extra arguments: 59 expect (f (op.pow, {[2] = 3}) (2)).to_be (8) 60 - it propagates nil arguments correctly: 61 expect ({f (M.id, {[2]="b", [4]="d"}) (nil, 3, 5, 6, nil)}). 62 to_equal {nil, "b", 3, "d", 5, 6, nil} 63 - it supports the legacy api: 64 expect (f (math.min) (2, 3, 4)).to_be (2) 65 expect (f (math.min, 1, 0) (2, 3, 4)).to_be (0) 66 expect (f (op.pow, nil, 3) (2)).to_be (8) 67 68 69- describe callable: 70 - before: 71 f = M.callable 72 73 - context with bad arguments: 74 badargs.diagnose (f, "std.functional.callable (?any)") 75 76 - it returns the function associated with a callable: 77 Container = require "std.container" { __call = M.nop } 78 for _, v in ipairs { 79 true, 80 42, 81 "str", 82 io.stderr, 83 {}, 84 M.nop, 85 setmetatable ({}, {__call = M.nop}), 86 Container, 87 } do 88 expect (f (v)).to_be (pcall (v, {}) and M.nop or nil) 89 end 90 - it returns 'nil' for uncallable arguments: 91 expect (f ()).to_be (nil) 92 expect (f {}).to_be (nil) 93 expect (f "").to_be (nil) 94 95- describe case: 96 - before: 97 yes = function () return true end 98 no = function () return false end 99 default = function (s) return s end 100 branches = { yes = yes, no = no, default } 101 102 f = M.case 103 104 - context with bad arguments: | 105 badargs.diagnose (f, "std.functional.case (?any, #table)") 106 107 - it matches against branch keys: 108 expect (f ("yes", branches)).to_be (true) 109 expect (f ("no", branches)).to_be (false) 110 - it has a default for unmatched keys: 111 expect (f ("none", branches)).to_be "none" 112 - it returns nil for unmatched keys with no default: 113 expect (f ("none", { yes = yes, no = no })).to_be (nil) 114 - it returns non-function matches: 115 expect (f ("t", {t = true})).to_be (true) 116 - it evaluates returned functions: 117 expect (f ("fn", {fn = function () return true end})). 118 to_be (true) 119 - it passes 'with' to function matches: 120 expect (f ("with", {function (s) return s end})).to_be "with" 121 - it evaluates returned functables: 122 functable = setmetatable ({}, {__call = function (t, with) return with end}) 123 expect (f ("functable", {functable})).to_be "functable" 124 - it evaluates 'with' exactly once: 125 s = "prince" 126 function acc () s = s .. "s"; return s end 127 expect (f (acc (), { 128 prince = function () return "one" end, 129 princes = function () return "many" end, 130 princess = function () return "one" end, 131 function () return "gibberish" end, 132 })).to_be "many" 133 134 135- describe collect: 136 - before: 137 f = M.collect 138 139 - context with bad arguments: 140 badargs.diagnose (f, "std.functional.collect ([func], any*)") 141 142 - it collects a list of single return value iterator results: 143 expect (f (base.ielems, {"a", "b", "c"})).to_equal {"a", "b", "c"} 144 - it collects a table of key:value iterator results: 145 t = {"first", second="two", last=3} 146 expect (f (pairs, t)).to_equal (t) 147 - it propagates nil arguments correctly: 148 expect (f {"a", nil, nil, "d", "e"}).to_equal {"a", [4]="d", [5]="e"} 149 - it defaults to npairs iteration: 150 expect (f {1, 2, [5]=5, a="b", c="d"}).to_equal {1, 2, [5]=5} 151 152 153- describe compose: 154 - before: 155 f = M.compose 156 157 - context with bad arguments: 158 badargs.diagnose (f, "std.functional.compose (func*)") 159 160 - it composes a single function correctly: 161 expect (f (M.id) (1)).to_be (1) 162 - it propagates nil arguments correctly: 163 expect ({f (M.id) (1, nil, nil, 4)}).to_equal {1, nil, nil, 4} 164 expect ({f (M.id, M.id) (1, nil, nil, 4)}).to_equal {1, nil, nil, 4} 165 - it composes functions in the correct order: 166 expect (f (math.sin, math.cos) (1)). 167 to_be (math.cos (math.sin (1))) 168 169 170- describe cond: 171 - before: 172 yes = function () return true end 173 no = function () return false end 174 default = function (s) return s end 175 branches = { yes = yes, no = no, default } 176 177 f = M.cond 178 179 - it returns nil for no arguments: 180 expect (f ()).to_be (nil) 181 - it evaluates a single function argument: 182 expect (f (function () return true end)).to_be (true) 183 - it evaluates a single functable argument: 184 functable = setmetatable ({}, {__call = function () return true end}) 185 expect (f (functable)).to_be (true) 186 - it returns a non-callable single argument directly: 187 expect (f "foo").to_be "foo" 188 - it evaluates a branch function if expr is truthy: 189 expect (f ("truthy", function (s) return s end)).to_be "truthy" 190 - it returns nil if the last expr is falsey: 191 expect (f (nil, function (s) return "falsey" end)).to_be (nil) 192 expect (f (false, true, false, true)).to_be (nil) 193 - it recurses with remaining arguments if first argument is falsey: 194 expect (f (nil, true, 42, M.id)).to_be (42) 195 expect (f (nil, true, false, false, 42, M.id)).to_be (42) 196 197 198- describe curry: 199 - before: 200 op = require "std.operator" 201 202 f = M.curry 203 204 - context with bad arguments: 205 badargs.diagnose (f, "std.functional.curry (func, int)") 206 207 - it returns a zero argument function uncurried: 208 expect (f (f, 0)).to_be (f) 209 - it returns a one argument function uncurried: 210 expect (f (f, 1)).to_be (f) 211 - it curries a two argument function: 212 expect (f (f, 2)).not_to_be (f) 213 - it evaluates intermediate arguments one at a time: 214 expect (f (math.min, 3) (2) (3) (4)).to_equal (2) 215 - it returns a curried function that can be partially applied: 216 bin = f (op.pow, 2) (2) 217 expect (bin (2)).to_be (op.pow (2, 2)) 218 expect (bin (10)).to_be (op.pow (2, 10)) 219 220 221- describe eval: 222 - before: 223 f = M.eval 224 225 - it writes a deprecation warning: 226 setdebug { deprecate = "nil" } 227 expect (capture (f, {"42"})).to_contain_error "was deprecated" 228 setdebug { deprecate = false } 229 expect (capture (f, {"42"})).not_to_contain_error "was deprecated" 230 231 - it diagnoses invalid lua: 232 # Some internal error when eval tries to call uncompilable "=" code. 233 expect (f "=").to_raise () 234 - it evaluates a string of lua code: 235 expect (f "math.min (2, 10)").to_be (math.min (2, 10)) 236 237 238- describe filter: 239 - before: 240 elements = {"a", "b", "c", "d", "e"} 241 inverse = {a=1, b=2, c=3, d=4, e=5} 242 243 f = M.filter 244 245 - context with bad arguments: 246 badargs.diagnose (f, "std.functional.filter (func, [func], any*)") 247 248 - it works with an empty table: 249 expect (f (M.id, pairs, {})).to_equal {} 250 - it iterates through element keys: 251 expect (f (M.id, base.ielems, elements)).to_equal {"a", "b", "c", "d", "e"} 252 expect (f (M.id, base.elems, inverse)).to_contain.a_permutation_of {1, 2, 3, 4, 5} 253 - it propagates nil arguments correctly: 254 t = {"a", nil, nil, "d", "e"} 255 expect (f (M.id, base.npairs, t)).to_equal (t) 256 - it passes all iteration result values to filter predicate: 257 t = {} 258 f (function (k, v) t[k] = v end, pairs, elements) 259 expect (t).to_equal (elements) 260 - it returns a list of filtered single return value iterator results: 261 expect (f (function (e) return e:match "[aeiou]" end, base.ielems, elements)). 262 to_equal {"a", "e"} 263 - it returns a table of filtered key:value iterator results: 264 t = {"first", second=2, last="three"} 265 expect (f (function (k, v) return type (v) == "string" end, pairs, t)). 266 to_equal {"first", last="three"} 267 expect (f (function (k, v) return k % 2 == 0 end, ipairs, elements)). 268 to_equal {[2]="b", [4]="d"} 269 - it defaults to pairs iteration: 270 t = {"first", second=2, last="three"} 271 expect (f (function (k, v) return type (v) == "string" end, t)). 272 to_equal {"first", last="three"} 273 274 275- describe fold: 276 - before: 277 op = require "std.operator" 278 f = M.fold 279 280 - it writes a deprecation warning: 281 setdebug { deprecate = "nil" } 282 expect (capture (f, {M.id, 1, ipairs, {}})). 283 to_contain_error "was deprecated" 284 setdebug { deprecate = false } 285 expect (capture (f, {M.id, 1, ipairs, {}})). 286 not_to_contain_error "was deprecated" 287 288 - it works with an empty table: 289 expect (f (op.sum, 2, ipairs, {})).to_be (2) 290 - it calls a binary function over single return value iterator results: 291 expect (f (op.sum, 2, base.ielems, {3})). 292 to_be (2 + 3) 293 expect (f (op.prod, 2, base.ielems, {3, 4})). 294 to_be (2 * 3 * 4) 295 - it calls a binary function over key:value iterator results: 296 expect (f (op.sum, 2, ipairs, {3})).to_be (2 + 3) 297 expect (f (op.prod, 2, ipairs, {3, 4})).to_be (2 * 3 * 4) 298 - it folds elements from left to right: 299 expect (f (op.pow, 2, ipairs, {3, 4})).to_be ((2 ^ 3) ^ 4) 300 301 302- describe foldl: 303 - before: 304 op = require "std.operator" 305 f = M.foldl 306 307 - context with bad arguments: 308 badargs.diagnose (f, "std.functional.foldl (func, [any], table)") 309 310 - it works with an empty table: 311 expect (f (op.sum, 10000, {})).to_be (10000) 312 - it folds a binary function through a table: 313 expect (f (op.sum, 10000, {1, 10, 100})).to_be (10111) 314 - it folds from left to right: 315 expect (f (op.pow, 2, {3, 4})).to_be ((2 ^ 3) ^ 4) 316 - it supports eliding init argument: 317 expect (f (op.pow, {2, 3, 4})).to_be ((2 ^ 3) ^ 4) 318 319 320- describe foldr: 321 - before: 322 op = require "std.operator" 323 f = M.foldr 324 325 - context with bad arguments: 326 badargs.diagnose (f, "std.functional.foldr (func, [any], table)") 327 328 - it works with an empty table: 329 expect (f (op.sum, 1, {})).to_be (1) 330 - it folds a binary function through a table: 331 expect (f (op.sum, {10000, 100, 10, 1})).to_be (10111) 332 - it folds from right to left: 333 expect (f (op.quot, 10, {10000, 100})).to_be (10000 / (100 / 10)) 334 - it supports eliding init argument: 335 expect (f (op.quot, {10000, 100, 10})).to_be (10000 / (100 / 10)) 336 337 338- describe id: 339 - before: 340 f = M.id 341 - it returns argument unchanged: 342 expect (f (true)).to_be (true) 343 expect (f {1, 1, 2, 3}).to_equal {1, 1, 2, 3} 344 - it returns multiple arguments unchanged: 345 expect ({f (1, "two", false)}).to_equal {1, "two", false} 346 347 348- describe lambda: 349 - before: 350 f = M.lambda 351 352 - context with bad arguments: 353 badargs.diagnose (f, "std.functional.lambda (string)") 354 355 examples {["it diagnoses bad lambda string"] = function () 356 expect (select (2, f "foo")).to_be "invalid lambda string 'foo'" 357 end} 358 examples {["it diagnoses an uncompilable expression"] = function () 359 expect (select (2, f "||+")).to_be "invalid lambda string '||+'" 360 expect (select (2, f "=")).to_be "invalid lambda string '='" 361 end} 362 363 - context with argument format: 364 - it returns a function: 365 expect (prototype (f "|x| 1+x")).to_be "function" 366 - it compiles to a working Lua function: 367 fn = f "||42" 368 expect (fn ()).to_be (42) 369 - it propagates argument values: 370 fn = f "|...| {...}" 371 expect (fn (1,2,3)).to_equal {1,2,3} 372 - context with expression format: 373 - it returns a function: 374 expect (prototype (f "_")).to_be "function" 375 - it compiles to a working Lua function: 376 fn = f "=42" 377 expect (fn ()).to_be (42) 378 - it sets auto-argument values: 379 fn = f "_*_" 380 expect (fn (42)).to_be (1764) 381 - it sets numeric auto-argument values: 382 fn = f "_1+_2+_3" 383 expect (fn (1, 2, 5)).to_be (8) 384 385 386- describe map: 387 - before: 388 elements = {"a", "b", "c", "d", "e"} 389 inverse = {a=1, b=2, c=3, d=4, e=5} 390 391 f = M.map 392 393 - context with bad arguments: 394 badargs.diagnose (f, "std.functional.map (func, [func], any*)") 395 396 - it works with an empty table: 397 expect (f (M.id, ipairs, {})).to_equal {} 398 - it iterates through elements: 399 expect (f (M.id, ipairs, elements)).to_equal (elements) 400 expect (f (M.id, pairs, inverse)).to_contain.a_permutation_of (elements) 401 - it propagates nil arguments correctly: 402 t = {"a", nil, nil, "d", "e"} 403 expect (f (M.id, base.npairs, t)).to_equal (t) 404 t = {nil, nil, 3, 4} 405 expect (f (M.id, base.npairs, t)).to_equal (t) 406 - it passes all iteration result values to map function: 407 t = {} 408 f (function (k, v) t[k] = v end, pairs, elements) 409 expect (t).to_equal (elements) 410 - it returns a list of mapped single return value iterator results: 411 expect (f (function (e) return e:match "[aeiou]" end, base.ielems, elements)). 412 to_equal {"a", "e"} 413 expect (f (function (e) return e .. "x" end, base.elems, elements)). 414 to_contain.a_permutation_of {"ax", "bx", "cx", "dx", "ex"} 415 - it returns a table of mapped key:value iterator results: 416 t = {"first", second=2, last="three"} 417 expect (f (function (k, v) return type (v) == "string" end, pairs, t)). 418 to_contain.a_permutation_of {true, false, true} 419 expect (f (function (k, v) return k % 2 == 0 end, ipairs, elements)). 420 to_equal {false, true, false, true, false} 421 - it supports key:value results from mapping function: 422 expect (f (function (k, v) return v, k end, pairs, elements)). 423 to_equal (inverse) 424 - it defaults to pairs iteration: 425 t = {"first", second=2, last="three"} 426 expect (f (function (k, v) return type (v) == "string" end, t)). 427 to_contain.a_permutation_of {true, false, true} 428 429 430- describe map_with: 431 - before: 432 t = {{1, 2, 3}, {4, 5}} 433 fn = function (...) return select ("#", ...) end 434 435 f = M.map_with 436 437 - context with bad arguments: 438 badargs.diagnose (f, "std.functional.map_with (func, table of tables)") 439 440 - it works for an empty table: 441 expect (f (fn, {})).to_equal ({}) 442 - it returns a table: 443 u = f (fn, t) 444 expect (type (u)).to_be "table" 445 - it creates a new table: 446 old = t 447 u = f (fn, t) 448 expect (t).to_equal (old) 449 expect (u).not_to_equal (old) 450 expect (t).to_equal {{1, 2, 3}, {4, 5}} 451 - it maps a function over a list of argument lists: 452 expect (f (fn, t)).to_equal {3, 2} 453 - it discards hash-part arguments: 454 expect (f (fn, {{1,x=2,3}, {4,5,y="z"}})).to_equal {2, 2} 455 - it maps a function over a table of argument lists: 456 expect (f (fn, {a={1,2,3}, b={4,5}})).to_equal {a=3, b=2} 457 458 459- describe memoize: 460 - before: 461 f = M.memoize 462 463 memfn = f (function (x) 464 if x then return {x} else return nil, "bzzt" end 465 end) 466 467 - context with bad arguments: 468 badargs.diagnose (f, "std.functional.memoize (func, ?func)") 469 470 - it propagates multiple return values: 471 expect (select (2, memfn (false))).to_be "bzzt" 472 - it returns the same object for the same arguments: 473 t = memfn (1) 474 expect (memfn (1)).to_be (t) 475 - it returns a different object for different arguments: 476 expect (memfn (1)).not_to_be (memfn (2)) 477 - it returns the same object for table valued arguments: 478 t = memfn {1, 2, 3} 479 expect (memfn {1, 2, 3}).to_be (t) 480 t = memfn {foo = "bar", baz = "quux"} 481 expect (memfn {foo = "bar", baz = "quux"}).to_be (t) 482 expect (memfn {baz = "quux", foo = "bar"}).to_be (t) 483 - it returns a different object for different table arguments: 484 expect (memfn {1, 2, 3}).not_to_be (memfn {1, 2}) 485 expect (memfn {1, 2, 3}).not_to_be (memfn {3, 1, 2}) 486 expect (memfn {1, 2, 3}).not_to_be (memfn {1, 2, 3, 4}) 487 - it accepts alternative normalization function: 488 normalize = function (...) return select ("#", ...) end 489 memfn = f (function (x) return {x} end, normalize) 490 expect (memfn "same").to_be (memfn "not same") 491 expect (memfn (1, 2)).to_be (memfn (false, "x")) 492 expect (memfn "one").not_to_be (memfn ("one", "two")) 493 494 495- describe nop: 496 - before: 497 f = M.nop 498 - it accepts any number of arguments: 499 expect (f ()).to_be (nil) 500 expect (f (false)).to_be (nil) 501 expect (f (1, 2, 3, nil, "str", {}, f)).to_be (nil) 502 - it returns no values: 503 expect (f (1, "two", false)).to_be (nil) 504 505 506- describe op: 507 - context with []: 508 - before: 509 f = M.op["[]"] 510 511 - it writes a deprecation warning: 512 setdebug { deprecate = "nil" } 513 expect (capture (f, {{2}, 1})). 514 to_contain_error "was deprecated" 515 setdebug { deprecate = false } 516 expect (capture (f, {{2}, 1})). 517 not_to_contain_error "was deprecated" 518 519 - it dereferences a table: 520 expect (f ({}, 1)).to_be (nil) 521 expect (f ({"foo", "bar"}, 1)).to_be "foo" 522 expect (f ({foo = "bar"}, "foo")).to_be "bar" 523 524 - context with +: 525 - before: 526 f = M.op["+"] 527 528 - it writes a deprecation warning: 529 setdebug { deprecate = "nil" } 530 expect (capture (f, {2, 1})). 531 to_contain_error "was deprecated" 532 setdebug { deprecate = false } 533 expect (capture (f, {2, 1})). 534 not_to_contain_error "was deprecated" 535 536 - it returns the sum of its arguments: 537 expect (f (99, 2)).to_be (99 + 2) 538 539 - context with -: 540 - before: 541 f = M.op["-"] 542 543 - it writes a deprecation warning: 544 setdebug { deprecate = "nil" } 545 expect (capture (f, {2, 1})). 546 to_contain_error "was deprecated" 547 setdebug { deprecate = false } 548 expect (capture (f, {2, 1})). 549 not_to_contain_error "was deprecated" 550 551 - it returns the difference of its arguments: 552 expect (f (99, 2)).to_be (99 - 2) 553 554 - context with *: 555 - before: 556 f = M.op["*"] 557 558 - it writes a deprecation warning: 559 setdebug { deprecate = "nil" } 560 expect (capture (f, {2, 1})). 561 to_contain_error "was deprecated" 562 setdebug { deprecate = false } 563 expect (capture (f, {2, 1})). 564 not_to_contain_error "was deprecated" 565 566 - it returns the product of its arguments: 567 expect (f (99, 2)).to_be (99 * 2) 568 569 - context with /: 570 - before: 571 f = M.op["/"] 572 573 - it writes a deprecation warning on: 574 setdebug { deprecate = "nil" } 575 expect (capture (f, {2, 1})). 576 to_contain_error "was deprecated" 577 setdebug { deprecate = false } 578 expect (capture (f, {2, 1})). 579 not_to_contain_error "was deprecated" 580 581 - it returns the quotient of its arguments: 582 expect (f (99, 2)).to_be (99 / 2) 583 584 - context with and: 585 - before: 586 f = M.op["and"] 587 588 - it writes a deprecation warning: 589 setdebug { deprecate = "nil" } 590 expect (capture (f, {true, false})). 591 to_contain_error "was deprecated" 592 setdebug { deprecate = false } 593 expect (capture (f, {true, false})). 594 not_to_contain_error "was deprecated" 595 596 - it returns the logical and of its arguments: 597 expect (f (false, false)).to_be (false) 598 expect (f (false, true)).to_be (false) 599 expect (f (true, false)).to_be (false) 600 expect (f (true, true)).to_be (true) 601 - it supports truthy and falsey arguments: 602 expect (f ()).to_be (nil) 603 expect (f (0)).to_be (nil) 604 expect (f (nil, 0)).to_be (nil) 605 expect (f (0, "false")).to_be ("false") 606 607 - context with or: 608 - before: 609 f = M.op["or"] 610 611 - it writes a deprecation warning: 612 setdebug { deprecate = "nil" } 613 expect (capture (f, {true, false})). 614 to_contain_error "was deprecated" 615 setdebug { deprecate = false } 616 expect (capture (f, {true, false})). 617 not_to_contain_error "was deprecated" 618 619 - it returns the logical or of its arguments: 620 expect (f (false, false)).to_be (false) 621 expect (f (false, true)).to_be (true) 622 expect (f (true, false)).to_be (true) 623 expect (f (true, true)).to_be (true) 624 - it supports truthy and falsey arguments: 625 expect (f ()).to_be (nil) 626 expect (f (0)).to_be (0) 627 expect (f (nil, 0)).to_be (0) 628 expect (f (0, "false")).to_be (0) 629 630 - context with not: 631 - before: 632 f = M.op["not"] 633 634 - it writes a deprecation warning: 635 setdebug { deprecate = "nil" } 636 expect (capture (f, {true})). 637 to_contain_error "was deprecated" 638 setdebug { deprecate = false } 639 expect (capture (f, {true})). 640 not_to_contain_error "was deprecated" 641 642 - it returns the logical not of its argument: 643 expect (f (false)).to_be (true) 644 expect (f (true)).to_be (false) 645 - it supports truthy and falsey arguments: 646 expect (f ()).to_be (true) 647 expect (f (0)).to_be (false) 648 649 - context with ==: 650 - before: 651 f = M.op["=="] 652 653 - it writes a deprecation warning: 654 setdebug { deprecate = "nil" } 655 expect (capture (f, {2, 1})). 656 to_contain_error "was deprecated" 657 setdebug { deprecate = false } 658 expect (capture (f, {2, 1})). 659 not_to_contain_error "was deprecated" 660 661 - it returns true if the arguments are equal: 662 expect (f ()).to_be (true) 663 expect (f ("foo", "foo")).to_be (true) 664 - it returns false if the arguments are unequal: 665 expect (f (1)).to_be (false) 666 expect (f ("foo", "bar")).to_be (false) 667 668 - context with ~=: 669 - before: 670 f = M.op["~="] 671 672 - it writes a deprecation warning: 673 setdebug { deprecate = "nil" } 674 expect (capture (f, {2, 1})). 675 to_contain_error "was deprecated" 676 setdebug { deprecate = false } 677 expect (capture (f, {2, 1})). 678 not_to_contain_error "was deprecated" 679 680 - it returns false if the arguments are equal: 681 expect (f (1, 1)).to_be (false) 682 expect (f ("foo", "foo")).to_be (false) 683 - it returns true if the arguments are unequal: 684 expect (f (1, 2)).to_be (true) 685 expect (f ("foo", "bar")).to_be (true) 686 expect (f ({}, {})).to_be (true) 687 688 689- describe reduce: 690 - before: 691 op = require "std.operator" 692 693 f = M.reduce 694 695 - context with bad arguments: 696 badargs.diagnose (f, "std.functional.reduce (func, any, [func], any*)") 697 698 - it works with an empty table: 699 expect (f (op.sum, 2, ipairs, {})).to_be (2) 700 - it calls a binary function over single return value iterator results: 701 expect (f (op.sum, 2, base.ielems, {3})). 702 to_be (2 + 3) 703 expect (f (op.prod, 2, base.ielems, {3, 4})). 704 to_be (2 * 3 * 4) 705 - it calls a binary function over key:value iterator results: 706 expect (f (op.sum, 2, base.ielems, {3})).to_be (2 + 3) 707 expect (f (op.prod, 2, base.ielems, {3, 4})).to_be (2 * 3 * 4) 708 - it propagates nil arguments correctly: 709 function set (t, k, v) t[k] = tostring (v) return t end 710 expect (f (set, {}, base.npairs, {1, nil, nil, "a", false})). 711 to_equal {"1", "nil", "nil", "a", "false"} 712 expect (f (set, {}, base.npairs, {nil, nil, "3"})). 713 to_equal {"nil", "nil", "3"} 714 - it reduces elements from left to right: 715 expect (f (op.pow, 2, base.ielems, {3, 4})).to_be ((2 ^ 3) ^ 4) 716 - it passes all iterator results to accumulator function: 717 expect (f (rawset, {}, {"one", two=5})).to_equal {"one", two=5} 718 719 720- describe zip: 721 - before: 722 tt = {{1, 2}, {3, 4}, {5, 6}} 723 724 f = M.zip 725 726 - context with bad arguments: 727 badargs.diagnose (f, "std.functional.zip (table)") 728 729 - it works for an empty table: 730 expect (f {}).to_equal {} 731 - it is the inverse of itself: 732 expect (f (f (tt))).to_equal (tt) 733 - it transposes rows and columns: 734 expect (f (tt)).to_equal {{1, 3, 5}, {2, 4, 6}} 735 expect (f {x={a=1, b=2}, y={a=3, b=4}, z={b=5}}). 736 to_equal {a={x=1, y=3}, b={x=2,y=4,z=5}} 737 738 739- describe zip_with: 740 - before: 741 tt = {{1, 2}, {3, 4}, {5}} 742 fn = function (...) return tonumber (table.concat {...}) end 743 744 f = M.zip_with 745 746 - context with bad arguments: 747 badargs.diagnose (f, "std.functional.zip_with (function, table of tables)") 748 749 - it works for an empty table: 750 expect (f (fn, {})).to_equal {} 751 - it returns a table: 752 expect (type (f (fn, tt))).to_be "table" 753 - it returns the result in a new table: 754 expect (f (fn, tt)).not_to_be (tt) 755 - it does not perturb the argument list: 756 m = f (fn, tt) 757 expect (tt).to_equal {{1, 2}, {3, 4}, {5}} 758 - it combines column entries with a function: 759 expect (f (fn, tt)).to_equal {135, 24} 760 - it discards hash-part arguments: 761 expect (f (fn, {{1,2}, x={3,4}, {[2]=5}})).to_equal {1, 25} 762 - it combines matching key entries with a function: 763 expect (f (fn, {{a=1,b=2}, {a=3,b=4}, {b=5}})). 764 to_equal {a=13, b=245} 765