lazy<TDecoded>(factory: () => Coder<TDecoded>): Coder<TDecoded>
Wraps a coder factory so the coder it produces is built on first use
instead of when lazy() is called, and only ever built once.
Use this to close mutually-recursive coder graphs — for example a
tunneling protocol whose payload can itself contain the outer protocol
(VXLAN carrying Ethernet carrying IPv4 carrying UDP carrying VXLAN again).
Writing that graph with plain consts is impossible: whichever coder is
declared last needs the first one, which is still in its temporal dead
zone. Wrapping the back-reference in lazy(() => firstCoder) defers
reading firstCoder until an encode/decode call actually happens,
by which point every top-level const in the module has been assigned.
The factory is called at most once. Its result is cached and reused for
every subsequent encode/decode on this lazy() coder, so building the
inner coder graph (e.g. calling struct()/refineSwitch()) is not
repeated per call.
Deferring construction until first use
Deferring construction until first use
import { assertEquals } from "@std/assert"; import { lazy } from "@hertzg/binstruct/lazy"; import { u16le } from "@hertzg/binstruct/numeric"; let builds = 0; const coder = lazy(() => { builds++; return u16le(); }); assertEquals(builds, 0); // not built yet const buffer = new Uint8Array(4); coder.encode(513, buffer); coder.decode(buffer); coder.encode(7, buffer); assertEquals(builds, 1); // built once, then memoized