The collections library for Teal projects built with htl:
what Penlight's tablex / List / seq / OrderedMap, Lua's table, lodash, Kotlin's
kotlin.collections, Go's slices / maps and Deno's @std/collections have in common,
written once, typed, for the two table shapes Teal has.
Pure Teal, no host. Four modules:
| module | over | what |
|---|---|---|
htlx.list |
arrays {T} |
walk, cut, order, combine, split, set operations, random, ask |
htlx.tablex |
maps {K:V}, and any table for deep_* / freeze |
keys and entries, pick and merge, deep copy / equals / merge, read-only views |
htlx.seq |
iterators function(): T |
the lazy side: a pipeline over an array, a map or an endless rule, pulled one element at a time |
htlx.ordered |
Map<K, V> and Set<T> records |
maps and sets that remember the order things were put in, which pairs does not |
require("htlx") gives all four as htlx.list, htlx.tablex, htlx.seq and htlx.ordered.
In your project's mlua-pkg.toml:
[deps]
htlx = { git = "https://github.com/ynishi/htl-x", tag = "v0.1.0" }then
htl pkg installand in any .tl:
local list = require("htlx.list")
local tablex = require("htlx.tablex")
local seq = require("htlx.seq")
local ordered = require("htlx.ordered")
for name, count in tablex.sorted_pairs(counts) do -- pairs, in key order
print(name, count)
end
local wanted = list.to_set({ "json", "quiet" }) -- { json = true, quiet = true }
local ids = list.map(rows, function(row: Row): string return row.id end)
local by_kind = list.group_by(rows, function(row: Row): string return row.kind end)
local oldest = list.min_by(rows, function(row: Row): integer return row.born end)
local firsts = seq.collect(seq.take(seq.map(seq.iterate(1, function(n: integer): integer
return n * 2
end), tostring), 5)) -- { "1", "2", "4", "8", "16" }
local m: ordered.Map<string, integer> = ordered.map()
m:set("b", 2); m:set("a", 1)
for k, v in m:pairs() do print(k, v) end -- b 2, then a 1 The [deps] key is the require root, so it has to be htlx. Needs an htl that applies
a dependency's entry — main after ynishi/htl#204, or the release after 0.4.0; on
0.4.0 itself every require("htlx.*") is module not found.
htlx.list — arrays {T}:
| walk | map filter filter_map flat_map each fold any all find find_index index_of last_index_of contains enumerate |
| cut | take drop take_while drop_while slice chunk |
| order | sorted sorted_by reverse min max min_by max_by sum sum_of binary_search |
| combine | concat flatten zip unzip range join fill |
| split | partition group_by uniq uniq_by |
| sets | to_set union intersect difference |
| random | shuffle sample |
| ask | equals is_empty len copy |
| stack | push pop |
htlx.tablex — maps {K:V}:
| walk | keys values sorted_keys sorted_pairs each entries from_entries |
| shape | map map_keys filter merge pick omit invert |
| ask | get_or count is_empty equals copy |
| any table | deep_copy deep_equals deep_merge freeze is_frozen |
htlx.seq — iterators function(): T:
| in | of keys values entries range iterate |
| through | map filter filter_map flat_map take drop take_while drop_while zip enumerate chain |
| out | collect fold any all find each to_map count |
htlx.ordered — Map<K, V> and Set<T>:
| make | map from_entries from_table set from is_map is_set |
Map |
set get get_or has delete keys values entries pairs len is_empty copy each sorted to_table |
Set |
add has delete values iter len is_empty copy each union intersect difference is_subset to_table |
Each function's doc comment, above it in the source, is the contract.
Every function in every module keeps these:
- Argument bugs raise. A non-table where a table is due, or a non-function for
f, is anerrornaming the function, not anil, errreturn. Those are for the caller's code, not the caller's data. nilnever goes into an array, and ends a sequence.{T}is a Lua sequence; a hole stops#and every loop after it.list.push(t, nil)raises,list.mapraises whenfreturnsnil, andfilter/filter_mapare the way to drop elements. Inseq,nilis the end;falseis an element.- Nothing is mutated except what exists to mutate:
list.push/pop/shuffle, and theset/add/deletemethods ofordered. Every other function returns a new table (freezereturns a view). Insertion and removal at a position, and sorting in place, aretable.insert/table.remove/table.sort, which Lua already has. - A lookup that finds nothing returns
nil(find,min,pop,get, ...). Teal cannot writeT | nil; the doc comment says it where it applies.get_oris the one that does not. _bytakes a key function (sorted_by,min_by,group_by,uniq_by), as in Kotlin, Deno and lodash. A comparator is always the last, optional argument and is<astable.sorttakes it:function(a, b): boolean, true whenasorts first. With none,<is used, and elements it cannot compare raise at run time.- Arrays come back as arrays.
keys,values,map,filterreturn{T}; the iterators aresorted_pairs,enumerate,ordered'spairs/iter, and everything inseq. - A map and an array never share a signature. They are different types in Teal, so
tablex.mapandlist.mapare two functions and neither accepts the other's argument.deep_copy,deep_equals,deep_mergeandfreezetake any table asTand give the sameTback.
Two things Teal makes you spell:
- A function type followed by more parameters needs parentheses —
key: (function(T): K), cmp?: ...— or the return list swallows what follows. ordered.map()andordered.set()need the type on the variable —local m: ordered.Map<string, integer> = ordered.map()— since nothing else fixesKandV.from_entries/from/from_tableinfer it.
- string helpers, argparse, pretty-printing, JSON, IO, regex: those are on the Rust side
(mlua-batteries,
std.*). This package is the Teal side: tables only. - in-place insert / remove / sort / clear:
table.*has them.
htl check . # type-check + lints (strict)
htl test # tests/*_test.tl via htl.test
htl fmt . # whitespace formatterSources are under src/htlx/; mlua-pkg.toml entry = "src/htlx" is what makes a
consumer's require("htlx.list") land on src/htlx/list.tl.
MIT or Apache-2.0, at your option — the same as htl. See LICENSE-MIT and
LICENSE-APACHE.