collection.fn writes a function and its dot narrowings in one block (the other kind, collection.handler, is in collection_handlers.zg).
collections.zg
# Collections (owner, 2026-10-13): `collection.fn` writes a function and its dot narrowings in one# block (the other kind, `collection.handler`, is in collection_handlers.zg). The own function carries# the collection's name (no `fn`), a member with its own body is reached as `greet.loud`, and a preset,# a member that only fills some of the own function's arguments, is one line: `greet.hi['who']` is# `greet[lend 'who', "hi", 1]`. Private here, so no `pub` and no `bare.` mark; a `pub collection.fn`# makes every member public under its one `bare.` mark.collection.fngreet{greet[lent'who':str.utf8,'word':str.utf8,'times':int32[min1,max5]]->str.utf8orfail[too_long]{ifstr.bytes[lend'who']>20{failtoo_long;}let'out':=str.empty;for'i'from1to'times'by1{change'out'='out'++'word'++", ";}return'out'++'who';}hi['word'="hi",'times'=1];cheer['word'="hooray",'times'=3];loud[lent'who':str.utf8]->str.utf8{returnstd::str.upper[lend'who'];}}# A template (owner, 2026-10-13) makes members whose names follow a pattern. It loops with the usual# `for` forms over values known before building, its first line `named` works out each member's name# like text, and `zomp generate` (here `oracle --generate`) writes what it makes into this file,# under the `#generated_N_lines_below` marker. The build only reads that part, and refuses it when it# no longer matches the template; it is never edited by hand. Members that only fix an argument are# presets, as `scale.times_2` here (Good practice 14): a template is for what a preset cannot make,# such as a member whose signature changes with the value, as `zero`'s below.const'big':=100;collection.fnscale{scale['x':int32,'by':int32]->int32{return'x'times'by';}times_2['by'=2];times_10['by'=10];times_100['by'='big'];}# A template over types: each member returns its own type, which no preset can do. A type's name is# text in `named`.collection.fnzero{for'T'in[int16,int64]{named"of_"++'T';[]->'T'{return0;}}#generated_2_lines_belowof_int16[]->int16{return0;}of_int64[]->int64{return0;}}# `named` may call a function, run while generating, when a name needs logic (owner, 2026-10-13:# `named size_name['n'];`). A type is its name's text there.fnwidth_name['t':str.utf8]->str.utf8{if't'equal_to"int16"{return"small";}return"big";}collection.fntwice{for'T'in[int16,int64]{namedwidth_name['T'];['x':'T']->'T'{return'x'times2;}}#generated_2_lines_belowsmall['x':int16]->int16{return'x'times2;}big['x':int64]->int64{return'x'times2;}}MAIN{let'name':="Ann";let'a':=greet[lend'name',"hello",2]otherwise"?";let'b':=greet.hi[lend'name']otherwise"?";let'c':=greet.cheer[lend'name']otherwise"?";std::print.useful['a',newln,'b',newln,'c',newln,greet.loud[lend'name'],newln];let'long':="a name much longer than twenty bytes";let'd':=greet.hi[lend'long']otherwise"too long";std::print.useful['d',newln];std::print.useful[scale[7,3]," ",scale.times_2[7]," ",scale.times_10[7]," ",scale.times_100[7]," ",zero.of_int64[]," ",twice.small[300]," ",twice.big[5_000_000_000],newln];}
Output
hello, hello, Ann
hi, Ann
hooray, hooray, hooray, Ann
ANN
too long
21 14 70 700 0 600 10000000000