{"entries":[{"anchor":"type-ordering","kind":"type","line":27,"name":"Ordering","qualifiedName":"Ordering","signatures":[],"summary":"Algebraic structures, ordering, and combining values associatively.\n\nKex traits do not inherit from one another, so concrete types explicitly implement every structure whose laws they satisfy.\n\nThe two most important things that you will meet in everyday code. `Ordering` is what a comparison answers, and it composes. This is how a multi-key sort is written without nested `if`s:\n\n```kex\na.age.compare(b.age).thenBy { a.score.compare(b.score) }\n```\n\n`Monoid` is \"these two values combine, and there is a neutral one\". Numbers, strings and lists all satisfy it, which is what lets `repeat` be written once:\n\n```kex\n\"ab\".repeat(3)   # => \"ababab\"\n[1].repeat(2)    # => [1, 1]\n5.repeat(3)      # => 15\n```\n\nThe result of a comparison: `Less`, `Equal` or `Greater`.\n\nDeclared here rather than only inside the interpreter so that `Ordering`, `Less`, `Equal` and `Greater` reach the semantic layer the same way every other stdlib type does (through the collected interfaces) instead of existing solely as native environment bindings the type checker and name resolver cannot see.","types":[],"urlPath":"algebra"},{"anchor":"trait-comparable","kind":"trait","line":32,"name":"Comparable","qualifiedName":"Comparable","signatures":["compare : This -> Ordering"],"summary":"Types that have a total order.\n\nImplemented by `Number`, which covers both `Integer` and `Float`.","types":["(This) -> Ordering"],"urlPath":"algebra"},{"anchor":"fn-compare","kind":"function","line":51,"name":"compare","qualifiedName":"Comparable.compare","signatures":["compare : This -> Ordering"],"summary":"Compares this value with `other` and answers `Less`, `Equal` or `Greater`.\n\n`==` stays independent of this: a type may be equatable without being ordered.\n\nA total order: `compare` answers Less, Equal or Greater. `==` stays independent: a type may be Equatable without being ordered.","types":["(This) -> Ordering"],"urlPath":"algebra"},{"anchor":"make-number","kind":"make","line":57,"name":"Number","qualifiedName":"Number","signatures":["compare(other)"],"summary":"Number carries the implementation, so Integer and Float both inherit it rather than repeating the same three comparisons. Mixed receivers work because `<` and `>` promote across the two (`1.compare(1.0)` is Equal).","types":[],"urlPath":"algebra"},{"anchor":"fn-compare","kind":"function","line":70,"name":"compare","qualifiedName":"Number.compare","signatures":["compare(other)"],"summary":"Compares two numbers, across the `Integer`/`Float` boundary.\n\nMixed receivers work because `<` and `>` promote across the two, so `1.compare(1.0)` is `Equal`.","types":[],"urlPath":"algebra"},{"anchor":"trait-monoid","kind":"trait","line":83,"name":"Monoid","qualifiedName":"Monoid","signatures":["identity : This","combine : This -> This","repeat(0)"],"summary":"Types whose values combine associatively and have a neutral element.\n\nImplemented by `Integer` (addition), `String` and `List` (concatenation), `Map` and both `Set` flavours (union), and `Ordering` (\"first decision wins\").","types":["This","(This) -> This"],"urlPath":"algebra"},{"anchor":"fn-identity","kind":"function","line":92,"name":"identity","qualifiedName":"Monoid.identity","signatures":["identity : This"],"summary":"The neutral element: combining it with any value gives that value back.\n\n`combine` must be associative and `identity` neutral on both sides.","types":["This"],"urlPath":"algebra"},{"anchor":"fn-combine","kind":"function","line":109,"name":"combine","qualifiedName":"Monoid.combine","signatures":["combine : This -> This"],"summary":"Combines this value with `other`.\n\nMust be associative: `a.combine(b).combine(c)` and `a.combine(b.combine(c))` have to agree.","types":["(This) -> This"],"urlPath":"algebra"},{"anchor":"fn-repeat","kind":"function","line":128,"name":"repeat","qualifiedName":"Monoid.repeat","signatures":["repeat(0)"],"summary":"Combines this value with itself `n` times.\n\nRepeating zero times gives the identity: `\"\"` for a string, `0` for an integer, `[]` for a list. A negative count is invalid and ends the program.","types":[],"urlPath":"algebra"},{"anchor":"trait-group","kind":"trait","line":140,"name":"Group","qualifiedName":"Group","signatures":["identity : This","combine : This -> This","inverse : This"],"summary":"A `Monoid` in which every value has an inverse that combines with it to give the identity.\n\nImplemented by `Integer`, where the inverse is negation.","types":["This","(This) -> This"],"urlPath":"algebra"},{"anchor":"fn-identity","kind":"function","line":144,"name":"identity","qualifiedName":"Group.identity","signatures":["identity : This"],"summary":"The neutral element.","types":["This"],"urlPath":"algebra"},{"anchor":"fn-combine","kind":"function","line":150,"name":"combine","qualifiedName":"Group.combine","signatures":["combine : This -> This"],"summary":"Combines this value with `other`.","types":["(This) -> This"],"urlPath":"algebra"},{"anchor":"fn-inverse","kind":"function","line":159,"name":"inverse","qualifiedName":"Group.inverse","signatures":["inverse : This"],"summary":"The value that combines with this one to give the identity.","types":["This"],"urlPath":"algebra"},{"anchor":"make-integer","kind":"make","line":163,"name":"Integer","qualifiedName":"Integer","signatures":["combine(other)"],"summary":"Implements `Monoid`, `Group` over `Integer` for addition.","types":[],"urlPath":"algebra"},{"anchor":"fn-combine","kind":"function","line":180,"name":"combine","qualifiedName":"Integer.combine","signatures":["combine(other)"],"summary":"Adds `other` to this integer. Addition is the monoid operation for `Integer`.","types":[],"urlPath":"algebra"},{"anchor":"make-string","kind":"make","line":193,"name":"String","qualifiedName":"String","signatures":["combine(other)"],"summary":"Implements `Monoid` over `String` for concatenation.","types":[],"urlPath":"algebra"},{"anchor":"fn-combine","kind":"function","line":210,"name":"combine","qualifiedName":"String.combine","signatures":["combine(other)"],"summary":"Concatenates `other` onto this string. Concatenation is the monoid operation for `String`.","types":[],"urlPath":"algebra"},{"anchor":"make-[a]","kind":"make","line":214,"name":"[A]","qualifiedName":"[A]","signatures":["combine(other)"],"summary":"Implements `Monoid` over `List<A>` for concatenation.","types":[],"urlPath":"algebra"},{"anchor":"fn-combine","kind":"function","line":234,"name":"combine","qualifiedName":"[A].combine","signatures":["combine(other)"],"summary":"Concatenates `other` onto this list. Concatenation is the monoid operation for `List`.","types":[],"urlPath":"algebra"},{"anchor":"make-ordering","kind":"make","line":244,"name":"Ordering","qualifiedName":"Ordering","signatures":["combine(@Equal, other)","reverse : Ordering","thenBy : Block<Ordering> -> Ordering"],"summary":"`Ordering` is a Monoid under \"first decision wins\", with Equal as identity. That is what makes multi-key comparison compose instead of nesting ifs:\n\n```kex\na.name.compare(b.name).combine(a.age.compare(b.age))\n```\n\n`combine` evaluates its argument eagerly, so the later comparison runs even when the earlier one already decided. Use `thenBy` when that matters.","types":["Ordering","(Block<Ordering>) -> Ordering"],"urlPath":"algebra"},{"anchor":"fn-combine","kind":"function","line":267,"name":"combine","qualifiedName":"Ordering.combine","signatures":["combine(@Equal, other)"],"summary":"Returns the first decisive ordering: this one if it is not `Equal`, otherwise `other`.\n\nThis is what makes multi-key comparison compose. Note that `other` is evaluated eagerly, so the later comparison runs even when the earlier one has already decided: use `thenBy` when that matters.","types":[],"urlPath":"algebra"},{"anchor":"fn-reverse","kind":"function","line":285,"name":"reverse","qualifiedName":"Ordering.reverse","signatures":["reverse : Ordering"],"summary":"Returns the opposite ordering: `Less` becomes `Greater`, `Greater` becomes `Less`, and `Equal` stays `Equal`.\n\nThe one-word way to turn an ascending comparison into a descending one.","types":["Ordering"],"urlPath":"algebra"},{"anchor":"fn-thenby","kind":"function","line":307,"name":"thenBy","qualifiedName":"Ordering.thenBy","signatures":["thenBy : Block<Ordering> -> Ordering"],"summary":"Returns this ordering if it is decisive, otherwise the result of calling `tieBreaker`.\n\nThe short-circuiting form of `combine`: the block runs only when this comparison is `Equal`, so a tie-breaker costs nothing once the order is already decided. Prefer it whenever the tie-breaker is more than a field read.","types":["(Block<Ordering>) -> Ordering"],"urlPath":"algebra"},{"anchor":"type-binary","kind":"type","line":27,"name":"Binary","qualifiedName":"Binary","signatures":[],"summary":"An opaque, immutable sequence of bytes. A `Binary` never implicitly becomes text.\n\n`[Byte]` is the materialized list of bytes, while `Binary` is the storage form. A file, an HTTP body, or a digest is a `Binary`. On the BEAM it is a native binary, so slicing shares storage instead of copying. None of the operations here walks a character list.\n\n```kex\nlet data = Binary.fromHex(\"00686900\").try\ndata.length            # => 4\ndata.at(1)             # => Just(104)\ndata.take(2).hex       # => \"0068\"\ndata.showValue         # => \"#Binary<4 bytes>\"\n```\n\nThe type is opaque on purpose: there is no field to reach through, so text operations cannot be run on bytes by accident. Conversions are always explicit in both directions, and both directions can fail. Runtime conversions through `to` always return an `Optional<T>`:\n\n```kex\n\"árvíz\".to(Binary)                  #  => Just(#Binary<7 bytes>) : Binary?\nBinary.fromBytes([255]).to(String)  # => None\n```\n\n`Showable` and `Inspectable` deliberately render the length alone. Neither ever decodes or interpolates the payload, so printing a binary cannot leak its contents or fail on bytes that are not text. Use `hex`, `base64`, or `to(String)` when you actually want to see it.","types":[],"urlPath":"binary"},{"anchor":"module-binary","kind":"module","line":29,"name":"Binary","qualifiedName":"Binary","signatures":["fromBytes(values) : [Byte] -> Binary","fromHex(text) : String -> Binary?","fromBase64(text) : String -> Binary?"],"summary":"","types":["Binary","[Byte]","String","Binary?"],"urlPath":"binary"},{"anchor":"constant-empty","kind":"constant","line":39,"name":"empty","qualifiedName":"Binary.empty","signatures":[],"summary":"The binary holding no bytes.\n\nBacked by `Binary.fromBytes([])`, this is the natural starting value this library uses, just like (`Headers.empty`).","types":["Binary"],"urlPath":"binary"},{"anchor":"function-frombytes","kind":"function","line":53,"name":"fromBytes","qualifiedName":"Binary.fromBytes","signatures":["fromBytes(values) : [Byte] -> Binary"],"summary":"Builds a binary from a list of bytes.\n\nTotal: every `Byte` is in `0..255` by construction, so there is no rejection case. `Binary.fromBytes([])` is the empty binary.","types":["[Byte]","Binary"],"urlPath":"binary"},{"anchor":"function-fromhex","kind":"function","line":74,"name":"fromHex","qualifiedName":"Binary.fromHex","signatures":["fromHex(text) : String -> Binary?"],"summary":"Decodes lowercase hexadecimal text.\n\nStrict, so that decoding is the exact inverse of `hex`: uppercase digits, an odd length, whitespace, a `0x` prefix, and any non-hex character all answer `None` rather than being repaired.","types":["String","Binary?"],"urlPath":"binary"},{"anchor":"function-frombase64","kind":"function","line":94,"name":"fromBase64","qualifiedName":"Binary.fromBase64","signatures":["fromBase64(text) : String -> Binary?"],"summary":"Decodes standard base64 text (RFC 4648).\n\nStrict, so that decoding is the exact inverse of `base64`: the URL-safe alphabet, missing or malformed padding, whitespace, and any other noncanonical encoding all answer `None`.","types":["String","Binary?"],"urlPath":"binary"},{"anchor":"make-binary","kind":"make","line":98,"name":"Binary","qualifiedName":"Binary","signatures":["at(index)","take(count)","drop(count)","+(other)","inspectValue(_)","to(String)"],"summary":"","types":[],"urlPath":"binary"},{"anchor":"fn-at","kind":"function","line":140,"name":"at","qualifiedName":"Binary.at","signatures":["at(index)"],"summary":"The byte at `index`, counting from zero.\n\nAnswers `None` for a negative or out-of-range index, so an index you did not check is something you handle rather than something that stops the program.","types":[],"urlPath":"binary"},{"anchor":"fn-take","kind":"function","line":155,"name":"take","qualifiedName":"Binary.take","signatures":["take(count)"],"summary":"The first `count` bytes.\n\nClamped at both ends, like the list operations: a `count` of zero or less answers the empty binary, and an oversized one answers the whole binary.","types":[],"urlPath":"binary"},{"anchor":"fn-drop","kind":"function","line":170,"name":"drop","qualifiedName":"Binary.drop","signatures":["drop(count)"],"summary":"Everything after the first `count` bytes.\n\nClamped at both ends: a `count` of zero or less answers the whole binary, and an oversized one answers the empty binary. `take(n)` and `drop(n)` therefore always concatenate back to the original.","types":[],"urlPath":"binary"},{"anchor":"fn-+","kind":"function","line":179,"name":"+","qualifiedName":"Binary.+","signatures":["+(other)"],"summary":"Joins two binaries end to end.","types":[],"urlPath":"binary"},{"anchor":"fn-inspectvalue","kind":"function","line":208,"name":"inspectValue","qualifiedName":"Binary.inspectValue","signatures":["inspectValue(_)"],"summary":"The length-only rendering: never the payload. The same as `showValue`.","types":[],"urlPath":"binary"},{"anchor":"fn-to","kind":"function","line":224,"name":"to","qualifiedName":"Binary.to","signatures":["to(String)"],"summary":"Decodes the payload as UTF-8, or answers `None` when the bytes are not valid text.\n\nThis deliberately differs from `showValue`, which reveals only the byte count. Converting asks to inspect the actual payload and therefore makes the possibility of invalid text explicit.","types":[],"urlPath":"binary"},{"anchor":"module-bits","kind":"module","line":14,"name":"Bits","qualifiedName":"Bits","signatures":["and(a, b) : Integer -> Integer -> Integer","or(a, b) : Integer -> Integer -> Integer","xor(a, b) : Integer -> Integer -> Integer","not(a) : Integer -> Integer","shiftLeft(n, by) : Integer -> Integer -> Integer","shiftRight(n, by) : Integer -> Integer -> Integer","test?(n, index) : Integer -> Integer -> Bool","set(n, index) : Integer -> Integer -> Integer","clear(n, index) : Integer -> Integer -> Integer","toggle(n, index) : Integer -> Integer -> Integer","count(n) : Integer -> Integer","width(n) : Integer -> Integer"],"summary":"Bitwise operations on `Integer`.\n\n```kex\nBits.and(0xff, 0x0f)      # => 15\nBits.shiftLeft(1, 8)      # => 256\nBits.test?(0b1010, 1)     # => true\n```\n\nUseful for packing flags into one number, reading a binary format, or working with a protocol that describes its fields in bits.\n\nIntegers are arbitrary precision, and a negative one behaves as if it were written in infinite-precision two's complement, so `Bits.not(0)` is `-1` and `Bits.and(-1, 255)` is `255`, with no word size to overflow. Shifts and bit indices count from bit 0 (the least significant bit).","types":["Integer","Bool"],"urlPath":"bits"},{"anchor":"function-and","kind":"function","line":30,"name":"and","qualifiedName":"Bits.and","signatures":["and(a, b) : Integer -> Integer -> Integer"],"summary":"Bitwise AND of `a` and `b`.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-or","kind":"function","line":44,"name":"or","qualifiedName":"Bits.or","signatures":["or(a, b) : Integer -> Integer -> Integer"],"summary":"Bitwise OR of `a` and `b`.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-xor","kind":"function","line":58,"name":"xor","qualifiedName":"Bits.xor","signatures":["xor(a, b) : Integer -> Integer -> Integer"],"summary":"Bitwise exclusive OR of `a` and `b`.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-not","kind":"function","line":70,"name":"not","qualifiedName":"Bits.not","signatures":["not(a) : Integer -> Integer"],"summary":"Bitwise complement of `a`. Every integer is signed and unbounded, so this is always `-(a ` 1)+ rather than a width-dependent mask.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-shiftleft","kind":"function","line":85,"name":"shiftLeft","qualifiedName":"Bits.shiftLeft","signatures":["shiftLeft(n, by) : Integer -> Integer -> Integer"],"summary":"Shifts `n` left by `by` bits. Raises if `by` is negative.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-shiftright","kind":"function","line":98,"name":"shiftRight","qualifiedName":"Bits.shiftRight","signatures":["shiftRight(n, by) : Integer -> Integer -> Integer"],"summary":"Shifts `n` right by `by` bits, propagating the sign: the result of shifting a negative number stays negative. Raises if `by` is negative.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-test?","kind":"function","line":110,"name":"test?","qualifiedName":"Bits.test?","signatures":["test?(n, index) : Integer -> Integer -> Bool"],"summary":"True when the bit at `index` of `n` is set. Raises if `index` is negative.","types":["Integer","Bool"],"urlPath":"bits"},{"anchor":"function-set","kind":"function","line":121,"name":"set","qualifiedName":"Bits.set","signatures":["set(n, index) : Integer -> Integer -> Integer"],"summary":"`n` with the bit at `index` set. Raises if `index` is negative.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-clear","kind":"function","line":132,"name":"clear","qualifiedName":"Bits.clear","signatures":["clear(n, index) : Integer -> Integer -> Integer"],"summary":"`n` with the bit at `index` cleared. Raises if `index` is negative.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-toggle","kind":"function","line":143,"name":"toggle","qualifiedName":"Bits.toggle","signatures":["toggle(n, index) : Integer -> Integer -> Integer"],"summary":"`n` with the bit at `index` flipped. Raises if `index` is negative.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-count","kind":"function","line":158,"name":"count","qualifiedName":"Bits.count","signatures":["count(n) : Integer -> Integer"],"summary":"Number of set bits in `n` (population count). A negative value has infinitely many under two's complement, so this raises for one.","types":["Integer"],"urlPath":"bits"},{"anchor":"function-width","kind":"function","line":171,"name":"width","qualifiedName":"Bits.width","signatures":["width(n) : Integer -> Integer"],"summary":"Number of bits needed to represent `n`, i.e. the position of its highest set bit plus one. Zero needs none. Raises for a negative value.","types":["Integer"],"urlPath":"bits"},{"anchor":"trait-blankable","kind":"trait","line":17,"name":"Blankable","qualifiedName":"Blankable","signatures":["blank? : Bool"],"summary":"`Blankable`: types that can be asked whether they hold anything meaningful.\n\nA value is blank when it has no meaningful content: `None`, an empty or all-whitespace string, an empty collection, or `false`. The semantics are Rails's, and the point is the same: one question that works across types, so a validation does not need a different test per field.\n\n```kex\n\"\".blank?        # => true\n\"   \".blank?     # => true   (unlike \"   \".empty?)\n[].blank?        # => true\nNone.blank?      # => true\n\"hi\".present?    # => true\n```\n\n`present?` is the negation, and is often what reads better:\n\n```kex\nfields.all? { |name, value| value.present? }\n```","types":["Bool"],"urlPath":"blankable"},{"anchor":"fn-blank?","kind":"function","line":29,"name":"blank?","qualifiedName":"Blankable.blank?","signatures":["blank? : Bool"],"summary":"Returns `true` when the value holds nothing meaningful.\n\nWhat that means is up to each type: whitespace-only for a string, no elements for a collection, `None` for an optional, `false` for a boolean.","types":["Bool"],"urlPath":"blankable"},{"anchor":"make-bool","kind":"make","line":44,"name":"Bool","qualifiedName":"Bool","signatures":[],"summary":"","types":[],"urlPath":"blankable"},{"anchor":"make-integer","kind":"make","line":56,"name":"Integer","qualifiedName":"Integer","signatures":["blank? : Bool"],"summary":"","types":["Bool"],"urlPath":"blankable"},{"anchor":"fn-blank?","kind":"function","line":64,"name":"blank?","qualifiedName":"Integer.blank?","signatures":["blank? : Bool"],"summary":"Always `false`: every integer is a value, including zero.","types":["Bool"],"urlPath":"blankable"},{"anchor":"make-float","kind":"make","line":68,"name":"Float","qualifiedName":"Float","signatures":["blank? : Bool"],"summary":"","types":["Bool"],"urlPath":"blankable"},{"anchor":"fn-blank?","kind":"function","line":75,"name":"blank?","qualifiedName":"Float.blank?","signatures":["blank? : Bool"],"summary":"Always `false`: every float is a value, including zero.","types":["Bool"],"urlPath":"blankable"},{"anchor":"make-string","kind":"make","line":79,"name":"String","qualifiedName":"String","signatures":[],"summary":"","types":[],"urlPath":"blankable"},{"anchor":"make-optional<x>","kind":"make","line":103,"name":"Optional<X>","qualifiedName":"Optional<X>","signatures":["blank? : Bool"],"summary":"","types":["Bool"],"urlPath":"blankable"},{"anchor":"fn-blank?","kind":"function","line":115,"name":"blank?","qualifiedName":"Optional<X>.blank?","signatures":["blank? : Bool"],"summary":"Returns `true` for `None` and `false` for a `Just`: whatever it wraps.\n\nNote that `Just(\"\")` is present, not blank: the optional holds a value, even though that value is itself blank.","types":["Bool"],"urlPath":"blankable"},{"anchor":"make-[x]","kind":"make","line":120,"name":"[X]","qualifiedName":"[X]","signatures":["blank? : Bool"],"summary":"","types":["Bool"],"urlPath":"blankable"},{"anchor":"fn-blank?","kind":"function","line":133,"name":"blank?","qualifiedName":"[X].blank?","signatures":["blank? : Bool"],"summary":"Returns `true` when the list has no elements.","types":["Bool"],"urlPath":"blankable"},{"anchor":"module-console","kind":"module","line":12,"name":"Console","qualifiedName":"Console","signatures":["colorize(text, color) : String -> String -> String"],"summary":"ANSI terminal styling: colors, text attributes, and cursor control.\n\nEvery constant here becomes an empty string when Kex is started with `--no-colors`, or when output is not going to a terminal. You can splice them into a string unconditionally.\n\n`colorize` is usually what you want, since it applies the reset for you.","types":["?","String","Bool"],"urlPath":"console"},{"anchor":"constant-reset","kind":"constant","line":20,"name":"RESET","qualifiedName":"Console.RESET","signatures":[],"summary":"Clears every active style. Ends a styled run started with a color or attribute constant; `colorize` appends it for you.","types":["?"],"urlPath":"console"},{"anchor":"constant-bold","kind":"constant","line":28,"name":"BOLD","qualifiedName":"Console.BOLD","signatures":[],"summary":"Renders following text in bold.","types":["?"],"urlPath":"console"},{"anchor":"constant-dim","kind":"constant","line":37,"name":"DIM","qualifiedName":"Console.DIM","signatures":[],"summary":"Renders following text dimmed. Useful for secondary detail that should not compete with the main output.","types":["?"],"urlPath":"console"},{"anchor":"constant-italic","kind":"constant","line":45,"name":"ITALIC","qualifiedName":"Console.ITALIC","signatures":[],"summary":"Renders following text in italics, where the terminal supports it.","types":["?"],"urlPath":"console"},{"anchor":"constant-underline","kind":"constant","line":53,"name":"UNDERLINE","qualifiedName":"Console.UNDERLINE","signatures":[],"summary":"Underlines following text.","types":["?"],"urlPath":"console"},{"anchor":"constant-blink","kind":"constant","line":62,"name":"BLINK","qualifiedName":"Console.BLINK","signatures":[],"summary":"Makes following text blink, where the terminal supports it. Most do not, and most users would rather they did not.","types":["?"],"urlPath":"console"},{"anchor":"constant-reverse","kind":"constant","line":70,"name":"REVERSE","qualifiedName":"Console.REVERSE","signatures":[],"summary":"Swaps the foreground and background colors of following text.","types":["?"],"urlPath":"console"},{"anchor":"constant-hidden","kind":"constant","line":78,"name":"HIDDEN","qualifiedName":"Console.HIDDEN","signatures":[],"summary":"Hides following text: it occupies space but is not drawn.","types":["?"],"urlPath":"console"},{"anchor":"constant-strikethrough","kind":"constant","line":86,"name":"STRIKETHROUGH","qualifiedName":"Console.STRIKETHROUGH","signatures":[],"summary":"Strikes through following text.","types":["?"],"urlPath":"console"},{"anchor":"constant-red","kind":"constant","line":94,"name":"RED","qualifiedName":"Console.RED","signatures":[],"summary":"Red. Conventionally: errors and failures.","types":["?"],"urlPath":"console"},{"anchor":"constant-green","kind":"constant","line":102,"name":"GREEN","qualifiedName":"Console.GREEN","signatures":[],"summary":"Green. Conventionally: success.","types":["?"],"urlPath":"console"},{"anchor":"constant-yellow","kind":"constant","line":110,"name":"YELLOW","qualifiedName":"Console.YELLOW","signatures":[],"summary":"Yellow. Conventionally: warnings.","types":["?"],"urlPath":"console"},{"anchor":"constant-blue","kind":"constant","line":118,"name":"BLUE","qualifiedName":"Console.BLUE","signatures":[],"summary":"Blue. Useful for informational labels and links.","types":["?"],"urlPath":"console"},{"anchor":"constant-magenta","kind":"constant","line":126,"name":"MAGENTA","qualifiedName":"Console.MAGENTA","signatures":[],"summary":"Magenta. Useful for highlighting a category distinct from status colors.","types":["?"],"urlPath":"console"},{"anchor":"constant-cyan","kind":"constant","line":134,"name":"CYAN","qualifiedName":"Console.CYAN","signatures":[],"summary":"Cyan. Useful for identifiers, paths, and other values inside prose.","types":["?"],"urlPath":"console"},{"anchor":"constant-white","kind":"constant","line":142,"name":"WHITE","qualifiedName":"Console.WHITE","signatures":[],"summary":"White. Useful for primary text on a dark terminal.","types":["?"],"urlPath":"console"},{"anchor":"constant-gray","kind":"constant","line":150,"name":"GRAY","qualifiedName":"Console.GRAY","signatures":[],"summary":"Gray. Conventionally: de-emphasised detail, like `DIM` but as a color.","types":["?"],"urlPath":"console"},{"anchor":"constant-purple","kind":"constant","line":158,"name":"PURPLE","qualifiedName":"Console.PURPLE","signatures":[],"summary":"Purple. An alternative accent when magenta is already in use.","types":["?"],"urlPath":"console"},{"anchor":"constant-clear","kind":"constant","line":169,"name":"CLEAR","qualifiedName":"Console.CLEAR","signatures":[],"summary":"Erases the screen and moves the cursor to the top-left corner.\n\nUse it to start a fresh frame. When you are redrawing repeatedly, `HOME` gives a smoother result: see below.","types":["?"],"urlPath":"console"},{"anchor":"constant-home","kind":"constant","line":183,"name":"HOME","qualifiedName":"Console.HOME","signatures":[],"summary":"Moves the cursor to the top-left corner without erasing anything.\n\nA redraw that starts here paints over the previous frame, so there is no blank flash between frames the way `CLEAR` produces.","types":["?"],"urlPath":"console"},{"anchor":"constant-clearline","kind":"constant","line":197,"name":"CLEARLINE","qualifiedName":"Console.CLEARLINE","signatures":[],"summary":"Erases the current line and returns the cursor to its start.\n\nThis is how to write a progress line in place rather than one line per update.","types":["?"],"urlPath":"console"},{"anchor":"function-colorize","kind":"function","line":215,"name":"colorize","qualifiedName":"Console.colorize","signatures":["colorize(text, color) : String -> String -> String"],"summary":"Wraps `text` in `color` and appends a reset, so the style ends where the text does.\n\nPreferred over splicing the constants by hand: it cannot leak a style into the rest of the line, and it still produces plain text when color is off.","types":["String"],"urlPath":"console"},{"anchor":"constant-enabled?","kind":"constant","line":229,"name":"enabled?","qualifiedName":"Console.enabled?","signatures":[],"summary":"Returns `true` when terminal styling is on for this process.\n\nIt is `false` under `--no-colors` and when output is redirected. You rarely need to check it (the constants already collapse to `\"\"`) but it is the right test when the alternative is a different layout rather than a different color.","types":["Bool"],"urlPath":"console"},{"anchor":"module-digest","kind":"module","line":13,"name":"Digest","qualifiedName":"Digest","signatures":["sha256(content) : String -> String","sha256(content) : Binary -> Binary","fileSha256(path) : String -> String?"],"summary":"Cryptographic content digests.\n\nDigests are returned as lowercase hex strings, so a Kex program never has to handle backend-specific binary values: they compare with `==`, print directly, and go into a map key unchanged.\n\n```kex\nDigest.sha256(\"hello\")\n# => \"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824\"\n```\n\nSHA-256 is a content fingerprint: use it to tell whether two things are the same, to key a cache, or to check that a download arrived intact. It is not a password hash: a purpose-built password KDF is what that needs.","types":["String","Binary","String?"],"urlPath":"digest"},{"anchor":"function-sha256","kind":"function","line":31,"name":"sha256","qualifiedName":"Digest.sha256","signatures":["sha256(content) : String -> String","sha256(content) : Binary -> Binary"],"summary":"Returns the SHA-256 digest of `content`, as a 64-character lowercase hex string.","types":["String","Binary"],"urlPath":"digest"},{"anchor":"function-filesha256","kind":"function","line":52,"name":"fileSha256","qualifiedName":"Digest.fileSha256","signatures":["fileSha256(path) : String -> String?"],"summary":"Returns the SHA-256 digest of the file at `path`, or `None` when the file cannot be read.\n\nReads the file for you, so a large file does not have to be pulled into a `String` first.","types":["String","String?"],"urlPath":"digest"},{"anchor":"trait-foldable","kind":"trait","line":15,"name":"Foldable","qualifiedName":"Foldable","signatures":["reduce : A -> (A -> T -> A) -> A","each(f)","eachIndexed(f)","all?(pred)","any?(pred)","find(pred)","count(pred)"],"summary":"Traversal operations that every foldable collection gets for free.\n\nA type becomes `Foldable` by implementing one method, `reduce`; the rest: `each`, `all?`, `any?`, `find`, `count`: are derived from it. `List`, `String`, `Map`, `Range` and both flavours of `Set` all implement it, so these methods read the same whatever you point them at.\n\n```kex\n[1, 2, 3].all? { |n| n > 0 }        # => true\n\"hello\".any?(~digit?)               # => false\n{ a: 1, b: 2 }.count { |k, v| v > 1 }   # => 1\n```\n\nBlocks are applied via Kex.Intrinsic.Fun.applyItem, which auto-splats a pair item into a two-argument block. That is what lets a `Map` traversal be written `{ |key, value| ... }` even though the fold hands over one tuple.","types":["(A) -> ((A) -> (T) -> A) -> A"],"urlPath":"enumerable"},{"anchor":"fn-reduce","kind":"function","line":25,"name":"reduce","qualifiedName":"Foldable.reduce","signatures":["reduce : A -> (A -> T -> A) -> A"],"summary":"Folds the collection from the left. The one operation a `Foldable` type must define; everything else here is written in terms of it.","types":["(A) -> ((A) -> (T) -> A) -> A"],"urlPath":"enumerable"},{"anchor":"fn-each","kind":"function","line":42,"name":"each","qualifiedName":"Foldable.each","signatures":["each(f)"],"summary":"Calls `f` with each item, for its side effects.\n\nThe loop of last resort, when what you want is a new collection rather than an effect, `map` or `filter` says so more clearly.","types":[],"urlPath":"enumerable"},{"anchor":"fn-eachindexed","kind":"function","line":65,"name":"eachIndexed","qualifiedName":"Foldable.eachIndexed","signatures":["eachIndexed(f)"],"summary":"Calls `f` with each item and its 0-based position, for its side effects.\n\nThe index is the LAST block parameter, so a `Map` entry can be taken either whole (`|entry, i|`) or spread (`|k, v, i|`).","types":[],"urlPath":"enumerable"},{"anchor":"fn-all?","kind":"function","line":86,"name":"all?","qualifiedName":"Foldable.all?","signatures":["all?(pred)"],"summary":"Returns `true` when every item satisfies `pred`. An empty collection answers `true`.","types":[],"urlPath":"enumerable"},{"anchor":"fn-any?","kind":"function","line":102,"name":"any?","qualifiedName":"Foldable.any?","signatures":["any?(pred)"],"summary":"Returns `true` when at least one item satisfies `pred`. An empty collection answers `false`.","types":[],"urlPath":"enumerable"},{"anchor":"fn-find","kind":"function","line":117,"name":"find","qualifiedName":"Foldable.find","signatures":["find(pred)"],"summary":"Returns the first item satisfying `pred`, or `None` when nothing does.","types":[],"urlPath":"enumerable"},{"anchor":"fn-count","kind":"function","line":134,"name":"count","qualifiedName":"Foldable.count","signatures":["count(pred)"],"summary":"Returns how many items satisfy `pred`.","types":[],"urlPath":"enumerable"},{"anchor":"trait-enumerable","kind":"trait","line":150,"name":"Enumerable","qualifiedName":"Enumerable","signatures":["reduce : A -> (A -> T -> A) -> A","map(f)","mapIndexed(f)","filter(pred)","flatMap(f)","collect(f)"],"summary":"Collection-producing operations that every foldable collection gets for free.\n\nLike `Foldable`, a type joins by implementing `reduce` alone. The defaults here answer with a list, because the block may return anything at all; a type that can do better overrides them: `Map.filter` gives back a map, `Set.map` gives back a set, `String.filter` gives back a string.\n\n```kex\n[1, 2, 3].map { |n| n * 2 }              # => [2, 4, 6]\n\"a1b2\".filter(~digit?)                   # => \"12\"\n{ a: 1, b: 2 }.filter { |k, v| v > 1 }   # => { :b: 2 }\n```","types":["(A) -> ((A) -> (T) -> A) -> A"],"urlPath":"enumerable"},{"anchor":"fn-reduce","kind":"function","line":160,"name":"reduce","qualifiedName":"Enumerable.reduce","signatures":["reduce : A -> (A -> T -> A) -> A"],"summary":"Folds the collection from the left. The one operation an `Enumerable` type must define.","types":["(A) -> ((A) -> (T) -> A) -> A"],"urlPath":"enumerable"},{"anchor":"fn-map","kind":"function","line":176,"name":"map","qualifiedName":"Enumerable.map","signatures":["map(f)"],"summary":"Applies `f` to each item and collects the results into a list.\n\nThe single most useful method here: it describes what each item becomes, and leaves the walking of the collection implied.","types":[],"urlPath":"enumerable"},{"anchor":"fn-mapindexed","kind":"function","line":196,"name":"mapIndexed","qualifiedName":"Enumerable.mapIndexed","signatures":["mapIndexed(f)"],"summary":"Applies `f` to each item and its 0-based position, and collects the results into a list.\n\nThe index is the LAST block parameter: see `eachIndexed`.","types":[],"urlPath":"enumerable"},{"anchor":"fn-filter","kind":"function","line":217,"name":"filter","qualifiedName":"Enumerable.filter","signatures":["filter(pred)"],"summary":"Returns the items for which `pred` answers `true`.","types":[],"urlPath":"enumerable"},{"anchor":"fn-flatmap","kind":"function","line":239,"name":"flatMap","qualifiedName":"Enumerable.flatMap","signatures":["flatMap(f)"],"summary":"Applies `f` to each item, expecting a list back, and concatenates the results into one flat list.\n\nUse it when each item expands into zero or more results: `map` would give you a list of lists.","types":[],"urlPath":"enumerable"},{"anchor":"fn-collect","kind":"function","line":258,"name":"collect","qualifiedName":"Enumerable.collect","signatures":["collect(f)"],"summary":"Applies `f` to each item, expecting an `Optional` back, and returns the values that were present: unwrapped.\n\nThis is filter and map fused into one pass, which is what you want whenever the test and the transformation are the same operation. Parsing is the classic case: an item either yields a value or it does not.","types":[],"urlPath":"enumerable"},{"anchor":"module-env","kind":"module","line":25,"name":"ENV","qualifiedName":"ENV","signatures":["get(key)","has?(key)","keys()","values()","count()","each(f)","entries()","set(name, value)","unset(name)"],"summary":"The process environment, as an immutable `Map<String, String>` snapshot taken at startup.\n\n`ENV` supports the whole `Map` API: `get`, `has?`, `keys`, `values`, `count`, `each`, `entries`, so reading a variable looks like any other map lookup:\n\n```kex\nENV.get(\"HOME\")                  # => Just(\"/home/ada\")\nENV.get(\"LOG_LEVEL\", \"info\")     # => \"info\" when unset\nENV.has?(\"PATH\")                 # => true\n```\n\nThe snapshot itself is immutable, but the global `ENV` namespace is an ambient input: the same call can answer differently between runs without anything appearing in a function's arguments. That is why reading it is `foul`.\n\nWhen you would rather the dependency be visible, take it as a parameter: `main` receives the same snapshot as its second argument, and reading a parameter is pure:\n\n```kex\nmain(args, env) do\n  let level = env.get(\"LOG_LEVEL\", \"info\")\n  IO.printLine(\"log level: ${level}\")\nend\n```","types":[],"urlPath":"env"},{"anchor":"function-get","kind":"function","line":41,"name":"get","qualifiedName":"ENV.get","signatures":["get(key)"],"summary":"Returns the value of the environment variable `key`, or `None` when it is not set.","types":[],"urlPath":"env"},{"anchor":"function-has?","kind":"function","line":73,"name":"has?","qualifiedName":"ENV.has?","signatures":["has?(key)"],"summary":"Returns `true` when `key` is set, whatever its value.\n\nDistinguishes an unset variable from one set to the empty string, which a `get` with a default cannot.","types":[],"urlPath":"env"},{"anchor":"function-keys","kind":"function","line":84,"name":"keys","qualifiedName":"ENV.keys","signatures":["keys()"],"summary":"Returns every variable name in the environment.","types":[],"urlPath":"env"},{"anchor":"function-values","kind":"function","line":92,"name":"values","qualifiedName":"ENV.values","signatures":["values()"],"summary":"Returns every variable value in the environment.","types":[],"urlPath":"env"},{"anchor":"function-count","kind":"function","line":100,"name":"count","qualifiedName":"ENV.count","signatures":["count()"],"summary":"Returns how many variables the environment has.","types":[],"urlPath":"env"},{"anchor":"function-each","kind":"function","line":109,"name":"each","qualifiedName":"ENV.each","signatures":["each(f)"],"summary":"Calls `f` with each variable's name and value.","types":[],"urlPath":"env"},{"anchor":"function-entries","kind":"function","line":119,"name":"entries","qualifiedName":"ENV.entries","signatures":["entries()"],"summary":"Returns the environment as a list of `(name, value)` pairs.\n\nThe bridge to the `List` operations, sorting, grouping, taking a slice.","types":[],"urlPath":"env"},{"anchor":"function-set","kind":"function","line":147,"name":"set","qualifiedName":"ENV.set","signatures":["set(name, value)"],"summary":"Sets an environment variable for this process and every child it starts.\n\n`ENV` is a snapshot, and the write rebuilds it: a later `ENV.get` answers what was set, not what the process started with.\n\nThis is how a program decides what a child sees. `Kex.AST`, for instance, shells out to the compiler named by `$KEX`, so a tool that knows which compiler it means says so here rather than hoping `PATH` agrees.\n\nSets a variable for THIS process and every child it starts. `ENV` is a snapshot, so it is rebuilt by the write: a later `ENV.get` answers what was set, not what the process started with.\n\nThis is how a program decides what a child sees: `Kex.AST` shells out to the compiler named by `$KEX`, so a tool that knows which compiler it means says so here rather than hoping PATH agrees.","types":[],"urlPath":"env"},{"anchor":"function-unset","kind":"function","line":160,"name":"unset","qualifiedName":"ENV.unset","signatures":["unset(name)"],"summary":"Removes an environment variable from this process and its children.","types":[],"urlPath":"env"},{"anchor":"trait-errorable","kind":"trait","line":10,"name":"Errorable","qualifiedName":"Errorable","signatures":["message : String"],"summary":"`Errorable`: the trait for values that describe a failure.\n\nImplemented by error types that carry a human-readable message, so a generic handler can display or log any error without knowing its concrete type or its structured fields.\n\n```kex\nfoul report(e: Errorable) -> Void do\n  IO.printError(\"error: ${e.message}\")\nend\n```","types":["String"],"urlPath":"errorable"},{"anchor":"fn-message","kind":"function","line":20,"name":"message","qualifiedName":"Errorable.message","signatures":["message : String"],"summary":"A human-readable description of what went wrong.\n\nWritten for a person reading output, not for a program to match on: branch on the error's own type or fields for that.","types":["String"],"urlPath":"errorable"},{"anchor":"record-parseerror","kind":"record","line":36,"name":"ParseError","qualifiedName":"ParseError","signatures":[],"summary":"A parse failure, with everything needed to report it or to carry on from it.\n\nAnswered by `Integer.parse`, `Float.parse` and `Number.parse` when the input is not what they expected. Reach for those over `to(Integer)` exactly when this detail matters: `to` answers a plain `None`.\n\n```kex\nInteger.parse(\"12x\")\n# => Error(ParseError { input: \"12x\", position: 2, value: 12,\n#                       message: \"trailing characters after integer\",\n#                       rest: \"x\" })\n```\n\nThe `position` and `rest` are what make a hand-written scanner possible: the error says exactly where it stopped and what was left.","types":["String","Integer","Any"],"urlPath":"errorable"},{"anchor":"module-evaluator","kind":"module","line":13,"name":"Evaluator","qualifiedName":"Evaluator","signatures":["run(source) : String -> Result<Any, String>","run(source) : String -> EvaluatorOptions -> Result<Any, String>","runExpression(source) : String -> Result<Any, String>","runExpression(source) : String -> EvaluatorOptions -> Result<Any, String>"],"summary":"Running Kex source code at run time, in a sandbox.\n\nEach call builds a fresh, isolated evaluator: the caller's environment is never shared, and the evaluated code sees only what `EvaluatorOptions` allows. Step and depth limits make a runaway program stop rather than hang.\n\n```kex\nEvaluator.runExpression(\"1 ` 2\")           # => Ok(3)\nEvaluator.runExpression(\"[3,1,2].sort\")    # => Ok([1, 2, 3])\n```\n\nThis is for evaluating expressions your program was given: a formula in a config file, a filter typed by a user. Every result is a `Result+, so a syntax error or a rejected call is a value you handle.","types":["String","Result<Any, String>","(EvaluatorOptions) -> Result<Any, String>","[Atom]","{String: {String: (Any) -> Any}}","Int"],"urlPath":"evaluator"},{"anchor":"function-run","kind":"function","line":33,"name":"run","qualifiedName":"Evaluator.run","signatures":["run(source) : String -> Result<Any, String>","run(source) : String -> EvaluatorOptions -> Result<Any, String>"],"summary":"Evaluates a whole Kex program and returns its result.\n\nThe source may declare functions, records and a `main`, exactly as a file would. Anything that goes wrong: a parse error, a call the sandbox does not allow, running past the step limit: comes back as `Error` with a message.","types":["String","Result<Any, String>","(EvaluatorOptions) -> Result<Any, String>"],"urlPath":"evaluator"},{"anchor":"function-runexpression","kind":"function","line":55,"name":"runExpression","qualifiedName":"Evaluator.runExpression","signatures":["runExpression(source) : String -> Result<Any, String>","runExpression(source) : String -> EvaluatorOptions -> Result<Any, String>"],"summary":"Evaluates a single Kex expression and returns its value.\n\nThe form to reach for when the input is a formula rather than a program: there is no `main` to write and no declarations to skip past.","types":["String","Result<Any, String>","(EvaluatorOptions) -> Result<Any, String>"],"urlPath":"evaluator"},{"anchor":"record-evaluatoroptions","kind":"record","line":65,"name":"EvaluatorOptions","qualifiedName":"Evaluator.EvaluatorOptions","signatures":[],"summary":"What an evaluated program is allowed to reach, and how long it may run.\n\nThe defaults are already conservative: pure computation over the common data types, no filesystem, no network, no processes. Narrow `allow` further, or lower the limits, when the source is less trusted still.","types":["[Atom]","{String: {String: (Any) -> Any}}","Int"],"urlPath":"evaluator"},{"anchor":"type-feed","kind":"type","line":22,"name":"Feed","qualifiedName":"Feed","signatures":[],"summary":"A one-shot sequence over a source that can only be read once.\n\nA feed is what `Stream` is not: reading it CONSUMES it. Where a stream remembers its elements so it can be walked again, a feed keeps nothing, and that is the point: it is how to walk a file, a socket or a device larger than memory, which a stream cannot do while anything still holds its start.\n\n```kex\nfoul lines = FS.File.feed(\"huge.log\").or(Feed.empty)\nlines.each { |line| IO.printLine(line) if line.contains?(\"ERROR\") }\n```\n\n`map`, `filter` and `drop` answer a feed over the SAME cursor rather than a second cursor over the same source, so a pipeline is one pass:\n\n```kex\nFS.File.feed(\"app.log\").or(Feed.empty)\n  .filter { |line| line.contains?(\"ERROR\") }\n  .take(10)\n```\n\nTaking twice answers two different windows: the first ten, then the ten after them. That is the whole difference from `Stream`, where taking twice answers the same ten. When you want the stream behaviour on a small source, `toStream` asks for it explicitly, at the cost of holding what it reads.","types":[],"urlPath":"feed"},{"anchor":"module-feed","kind":"module","line":25,"name":"Feed","qualifiedName":"Feed","signatures":["Elements(xs) : [A] -> Feed<A>"],"summary":"Constructors for `Feed`.","types":["Feed<A>","[A]"],"urlPath":"feed"},{"anchor":"constant-empty","kind":"constant","line":35,"name":"empty","qualifiedName":"Feed.empty","signatures":[],"summary":"A feed with nothing in it, already spent.\n\nMostly the `or` half of a failed open: `FS.File.feed(path).or(Feed.empty)` reads nothing rather than stopping the program.","types":["Feed<A>"],"urlPath":"feed"},{"anchor":"function-elements","kind":"function","line":51,"name":"Elements","qualifiedName":"Feed.Elements","signatures":["Elements(xs) : [A] -> Feed<A>"],"summary":"A feed over a list that is already in hand.\n\nThe elements are there, so nothing is saved by feeding them: this is for standing in for a real source, in a test, with something that consumes the way the real one does.","types":["[A]","Feed<A>"],"urlPath":"feed"},{"anchor":"make-feed<a>","kind":"make","line":70,"name":"Feed<A>","qualifiedName":"Feed<A>","signatures":["take(n) : Integer -> [A]","drop(n) : Integer -> Feed<A>","map(f) : (A -> B) -> Feed<B>","filter(pred) : (A -> Bool) -> Feed<A>","each(f) : (A -> Void) -> Void"],"summary":"Every method below is `let`, not `foul`, though reading a feed plainly is an effect. Two reasons, one principled and one practical.\n\nThe effect is already tracked where it enters: a feed over anything outside the program comes from `FS.File.feed` or `handle.feed`, both foul, so a pure function cannot obtain one. What `Feed.Elements` and `Stream.toFeed` build from data in hand mutates only a cursor the caller just made.\n\nAnd a foul method takes the hidden capability context, which puts it one BEAM arity above the pure `take`/`map`/`filter` every other receiver has. A feed usually arrives with no static type: `FS.File.feed(p).or(Feed.empty)`, or a `Just(f) =>` binding, and such a call has to go through the runtime dispatcher, which is built per arity and so could never reach a method one arity up. Marking these foul made every dynamically-typed feed call fail with \"Undefined method: take for Tuple\".","types":["Integer","[A]","Feed<A>","(A) -> B","Feed<B>","(A) -> Bool","(A) -> Void","Void"],"urlPath":"feed"},{"anchor":"fn-take","kind":"function","line":115,"name":"take","qualifiedName":"Feed<A>.take","signatures":["take(n) : Integer -> [A]"],"summary":"Returns the next `n` elements as a list, consuming them.\n\nAnswers fewer than `n` when the source ends first, and `[]` once it is spent. Unlike `Stream.take`, calling it twice walks forward.","types":["Integer","[A]"],"urlPath":"feed"},{"anchor":"fn-drop","kind":"function","line":128,"name":"drop","qualifiedName":"Feed<A>.drop","signatures":["drop(n) : Integer -> Feed<A>"],"summary":"Discards the next `n` elements and answers the feed.\n\nThere is only ever one cursor, so this hands back the same feed rather than a second one positioned differently.","types":["Integer","Feed<A>"],"urlPath":"feed"},{"anchor":"fn-map","kind":"function","line":141,"name":"map","qualifiedName":"Feed<A>.map","signatures":["map(f) : (A -> B) -> Feed<B>"],"summary":"Returns a feed with `f` applied to each element.\n\n`f` runs as elements are read, and only for those that are. The result draws from the same cursor, so reading it consumes the receiver too.","types":["(A) -> B","Feed<B>"],"urlPath":"feed"},{"anchor":"fn-filter","kind":"function","line":155,"name":"filter","qualifiedName":"Feed<A>.filter","signatures":["filter(pred) : (A -> Bool) -> Feed<A>"],"summary":"Returns a feed of only the elements `pred` accepts.\n\nReading one element of the result may consume many of the receiver's.","types":["(A) -> Bool","Feed<A>"],"urlPath":"feed"},{"anchor":"fn-each","kind":"function","line":165,"name":"each","qualifiedName":"Feed<A>.each","signatures":["each(f) : (A -> Void) -> Void"],"summary":"Applies `f` to every remaining element, draining the feed.","types":["(A) -> Void","Void"],"urlPath":"feed"},{"anchor":"type-readerror","kind":"type","line":8,"name":"ReadError","qualifiedName":"ReadError","signatures":[],"summary":"Why a read failed.\n\n`ReadFailed` means the source refused the read. `InvalidUtf8` means bytes were read but are not valid UTF-8, and carries the byte offset of the first malformed sequence, relative to that one operation. A failed read consumes the bytes it attempted to read and never substitutes U`FFFD: use `readBytes+ to recover the payload verbatim.","types":["Integer"],"urlPath":"filehandle"},{"anchor":"trait-readable","kind":"trait","line":24,"name":"Readable","qualifiedName":"Readable","signatures":["getLine : Result<String?, ReadError>","get : Result<String?, ReadError>","readLine : Result<String?, ReadError>","read : Result<String, ReadError>","readBytes : Result<Binary, ReadError>","eof? : Bool","atEnd? : Bool"],"summary":"`Readable`: a source that yields text.\n\nNamed so that anything can be one, not just a file: the vocabulary `FileHandle<CanRead, W>` already carried was an abstraction without a name, so nothing else could implement it and `IO` did not go through it (kexhq/kex#139). `IO.in` is a `Readable`; so is any handle opened for reading.\n\n```kex\nfoul firstLine(source: Readable) -> String do\n  source.getLine.or(\"(empty)\")\nend\n\nfirstLine(IO.in)\nfirstLine(FS.File.open(\"notes.txt\", Read).try)\n```","types":["Result<String?, ReadError>","Result<String, ReadError>","Result<Binary, ReadError>","Bool"],"urlPath":"filehandle"},{"anchor":"fn-getline","kind":"function","line":29,"name":"getLine","qualifiedName":"Readable.getLine","signatures":["getLine : Result<String?, ReadError>"],"summary":"Reads the next line, without its newline.","types":["Result<String?, ReadError>"],"urlPath":"filehandle"},{"anchor":"fn-get","kind":"function","line":37,"name":"get","qualifiedName":"Readable.get","signatures":["get : Result<String?, ReadError>"],"summary":"Reads a single character, as a one-character `String`.\n\nReads one complete Unicode scalar, not one byte.","types":["Result<String?, ReadError>"],"urlPath":"filehandle"},{"anchor":"fn-readline","kind":"function","line":43,"name":"readLine","qualifiedName":"Readable.readLine","signatures":["readLine : Result<String?, ReadError>"],"summary":"Reads the next line, without its newline. The same as `getLine`.","types":["Result<String?, ReadError>"],"urlPath":"filehandle"},{"anchor":"fn-read","kind":"function","line":50,"name":"read","qualifiedName":"Readable.read","signatures":["read : Result<String, ReadError>"],"summary":"Reads everything remaining, as one `String`.\n\nDraining an exhausted source answers `Ok(\"\")`.","types":["Result<String, ReadError>"],"urlPath":"filehandle"},{"anchor":"fn-readbytes","kind":"function","line":59,"name":"readBytes","qualifiedName":"Readable.readBytes","signatures":["readBytes : Result<Binary, ReadError>"],"summary":"Reads everything remaining as raw bytes, without decoding it as text.\n\nThe byte counterpart of `read`: it never validates UTF-8, so it recovers the payload of a source that is not text, or one `read` has just rejected. Draining an exhausted source answers `Ok(Binary.fromBytes([]))`.","types":["Result<Binary, ReadError>"],"urlPath":"filehandle"},{"anchor":"fn-eof?","kind":"function","line":64,"name":"eof?","qualifiedName":"Readable.eof?","signatures":["eof? : Bool"],"summary":"Returns `true` when the source has reached its end.","types":["Bool"],"urlPath":"filehandle"},{"anchor":"fn-atend?","kind":"function","line":69,"name":"atEnd?","qualifiedName":"Readable.atEnd?","signatures":["atEnd? : Bool"],"summary":"Returns `true` when the source has reached its end. The same as `eof?`.","types":["Bool"],"urlPath":"filehandle"},{"anchor":"trait-writable","kind":"trait","line":97,"name":"Writable","qualifiedName":"Writable","signatures":["printLine : Showable -> Void","print : Showable -> Void","writeLine : Showable -> Void","write : Showable -> Void","writeBytes : Binary -> Void"],"summary":"`Writable`: a sink that accepts text.\n\nThe payoff of naming it is that a sink becomes a VALUE a library can accept, rather than a global switch it can only sit underneath: output from one library can go to a buffer while another's goes to the terminal (kexhq/kex#139).\n\n```kex\nfoul report(out: Writable, lines: [String]) -> Void do\n  lines.each { |line| out.printLine(line) }\nend\n\nreport(IO.out, results)\nreport(IO.error, warnings)\nreport(FS.File.open(\"report.txt\", Write).try, results)\n```\n\nTwo deliberate choices, both settled in kexhq/kex#139:\n\n- The argument is `Showable`, not `String`. `IO.printLine` always took a   `Showable` while the handle methods took a `String`; the wider one is   right, and it is what makes `IO.printLine(x)` and `IO.out.printLine(x)`   the same call. - The result is `Void`, not `Bool`. A boolean nobody checks is not an error   channel, and `Result<Void, IOError>` on every print is miserable to use.   Erlang's answer is the one taken here: the call says `ok`, and failure   belongs to the device rather than to the call site.","types":["(Showable) -> Void","(Binary) -> Void"],"urlPath":"filehandle"},{"anchor":"fn-printline","kind":"function","line":102,"name":"printLine","qualifiedName":"Writable.printLine","signatures":["printLine : Showable -> Void"],"summary":"Writes `content` followed by a newline.","types":["(Showable) -> Void"],"urlPath":"filehandle"},{"anchor":"fn-print","kind":"function","line":108,"name":"print","qualifiedName":"Writable.print","signatures":["print : Showable -> Void"],"summary":"Writes `content` with no trailing newline.","types":["(Showable) -> Void"],"urlPath":"filehandle"},{"anchor":"fn-writeline","kind":"function","line":114,"name":"writeLine","qualifiedName":"Writable.writeLine","signatures":["writeLine : Showable -> Void"],"summary":"Writes `content` followed by a newline. The same as `printLine`.","types":["(Showable) -> Void"],"urlPath":"filehandle"},{"anchor":"fn-write","kind":"function","line":120,"name":"write","qualifiedName":"Writable.write","signatures":["write : Showable -> Void"],"summary":"Writes `content` with no trailing newline. The same as `print`.","types":["(Showable) -> Void"],"urlPath":"filehandle"},{"anchor":"fn-writebytes","kind":"function","line":130,"name":"writeBytes","qualifiedName":"Writable.writeBytes","signatures":["writeBytes : Binary -> Void"],"summary":"Writes `content` as raw bytes, with no trailing newline.\n\nThe byte counterpart of `write`: the payload goes to the sink exactly as given. It never renders the value, so a `Binary` reaches the sink as its bytes rather than as the `#Binary<N bytes>` that `Showable` would print.","types":["(Binary) -> Void"],"urlPath":"filehandle"},{"anchor":"type-filehandle","kind":"type","line":161,"name":"FileHandle","qualifiedName":"FileHandle","signatures":[],"summary":"An open file, obtained from `FS.File.open`.\n\nThe two type parameters record what the handle is allowed to do: `R` is `CanRead` or `CannotRead`, `W` is `CanWrite` or `CannotWrite`. `FS.File.open` picks them from the mode you pass, so calling `write` on a handle opened `Read` is a compile error rather than a run-time failure.\n\n```kex\nusing FS\n\nmain do\n  match FS.File.open(\"notes.txt\", Read) do\n    Ok(handle) => do\n      IO.printLine(handle.read.or(\"\"))\n      handle.close\n    end\n    Error(e) => IO.printError(\"cannot open: ${e}\")\n  end\nend\n```\n\nReach for a handle when you want to walk a large file a line at a time, or make many small writes. When a file fits comfortably in memory, `FS.File.read` and `FS.File.write` are shorter and need no closing. To be rid of the closing entirely, pass `FS.File.open` a block.\n\nThe handle methods are `foul`: obtaining a handle is not an effect, but reading or writing through one is, so a function that does so is `foul` no matter where the handle came from. Injection makes a thing substitutable, not pure.","types":[],"urlPath":"filehandle"},{"anchor":"make-filehandle<canread,-w>","kind":"make","line":163,"name":"FileHandle<CanRead, W>","qualifiedName":"FileHandle<CanRead, W>","signatures":["getLine() : Result<String?, ReadError>","get() : Result<String?, ReadError>","readLine() : Result<String?, ReadError>","read() : Result<String, ReadError>","readBytes() : Result<Binary, ReadError>","eof?() : Bool","atEnd?() : Bool","feed() : Feed<String>?"],"summary":"","types":[],"urlPath":"filehandle"},{"anchor":"fn-getline","kind":"function","line":182,"name":"getLine","qualifiedName":"FileHandle<CanRead, W>.getLine","signatures":["getLine() : Result<String?, ReadError>"],"summary":"Reads the next line from the handle, without its newline.\n\nAnswers `None` at end of file, which is what makes it usable as a loop condition. The same operation as `readLine`, under the name `IO.getLine` uses.","types":[],"urlPath":"filehandle"},{"anchor":"fn-get","kind":"function","line":193,"name":"get","qualifiedName":"FileHandle<CanRead, W>.get","signatures":["get() : Result<String?, ReadError>"],"summary":"Reads a single character from the handle, as a one-character `String`.\n\nAnswers `None` at end of file.","types":[],"urlPath":"filehandle"},{"anchor":"fn-readline","kind":"function","line":206,"name":"readLine","qualifiedName":"FileHandle<CanRead, W>.readLine","signatures":["readLine() : Result<String?, ReadError>"],"summary":"Reads the next line from the handle, without its newline. The same as `getLine`, named for reading from a file rather than from a console.","types":[],"urlPath":"filehandle"},{"anchor":"fn-read","kind":"function","line":222,"name":"read","qualifiedName":"FileHandle<CanRead, W>.read","signatures":["read() : Result<String, ReadError>"],"summary":"Reads everything remaining in the file and returns it as one `String`.\n\nReads from the current position, so calling it after a `readLine` gives the rest of the file rather than the whole of it.","types":[],"urlPath":"filehandle"},{"anchor":"fn-readbytes","kind":"function","line":234,"name":"readBytes","qualifiedName":"FileHandle<CanRead, W>.readBytes","signatures":["readBytes() : Result<Binary, ReadError>"],"summary":"Reads all remaining bytes from the handle without decoding them.\n\nUnlike `read`, this accepts arbitrary binary data and cannot fail because the input is not valid UTF-8. It starts at the handle's current position.","types":[],"urlPath":"filehandle"},{"anchor":"fn-eof?","kind":"function","line":243,"name":"eof?","qualifiedName":"FileHandle<CanRead, W>.eof?","signatures":["eof?() : Bool"],"summary":"Returns `true` when the handle has reached the end of the file.","types":[],"urlPath":"filehandle"},{"anchor":"fn-atend?","kind":"function","line":255,"name":"atEnd?","qualifiedName":"FileHandle<CanRead, W>.atEnd?","signatures":["atEnd?() : Bool"],"summary":"Returns `true` when the handle has reached the end of the file. The same as `eof?`, spelled out.","types":[],"urlPath":"filehandle"},{"anchor":"fn-feed","kind":"function","line":278,"name":"feed","qualifiedName":"FileHandle<CanRead, W>.feed","signatures":["feed() : Feed<String>?"],"summary":"Returns the handle's remaining lines as a lazy `Feed`.\n\nLines are read on demand off the handle's own position, so this is how to look at the start of a very large file, or process one without holding it all in memory. The feed shares the handle's cursor: interleaving `readLine` with it advances one position through one open file.\n\nThe feed ends at the last line, so taking more lines than the file has answers just the lines there are.\n\nNOT part of `Readable`: a feed is neither pure nor reusable, so requiring it of every `Readable` would put a foul, one-shot operation on types that have no such cursor to offer. It stays a FileHandle method.","types":[],"urlPath":"filehandle"},{"anchor":"make-filehandle<r,-canwrite>","kind":"make","line":282,"name":"FileHandle<R, CanWrite>","qualifiedName":"FileHandle<R, CanWrite>","signatures":["writeBytes(content) : Binary -> Void","printLine(content) : Showable -> Void","print(content) : Showable -> Void","writeLine(content) : Showable -> Void","write(content) : Showable -> Void"],"summary":"","types":["Binary","Void","Showable"],"urlPath":"filehandle"},{"anchor":"fn-writebytes","kind":"function","line":290,"name":"writeBytes","qualifiedName":"FileHandle<R, CanWrite>.writeBytes","signatures":["writeBytes(content) : Binary -> Void"],"summary":"Writes `content` verbatim, without text encoding or a trailing newline.","types":["Binary","Void"],"urlPath":"filehandle"},{"anchor":"fn-printline","kind":"function","line":300,"name":"printLine","qualifiedName":"FileHandle<R, CanWrite>.printLine","signatures":["printLine(content) : Showable -> Void"],"summary":"Writes `content` followed by a newline.","types":["Showable","Void"],"urlPath":"filehandle"},{"anchor":"fn-print","kind":"function","line":312,"name":"print","qualifiedName":"FileHandle<R, CanWrite>.print","signatures":["print(content) : Showable -> Void"],"summary":"Writes `content` with no trailing newline.","types":["Showable","Void"],"urlPath":"filehandle"},{"anchor":"fn-writeline","kind":"function","line":323,"name":"writeLine","qualifiedName":"FileHandle<R, CanWrite>.writeLine","signatures":["writeLine(content) : Showable -> Void"],"summary":"Writes `content` followed by a newline. The same as `printLine`, named for writing to a file rather than to a console.","types":["Showable","Void"],"urlPath":"filehandle"},{"anchor":"fn-write","kind":"function","line":333,"name":"write","qualifiedName":"FileHandle<R, CanWrite>.write","signatures":["write(content) : Showable -> Void"],"summary":"Writes `content` with no trailing newline. The same as `print`.","types":["Showable","Void"],"urlPath":"filehandle"},{"anchor":"make-filehandle<r,-w>","kind":"make","line":337,"name":"FileHandle<R, W>","qualifiedName":"FileHandle<R, W>","signatures":["close() : Void"],"summary":"","types":[],"urlPath":"filehandle"},{"anchor":"fn-close","kind":"function","line":354,"name":"close","qualifiedName":"FileHandle<R, W>.close","signatures":["close() : Void"],"summary":"Closes the handle, flushing anything still buffered.\n\nClose every handle you open. A written file is not guaranteed to be complete on disk until its handle is closed. Passing `FS.File.open` a block closes the handle for you.","types":[],"urlPath":"filehandle"},{"anchor":"module-fs","kind":"module","line":29,"name":"FS","qualifiedName":"FS","signatures":["open(path, mode) : FilePath -> Read -> Result<FileHandle<CanRead, CannotWrite>, FileError>","open(path, mode) : FilePath -> Write -> Result<FileHandle<CannotRead, CanWrite>, FileError>","open(path, mode) : FilePath -> Append -> Result<FileHandle<CannotRead, CanWrite>, FileError>","open(path, mode) : FilePath -> ReadWrite -> Result<FileHandle<CanRead, CanWrite>, FileError>","open(path, mode) : FilePath -> Read -> (FileHandle<CanRead, CannotWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> Write -> (FileHandle<CannotRead, CanWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> Append -> (FileHandle<CannotRead, CanWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> ReadWrite -> (FileHandle<CanRead, CanWrite> -> A) -> Result<A, FileError>","read(path) : FilePath -> Result<String, FileError>","readBytes(path) : FilePath -> Result<Binary, FileError>","writeBytes(path, content) : FilePath -> Binary -> Bool","write(path, content) : FilePath -> String -> Bool","append(path, content) : FilePath -> String -> Bool","exists?(path) : FilePath -> Bool","file?(path) : FilePath -> Bool","directory?(path) : FilePath -> Bool","delete(path) : FilePath -> Bool","copy(src, dst) : FilePath -> FilePath -> Bool","rename(src, dst) : FilePath -> FilePath -> Bool","readLines(path) : FilePath -> [String]?","feed(path) : FilePath -> Feed<String>?","size(path) : FilePath -> Integer?","absolute(path) : FilePath -> String?","join(a, b) : FilePath -> FilePath -> String","join(a, b) : FilePath -> FilePath -> FilePath -> String","joinAll(parts) : [FilePath] -> String","normalize(path) : FilePath -> String","segments(path) : FilePath -> [String]","absolute?(path) : FilePath -> Bool","relative?(path) : FilePath -> Bool","dirname(path) : FilePath -> String","basename(path) : FilePath -> String","extension(path) : FilePath -> String","stem(path) : FilePath -> String","withExtension(path, wanted) : FilePath -> String -> String","relativeTo(path, base) : FilePath -> FilePath -> String","exists?(path) : FilePath -> Bool","directory?(path) : FilePath -> Bool","file?(path) : FilePath -> Bool","create(path) : FilePath -> Bool","delete(path) : FilePath -> Bool","deleteAll(path) : FilePath -> Bool","list(path) : FilePath -> [String]?","files(path) : FilePath -> [String]?","directories(path) : FilePath -> [String]?","current() : String","home() : String?","temporary() : String"],"summary":"The filesystem: reading and writing files, walking directories, and manipulating paths.\n\n`FS` is not in the prelude: start with `using FS`.\n\n```kex\nusing FS\n\nmain do\n  match FS.File.read(\"config.txt\") do\n    Just(text) => IO.printLine(text.lines.count)\n    None       => IO.printError(\"config.txt is missing\")\n  end\nend\n```\n\nIt is organised in three parts:\n\n```kex\nFS.File        reading, writing, copying and deleting files\nFS.Directory   creating, listing and removing directories\nFS.Path        pure path arithmetic, with no filesystem access at all\n```\n\nThe first two are capabilities: everything in them touches the real filesystem, so they can only be called from `foul` code, and a test can replace the whole of `FS.File` for a lexical region rather than mutating global state. `FS.Path` is ordinary pure code.\n\nMost read operations answer with an `Optional` and most write operations with a `Bool`, so a missing file or a failed write is an ordinary value you handle rather than an exception you catch.","types":["FilePath","Integer","Read","Result<FileHandle<CanRead, CannotWrite>, FileError>","Write","Result<FileHandle<CannotRead, CanWrite>, FileError>","Append","ReadWrite","Result<FileHandle<CanRead, CanWrite>, FileError>","((FileHandle<CanRead, CannotWrite>) -> A) -> Result<A, FileError>","((FileHandle<CannotRead, CanWrite>) -> A) -> Result<A, FileError>","((FileHandle<CanRead, CanWrite>) -> A) -> Result<A, FileError>","Result<String, FileError>","Result<Binary, FileError>","Binary","Bool","String","[String]?","Feed<String>?","Integer?","String?","(FilePath) -> String","[FilePath]","[String]"],"urlPath":"fs"},{"anchor":"type-filepath","kind":"type","line":32,"name":"FilePath","qualifiedName":"FS.FilePath","signatures":[],"summary":"A filesystem path. An alias for `String`, so every `String` method applies to one; `FS.Path` adds the path-aware operations.","types":[],"urlPath":"fs"},{"anchor":"type-filemodes","kind":"type","line":38,"name":"FileModes","qualifiedName":"FS.FileModes","signatures":[],"summary":"How a file should be opened: `Read` to read it, `Write` to replace it, `Append` to add to its end, `ReadWrite` for both. The mode chosen decides what type the resulting `FileHandle` has, and therefore which operations the compiler will let you call on it.","types":[],"urlPath":"fs"},{"anchor":"type-readpermission","kind":"type","line":43,"name":"ReadPermission","qualifiedName":"FS.ReadPermission","signatures":[],"summary":"Whether a `FileHandle` may be read from. Part of the handle's type rather than a runtime flag, so reading from a write-only handle is a compile error.","types":[],"urlPath":"fs"},{"anchor":"type-writepermission","kind":"type","line":47,"name":"WritePermission","qualifiedName":"FS.WritePermission","signatures":[],"summary":"Whether a `FileHandle` may be written to. Part of the handle's type, so writing to a read-only handle is a compile error.","types":[],"urlPath":"fs"},{"anchor":"type-fileerror","kind":"type","line":54,"name":"FileError","qualifiedName":"FS.FileError","signatures":[],"summary":"A file operation that failed, carrying the path it failed on.\n\n`OpenFailed` and `ReadFailed` mean the filesystem refused the operation. `InvalidUtf8` means the bytes were read but are not valid UTF-8, and carries the byte offset of the first malformed sequence.","types":["FilePath","FilePath","FilePath","Integer"],"urlPath":"fs"},{"anchor":"module-fs-file","kind":"module","line":65,"name":"FS.File","qualifiedName":"FS.File","signatures":["open(path, mode) : FilePath -> Read -> Result<FileHandle<CanRead, CannotWrite>, FileError>","open(path, mode) : FilePath -> Write -> Result<FileHandle<CannotRead, CanWrite>, FileError>","open(path, mode) : FilePath -> Append -> Result<FileHandle<CannotRead, CanWrite>, FileError>","open(path, mode) : FilePath -> ReadWrite -> Result<FileHandle<CanRead, CanWrite>, FileError>","open(path, mode) : FilePath -> Read -> (FileHandle<CanRead, CannotWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> Write -> (FileHandle<CannotRead, CanWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> Append -> (FileHandle<CannotRead, CanWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> ReadWrite -> (FileHandle<CanRead, CanWrite> -> A) -> Result<A, FileError>","read(path) : FilePath -> Result<String, FileError>","readBytes(path) : FilePath -> Result<Binary, FileError>","writeBytes(path, content) : FilePath -> Binary -> Bool","write(path, content) : FilePath -> String -> Bool","append(path, content) : FilePath -> String -> Bool","exists?(path) : FilePath -> Bool","file?(path) : FilePath -> Bool","directory?(path) : FilePath -> Bool","delete(path) : FilePath -> Bool","copy(src, dst) : FilePath -> FilePath -> Bool","rename(src, dst) : FilePath -> FilePath -> Bool","readLines(path) : FilePath -> [String]?","feed(path) : FilePath -> Feed<String>?","size(path) : FilePath -> Integer?","absolute(path) : FilePath -> String?"],"summary":"Reading and writing files.\n\nA capability: every member reaches the real filesystem, so a test can replace the whole thing for a lexical region with `with FS.File = ...` instead of mutating global mock state (kexhq/kex#143). The boundary is here rather than on `FS`, because `FS.Path` below is pure string work with no implementation to substitute.","types":["FilePath","Read","Result<FileHandle<CanRead, CannotWrite>, FileError>","Write","Result<FileHandle<CannotRead, CanWrite>, FileError>","Append","ReadWrite","Result<FileHandle<CanRead, CanWrite>, FileError>","((FileHandle<CanRead, CannotWrite>) -> A) -> Result<A, FileError>","((FileHandle<CannotRead, CanWrite>) -> A) -> Result<A, FileError>","((FileHandle<CanRead, CanWrite>) -> A) -> Result<A, FileError>","Result<String, FileError>","Result<Binary, FileError>","Binary","Bool","String","[String]?","Feed<String>?","Integer?","String?"],"urlPath":"fs"},{"anchor":"function-open","kind":"function","line":102,"name":"open","qualifiedName":"FS.File.open","signatures":["open(path, mode) : FilePath -> Read -> Result<FileHandle<CanRead, CannotWrite>, FileError>","open(path, mode) : FilePath -> Write -> Result<FileHandle<CannotRead, CanWrite>, FileError>","open(path, mode) : FilePath -> Append -> Result<FileHandle<CannotRead, CanWrite>, FileError>","open(path, mode) : FilePath -> ReadWrite -> Result<FileHandle<CanRead, CanWrite>, FileError>","open(path, mode) : FilePath -> Read -> (FileHandle<CanRead, CannotWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> Write -> (FileHandle<CannotRead, CanWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> Append -> (FileHandle<CannotRead, CanWrite> -> A) -> Result<A, FileError>","open(path, mode) : FilePath -> ReadWrite -> (FileHandle<CanRead, CanWrite> -> A) -> Result<A, FileError>"],"summary":"Opens `path` and returns a `FileHandle` for it.\n\nThe mode decides the handle's type, and the type decides what you may do with it: a handle opened `Read` has no `write`, a handle opened `Write` has no `read`. That is checked at compile time, not at run time.\n\nUse the whole-file `read` and `write` below when a file fits in memory and you have no reason to hold it open; reach for a handle when you want to stream through a large file or make many small writes.\n\nClose the handle when you are done with it.","types":["FilePath","Read","Result<FileHandle<CanRead, CannotWrite>, FileError>","Write","Result<FileHandle<CannotRead, CanWrite>, FileError>","Append","ReadWrite","Result<FileHandle<CanRead, CanWrite>, FileError>","((FileHandle<CanRead, CannotWrite>) -> A) -> Result<A, FileError>","((FileHandle<CannotRead, CanWrite>) -> A) -> Result<A, FileError>","((FileHandle<CanRead, CanWrite>) -> A) -> Result<A, FileError>"],"urlPath":"fs"},{"anchor":"function-read","kind":"function","line":173,"name":"read","qualifiedName":"FS.File.read","signatures":["read(path) : FilePath -> Result<String, FileError>"],"summary":"Reads the whole file and decodes it as UTF-8 text.\n\nAnswers `Error(ReadFailed(path))` when the file does not exist or cannot be read, and `Error(InvalidUtf8(path, offset))` when the bytes are not valid UTF-8, so a missing or non-text file is something you handle rather than something that stops the program. Reach for `FS.File.readBytes` when the contents are not text.","types":["FilePath","Result<String, FileError>"],"urlPath":"fs"},{"anchor":"function-readbytes","kind":"function","line":190,"name":"readBytes","qualifiedName":"FS.File.readBytes","signatures":["readBytes(path) : FilePath -> Result<Binary, FileError>"],"summary":"Reads the whole file as raw bytes, without decoding it as text.\n\nThe byte counterpart of `FS.File.read`: it never validates UTF-8, so it round trips images, archives, and any other non-text payload losslessly. Answers `Error(ReadFailed(path))` when the file cannot be read.","types":["FilePath","Result<Binary, FileError>"],"urlPath":"fs"},{"anchor":"function-writebytes","kind":"function","line":208,"name":"writeBytes","qualifiedName":"FS.File.writeBytes","signatures":["writeBytes(path, content) : FilePath -> Binary -> Bool"],"summary":"Writes `content` to `path` as raw bytes, replacing whatever was there.\n\nThe byte counterpart of `FS.File.write`: the payload lands on disk exactly as given, with no encoding step. Answers `false` when the write fails.","types":["FilePath","Binary","Bool"],"urlPath":"fs"},{"anchor":"function-write","kind":"function","line":231,"name":"write","qualifiedName":"FS.File.write","signatures":["write(path, content) : FilePath -> String -> Bool"],"summary":"Writes `content` to `path`, replacing whatever was there.\n\nCreates the file if it does not exist. The containing directory must already exist: see `FS.Directory.create`. Answers `false` when the write fails.","types":["FilePath","String","Bool"],"urlPath":"fs"},{"anchor":"function-append","kind":"function","line":250,"name":"append","qualifiedName":"FS.File.append","signatures":["append(path, content) : FilePath -> String -> Bool"],"summary":"Adds `content` to the end of `path`, keeping what is already there.\n\nCreates the file if it does not exist, so it is safe to append to a log that has not been started yet.","types":["FilePath","String","Bool"],"urlPath":"fs"},{"anchor":"function-exists?","kind":"function","line":269,"name":"exists?","qualifiedName":"FS.File.exists?","signatures":["exists?(path) : FilePath -> Bool"],"summary":"Returns `true` when something exists at `path`: a file, a directory, or anything else. Use `file?` or `directory?` when the kind matters.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-file?","kind":"function","line":281,"name":"file?","qualifiedName":"FS.File.file?","signatures":["file?(path) : FilePath -> Bool"],"summary":"Returns `true` when `path` exists and is a regular file: not a directory.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-directory?","kind":"function","line":295,"name":"directory?","qualifiedName":"FS.File.directory?","signatures":["directory?(path) : FilePath -> Bool"],"summary":"Returns `true` when `path` exists and is a directory.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-delete","kind":"function","line":313,"name":"delete","qualifiedName":"FS.File.delete","signatures":["delete(path) : FilePath -> Bool"],"summary":"Deletes the file at `path`.\n\nAnswers `false` when the file does not exist or cannot be removed. Use `FS.Directory.delete` for a directory.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-copy","kind":"function","line":328,"name":"copy","qualifiedName":"FS.File.copy","signatures":["copy(src, dst) : FilePath -> FilePath -> Bool"],"summary":"Copies the file at `src` to `dst`, replacing `dst` if it exists.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-rename","kind":"function","line":343,"name":"rename","qualifiedName":"FS.File.rename","signatures":["rename(src, dst) : FilePath -> FilePath -> Bool"],"summary":"Renames (or moves) the file at `src` to `dst`.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-readlines","kind":"function","line":368,"name":"readLines","qualifiedName":"FS.File.readLines","signatures":["readLines(path) : FilePath -> [String]?"],"summary":"Reads the file and returns its lines, without their newlines.\n\nAnswers `None` when the file cannot be read. A trailing newline does not produce a final empty line, so the count is the number of lines you would see in an editor.\n\nNOT `lines`: FilePath is an alias for String, so a receiver function named `lines` here is indistinguishable from String's own `lines` at every call site, and merely saying `using FS` made `text.lines` ambiguous. `readLines` also says what it does: it reads the file.","types":["FilePath","[String]?"],"urlPath":"fs"},{"anchor":"function-feed","kind":"function","line":403,"name":"feed","qualifiedName":"FS.File.feed","signatures":["feed(path) : FilePath -> Feed<String>?"],"summary":"Returns a lazy `Feed` of the file's lines, or `None` when it cannot be read.\n\nUnlike `readLines`, the file is read a line at a time off a handle held open for the feed's lifetime, so this is the way to walk a file too large to hold in memory: nothing but the current line is retained.\n\nA `Feed` rather than a `Stream` because that is what a file honestly is: reading consumes, and there is no rewinding. Taking twice walks forward rather than answering the same lines again. On a file small enough to replay, `toStream` buys that back.\n\nThe feed ends at the last line, so asking for more lines than the file has answers just the lines there are: unlike `Stream.Sequence`, which is deliberately infinite.\n\n```kex\nFS.File.feed(\"two-lines.txt\").map { |lines| lines.take(5) }.or([])\n# => [\"one\", \"two\"]\n```","types":["FilePath","Feed<String>?"],"urlPath":"fs"},{"anchor":"function-size","kind":"function","line":420,"name":"size","qualifiedName":"FS.File.size","signatures":["size(path) : FilePath -> Integer?"],"summary":"Returns the file's size in bytes, or `None` when it cannot be read.\n\nBytes, not characters: a file of non-ASCII text has more bytes than it has characters.","types":["FilePath","Integer?"],"urlPath":"fs"},{"anchor":"function-absolute","kind":"function","line":442,"name":"absolute","qualifiedName":"FS.File.absolute","signatures":["absolute(path) : FilePath -> String?"],"summary":"Resolves `path` against the process's current directory and returns the absolute form, or `None` when it cannot be resolved.\n\nThis is the one path operation that is not in `FS.Path`, because it is not lexical: it asks the process where it is.\n\nPath manipulation lives in FS.Path: `basename`, `dirname`, `extension` and `join` used to be here too, but a name cannot sit in both modules: FilePath IS String, so two same-named receiver functions on it are indistinguishable at every call site. `absolute` stays because it is not lexical: it asks the process where it is.","types":["FilePath","String?"],"urlPath":"fs"},{"anchor":"module-fs-path","kind":"module","line":456,"name":"FS.Path","qualifiedName":"FS.Path","signatures":["join(a, b) : FilePath -> FilePath -> String","join(a, b) : FilePath -> FilePath -> FilePath -> String","joinAll(parts) : [FilePath] -> String","normalize(path) : FilePath -> String","segments(path) : FilePath -> [String]","absolute?(path) : FilePath -> Bool","relative?(path) : FilePath -> Bool","dirname(path) : FilePath -> String","basename(path) : FilePath -> String","extension(path) : FilePath -> String","stem(path) : FilePath -> String","withExtension(path, wanted) : FilePath -> String -> String","relativeTo(path, base) : FilePath -> FilePath -> String"],"summary":"Path arithmetic: joining, splitting, normalising and comparing paths.\n\nEverything here is pure string manipulation with no filesystem access, so the answer is the same whether or not the path exists, which also means these functions can be called from ordinary pure code, unlike `FS.File`. POSIX separators only for now.\n\n```kex\nFS.Path.join(\"src\", \"main.kex\")            # => \"src/main.kex\"\nFS.Path.extension(\"src/main.kex\")          # => \".kex\"\nFS.Path.withExtension(\"src/main.kex\", \"beam\")   # => \"src/main.beam\"\n```","types":["String","FilePath","(FilePath) -> String","[FilePath]","[String]","Bool"],"urlPath":"fs"},{"anchor":"constant-separator","kind":"constant","line":463,"name":"separator","qualifiedName":"FS.Path.separator","signatures":[],"summary":"The path separator, `\"/\"`.","types":["String"],"urlPath":"fs"},{"anchor":"function-join","kind":"function","line":484,"name":"join","qualifiedName":"FS.Path.join","signatures":["join(a, b) : FilePath -> FilePath -> String","join(a, b) : FilePath -> FilePath -> FilePath -> String"],"summary":"Joins two path parts with a single separator and normalises the result.\n\nRepeated separators collapse, so `join(\"a/\", \"/b\")` is `\"a/b\"` and not `\"a//b\"`. An absolute second part does NOT restart the path, the way Ruby's `Pathname#join` would: use that part on its own if that is what you mean.","types":["FilePath","String","(FilePath) -> String"],"urlPath":"fs"},{"anchor":"function-joinall","kind":"function","line":516,"name":"joinAll","qualifiedName":"FS.Path.joinAll","signatures":["joinAll(parts) : [FilePath] -> String"],"summary":"Joins any number of path parts, skipping empty ones, and normalises the result. An empty list gives `\".\"`.\n\nThe list form is `joinAll`, not another `join` overload: a list receiver already has `List.join`, and a second one-argument `join` on the same receiver would be indistinguishable from it.","types":["[FilePath]","String"],"urlPath":"fs"},{"anchor":"function-normalize","kind":"function","line":540,"name":"normalize","qualifiedName":"FS.Path.normalize","signatures":["normalize(path) : FilePath -> String"],"summary":"Resolves `.` and `..` in a path, lexically.\n\nBecause it is lexical it never follows a symlink and never touches the disk. A leading `..` in a RELATIVE path is kept: there is no way to know what it escapes to, while one in an absolute path is dropped, since `/` has no parent.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"function-segments","kind":"function","line":568,"name":"segments","qualifiedName":"FS.Path.segments","signatures":["segments(path) : FilePath -> [String]"],"summary":"Returns the non-empty parts of the normalised path. The root `/` and the current directory `.` have none.","types":["FilePath","[String]"],"urlPath":"fs"},{"anchor":"function-absolute?","kind":"function","line":583,"name":"absolute?","qualifiedName":"FS.Path.absolute?","signatures":["absolute?(path) : FilePath -> Bool"],"summary":"Returns `true` when the path starts at the root.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-relative?","kind":"function","line":595,"name":"relative?","qualifiedName":"FS.Path.relative?","signatures":["relative?(path) : FilePath -> Bool"],"summary":"Returns `true` when the path does not start at the root. The opposite of `absolute?`.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-dirname","kind":"function","line":613,"name":"dirname","qualifiedName":"FS.Path.dirname","signatures":["dirname(path) : FilePath -> String"],"summary":"Returns the path's parent directory.\n\nA child of the root has `\"/\"` as its parent; a bare name has `\".\"`, the current directory.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"function-basename","kind":"function","line":640,"name":"basename","qualifiedName":"FS.Path.basename","signatures":["basename(path) : FilePath -> String"],"summary":"Returns the last segment of the path: the file or directory name.\n\nThe root itself answers `\"/\"`, and an empty path answers `\".\"`.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"function-extension","kind":"function","line":667,"name":"extension","qualifiedName":"FS.Path.extension","signatures":["extension(path) : FilePath -> String"],"summary":"Returns the file extension, including its leading dot, or `\"\"` when there is none.\n\nOnly the last extension counts, so `\"a.tar.gz\"` has `\".gz\"`. A leading dot is part of the NAME rather than an extension, so `\".gitignore\"` has none.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"function-stem","kind":"function","line":694,"name":"stem","qualifiedName":"FS.Path.stem","signatures":["stem(path) : FilePath -> String"],"summary":"Returns the basename with its extension removed.\n\nA name that IS its extension (`\".gitignore\"`) keeps it, because it has none to drop.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"function-withextension","kind":"function","line":718,"name":"withExtension","qualifiedName":"FS.Path.withExtension","signatures":["withExtension(path, wanted) : FilePath -> String -> String"],"summary":"Returns the path with its extension replaced by `wanted`.\n\nThe new extension may be written with or without its leading dot; an empty one removes the extension entirely.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"function-relativeto","kind":"function","line":745,"name":"relativeTo","qualifiedName":"FS.Path.relativeTo","signatures":["relativeTo(path, base) : FilePath -> FilePath -> String"],"summary":"Expresses `path` relative to `base`, walking up with `..` as needed.\n\nPurely lexical, so it answers only when both sides are anchored the same way; a relative path against an absolute base, or the reverse, comes back unchanged. A path that IS the base answers `\".\"`.","types":["FilePath","String"],"urlPath":"fs"},{"anchor":"module-fs-directory","kind":"module","line":795,"name":"FS.Directory","qualifiedName":"FS.Directory","signatures":["exists?(path) : FilePath -> Bool","directory?(path) : FilePath -> Bool","file?(path) : FilePath -> Bool","create(path) : FilePath -> Bool","delete(path) : FilePath -> Bool","deleteAll(path) : FilePath -> Bool","list(path) : FilePath -> [String]?","files(path) : FilePath -> [String]?","directories(path) : FilePath -> [String]?","current() : String","home() : String?","temporary() : String"],"summary":"Creating, listing and removing directories.\n\nLike `FS.File`, everything here reaches the real filesystem and so is `foul`. Listings answer with an `Optional`, so a directory that cannot be read is a value you handle rather than an exception.","types":["FilePath","Bool","[String]?"],"urlPath":"fs"},{"anchor":"function-exists?","kind":"function","line":803,"name":"exists?","qualifiedName":"FS.Directory.exists?","signatures":["exists?(path) : FilePath -> Bool"],"summary":"Returns `true` when something exists at `path`.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-directory?","kind":"function","line":814,"name":"directory?","qualifiedName":"FS.Directory.directory?","signatures":["directory?(path) : FilePath -> Bool"],"summary":"Returns `true` when `path` exists and is a directory.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-file?","kind":"function","line":824,"name":"file?","qualifiedName":"FS.Directory.file?","signatures":["file?(path) : FilePath -> Bool"],"summary":"Returns `true` when `path` exists and is a regular file.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-create","kind":"function","line":837,"name":"create","qualifiedName":"FS.Directory.create","signatures":["create(path) : FilePath -> Bool"],"summary":"Creates the directory at `path`.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-delete","kind":"function","line":849,"name":"delete","qualifiedName":"FS.Directory.delete","signatures":["delete(path) : FilePath -> Bool"],"summary":"Removes the directory at `path`, which must be empty.\n\nUse `deleteAll` to remove a directory together with its contents.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-deleteall","kind":"function","line":866,"name":"deleteAll","qualifiedName":"FS.Directory.deleteAll","signatures":["deleteAll(path) : FilePath -> Bool"],"summary":"Removes the directory at `path` and everything inside it, recursively.\n\nThis deletes data and cannot be undone: check the path before calling it, particularly when it was computed or came from user input.","types":["FilePath","Bool"],"urlPath":"fs"},{"anchor":"function-list","kind":"function","line":883,"name":"list","qualifiedName":"FS.Directory.list","signatures":["list(path) : FilePath -> [String]?"],"summary":"Lists the names in `path`: both files and directories, one level deep.\n\nThe results are bare names, not paths; join them with `path` to get something you can open. Answers `None` when the directory cannot be read.","types":["FilePath","[String]?"],"urlPath":"fs"},{"anchor":"function-files","kind":"function","line":898,"name":"files","qualifiedName":"FS.Directory.files","signatures":["files(path) : FilePath -> [String]?"],"summary":"Lists only the regular files in `path`, one level deep.","types":["FilePath","[String]?"],"urlPath":"fs"},{"anchor":"function-directories","kind":"function","line":913,"name":"directories","qualifiedName":"FS.Directory.directories","signatures":["directories(path) : FilePath -> [String]?"],"summary":"Lists only the subdirectories of `path`, one level deep.","types":["FilePath","[String]?"],"urlPath":"fs"},{"anchor":"function-current","kind":"function","line":925,"name":"current","qualifiedName":"FS.Directory.current","signatures":["current() : String"],"summary":"Returns the process's current working directory, as an absolute path.","types":[],"urlPath":"fs"},{"anchor":"function-home","kind":"function","line":938,"name":"home","qualifiedName":"FS.Directory.home","signatures":["home() : String?"],"summary":"Returns the current user's home directory, or `None` when it cannot be determined.","types":[],"urlPath":"fs"},{"anchor":"function-temporary","kind":"function","line":960,"name":"temporary","qualifiedName":"FS.Directory.temporary","signatures":["temporary() : String"],"summary":"Returns the directory this system puts temporary files in.\n\nTotal, unlike `home`: it answers `TMPDIR` when the environment sets one (`TEMP` or `TMP` on Windows) and falls back to `/tmp`, so there is always somewhere to write. The path never ends in a separator, so it composes with `FS.Path.join` directly.\n\nThe directory is shared with every other process on the machine, so pick a name unlikely to collide and delete it when you are done.","types":[],"urlPath":"fs"},{"anchor":"module-io","kind":"module","line":13,"name":"IO","qualifiedName":"IO","signatures":["printLine(msg) : Showable -> Void","print(msg) : Showable -> Void","inspect(val) : A -> A","getLine() : String?","get() : String?","printError(msg) : Showable -> Void","warn(msg) : Showable -> Void","warning(msg) : Showable -> Void"],"summary":"Console input and output.\n\n`IO` is a capability: every function in it touches the outside world, so it can only be called from `foul` code (or from `main`). Reading a line and printing a line are the two workhorses; `inspect` is the debugging tool that can be dropped into the middle of a chain without changing its value.\n\n```kex\nmain do\n  IO.print(\"name? \")\n  let name = IO.getLine.or(\"world\")\n  IO.printLine(\"hello, ${name.trim}\")\nend\n```","types":["Showable","Void","A","FileHandle<CannotRead, CanWrite>","FileHandle<CanRead, CannotWrite>"],"urlPath":"io"},{"anchor":"function-printline","kind":"function","line":32,"name":"printLine","qualifiedName":"IO.printLine","signatures":["printLine(msg) : Showable -> Void"],"summary":"Writes `msg` to stdout followed by a newline.\n\nAny `Showable` value is accepted, not just strings: numbers, lists, maps and records print through their own `show` implementation. Called with no argument it prints an empty line.","types":["Showable","Void"],"urlPath":"io"},{"anchor":"function-print","kind":"function","line":52,"name":"print","qualifiedName":"IO.print","signatures":["print(msg) : Showable -> Void"],"summary":"Writes `msg` to stdout without a trailing newline.\n\nUse it to build a line from several pieces, or to write a prompt that the cursor should stay on.","types":["Showable","Void"],"urlPath":"io"},{"anchor":"function-inspect","kind":"function","line":80,"name":"inspect","qualifiedName":"IO.inspect","signatures":["inspect(val) : A -> A"],"summary":"Writes a colored, structured rendering of `val` to stderr and returns `val` unchanged.\n\nBecause it returns its argument, `inspect` can be spliced into the middle of a chain to see what is flowing through it, then removed again without touching the surrounding code. It writes to stderr, so it does not disturb a program whose stdout is piped somewhere. The type checker treats it as pure, so it is allowed inside pure functions.\n\nUse `inspected` instead when you want the rendering as a `String` rather than written out.","types":["A"],"urlPath":"io"},{"anchor":"function-getline","kind":"function","line":109,"name":"getLine","qualifiedName":"IO.getLine","signatures":["getLine() : String?"],"summary":"Reads one line from stdin, without the trailing newline.\n\nReturns `None` at end of input, which is what makes it usable as a loop condition: the `None` is the end of the stream, not an error.","types":[],"urlPath":"io"},{"anchor":"function-get","kind":"function","line":122,"name":"get","qualifiedName":"IO.get","signatures":["get() : String?"],"summary":"Reads a single character from stdin.\n\nReturns `None` at end of input. Note that the result is a one-character `String`, not a `Char`.","types":[],"urlPath":"io"},{"anchor":"function-printerror","kind":"function","line":140,"name":"printError","qualifiedName":"IO.printError","signatures":["printError(msg) : Showable -> Void"],"summary":"Writes `msg` to stderr followed by a newline.\n\nDiagnostics belong on stderr so that a program's real output can be piped or redirected on its own. Unlike a raised error, this only prints: it does not stop the program.","types":["Showable","Void"],"urlPath":"io"},{"anchor":"function-warn","kind":"function","line":152,"name":"warn","qualifiedName":"IO.warn","signatures":["warn(msg) : Showable -> Void"],"summary":"Writes `msg` to stderr. Identical to `printError`, named for the case where the message is a warning rather than a failure.","types":["Showable","Void"],"urlPath":"io"},{"anchor":"function-warning","kind":"function","line":162,"name":"warning","qualifiedName":"IO.warning","signatures":["warning(msg) : Showable -> Void"],"summary":"Writes `msg` to stderr. The long spelling of `warn`.","types":["Showable","Void"],"urlPath":"io"},{"anchor":"constant-out","kind":"constant","line":195,"name":"out","qualifiedName":"IO.out","signatures":[],"summary":"The three standard streams, as ordinary handle VALUES.\n\n`IO.printLine(x)` and `IO.out.printLine(x)` are the same call: the convenience spelling stays, and the handle behind it is now something a program can name, pass and substitute. That is what `Mock.IO` cannot do: it is one global switch, so output from one library cannot go to a buffer while another's goes to the terminal, and a library cannot ACCEPT a sink (kexhq/kex#139).\n\nTypestate says what each one permits: writing to `IO.in`, or reading from `IO.out`, is a compile error, exactly as it is for a file opened `Read`.\n\nThese three are PURE, so they are not part of the capability interface a stand-in must implement: naming a device performs no effect, writing THROUGH it does, and the handle methods are the `foul` ones. That also draws the seam between the two ways to redirect output: `with IO = ...` replaces the CALLS, so it does not touch a handle obtained here, while `Mock.IO` replaces the DEVICE (a group leader, kexhq/kex#141) and so captures `IO.out.printLine(x)` and `IO.printLine(x)` alike.","types":["FileHandle<CannotRead, CanWrite>"],"urlPath":"io"},{"anchor":"constant-error","kind":"constant","line":205,"name":"error","qualifiedName":"IO.error","signatures":[],"summary":"Standard error, as a handle. The sink `IO.printError` and `IO.warn` write to, reachable as a value.","types":["FileHandle<CannotRead, CanWrite>"],"urlPath":"io"},{"anchor":"constant-in","kind":"constant","line":219,"name":"in","qualifiedName":"IO.in","signatures":[],"summary":"Standard input, as a handle. The source `IO.getLine` and `IO.get` read from, reachable as a value.","types":["FileHandle<CanRead, CannotWrite>"],"urlPath":"io"},{"anchor":"module-json","kind":"module","line":26,"name":"JSON","qualifiedName":"JSON","signatures":["parse(text) : String -> Result<Any, Error>","parse(text) : String -> {Atom: Bool} -> Result<Any, Error>","stringify(value) : Any -> String"],"summary":"","types":["String","Integer","Atom","Result<Any, Error>","({Atom: Bool}) -> Result<Any, Error>","Any"],"urlPath":"json"},{"anchor":"type-error","kind":"type","line":33,"name":"Error","qualifiedName":"JSON.Error","signatures":[],"summary":"Why a document could not be parsed. Every variant carries the position in the input where the parser stopped, so a caller can point at the problem.\n\n```kex\nJSON.parse(\"[1, 2\")    # => Error(UnexpectedEnd(5))\nJSON.parse(\"// c\\n1\")  # => Error(UnexpectedCharacter(\"/\", 0))\n```","types":["String","Integer","Integer","String","Integer","String","Integer","String","Integer","String","Integer","Integer","Integer","Atom"],"urlPath":"json"},{"anchor":"function-parse","kind":"function","line":63,"name":"parse","qualifiedName":"JSON.parse","signatures":["parse(text) : String -> Result<Any, Error>","parse(text) : String -> {Atom: Bool} -> Result<Any, Error>"],"summary":"Parses a JSON document, strictly.\n\nThe whole text must be one JSON value with nothing after it: trailing input is `TrailingInput`, not a silently ignored tail. Objects come back as maps with atom keys, arrays as lists, `null` as `None`.","types":["String","Result<Any, Error>","({Atom: Bool}) -> Result<Any, Error>"],"urlPath":"json"},{"anchor":"function-stringify","kind":"function","line":136,"name":"stringify","qualifiedName":"JSON.stringify","signatures":["stringify(value) : Any -> String"],"summary":"Renders a Kex value as strict JSON text.\n\nMaps become objects, lists become arrays, `None` becomes `null`, and strings are escaped. A map written with atom keys (the usual Kex spelling) renders with those names as strings, so `{ name: \"Ada\" }` becomes `{\"name\":\"Ada\"}`. Object keys come out in canonical key order.\n\nAnything the encoder does not recognise renders as `null` rather than failing, so this never raises.","types":["Any","String"],"urlPath":"json"},{"anchor":"trait-inspectable","kind":"trait","line":13,"name":"Inspectable","qualifiedName":"Inspectable","signatures":["inspectValue : Bool -> String"],"summary":"Types that can be rendered structurally, for a person reading output.\n\nThe rendering shows the value's STRUCTURE: quotes on strings, `Just(...)` around an optional, which is what makes it right for debugging and wrong for user-facing text. `Showable` is the other half of that pair.\n\nEvery type is inspectable through a structural fallback, so `inspected` and `IO.inspect` work on anything; a type that wants a different rendering overrides `inspectValue`.\n\n```kex\n[1, 2].inspected       # => \"[1, 2]\"\nJust(\"hi\").inspected   # => \"Just(\\\"hi\\\")\"\n```","types":["(Bool) -> String"],"urlPath":"kex"},{"anchor":"fn-inspectvalue","kind":"function","line":24,"name":"inspectValue","qualifiedName":"Inspectable.inspectValue","signatures":["inspectValue : Bool -> String"],"summary":"Renders the value structurally, with ANSI colors when `colors` is true.\n\n`inspected` and `IO.inspect` call this for you, passing the console's own color setting: call it directly only when you need to force one.","types":["(Bool) -> String"],"urlPath":"kex"},{"anchor":"trait-showable","kind":"trait","line":37,"name":"Showable","qualifiedName":"Showable","signatures":["showValue : String"],"summary":"Types that have a concise, user-facing text representation.\n\n`Showable` is used by interpolation, printing, and `to(String)`. Its output should describe the value itself rather than its implementation structure: a string is shown without quotes, `Just(x)` is shown as `x`, and `None` is shown as an empty string. Use `Inspectable` when debugging structure matters.\n\n```kex\n\"hello\".showValue          # => \"hello\"\nJust(\"hello\").showValue    # => \"hello\"\nNone.showValue             # => \"\"\n```","types":["String"],"urlPath":"kex"},{"anchor":"fn-showvalue","kind":"function","line":45,"name":"showValue","qualifiedName":"Showable.showValue","signatures":["showValue : String"],"summary":"Returns the value's user-facing text representation.\n\nImplement this for domain types whose useful presentation differs from their structural rendering. Keep the result free of ANSI styling so it is safe in files, logs, and interpolation as well as on a terminal.","types":["String"],"urlPath":"kex"},{"anchor":"make-inspectable","kind":"make","line":50,"name":"Inspectable","qualifiedName":"Inspectable","signatures":["inspectValue(colors)"],"summary":"Structural fallback: the runtime only decomposes the value; implementation selection and overrides remain ordinary Kex trait dispatch.","types":[],"urlPath":"kex"},{"anchor":"fn-inspectvalue","kind":"function","line":51,"name":"inspectValue","qualifiedName":"Inspectable.inspectValue","signatures":["inspectValue(colors)"],"summary":"","types":[],"urlPath":"kex"},{"anchor":"make-showable","kind":"make","line":57,"name":"Showable","qualifiedName":"Showable","signatures":["to(String)"],"summary":"Current non-colored presentation for the primitive and standard compound types registered as Showable. Domain types can replace this with what their users care about (Time, Date, and DateTime do so in time.kex).","types":[],"urlPath":"kex"},{"anchor":"fn-to","kind":"function","line":59,"name":"to","qualifiedName":"Showable.to","signatures":["to(String)"],"summary":"","types":[],"urlPath":"kex"},{"anchor":"make-optional<showable>","kind":"make","line":62,"name":"Optional<Showable>","qualifiedName":"Optional<Showable>","signatures":["showValue(@Just(x))"],"summary":"","types":[],"urlPath":"kex"},{"anchor":"fn-showvalue","kind":"function","line":63,"name":"showValue","qualifiedName":"Optional<Showable>.showValue","signatures":["showValue(@Just(x))"],"summary":"","types":[],"urlPath":"kex"},{"anchor":"make-result<x,-e>","kind":"make","line":81,"name":"Result<X, E>","qualifiedName":"Result<X, E>","signatures":["showValue(@Ok(x))"],"summary":"The same for Result, with one deliberate asymmetry: SHOWING a result shows what it carries, so `Ok(42)` reads as `42` rather than leaking the wrapper into text, but an `Error` keeps its marker. Dropping it made a failure and a success print identically, and unlike `None` (which shows as \"\", visibly not a value) there would be nothing left to tell them apart.\n\nThe marker is spelled `Error(...)`, matching how the same value renders as an ELEMENT of a collection: `[1, Error(Bad(no))]`. Elements render structurally rather than through showValue (as in Ruby, where `nil.to_s` is \"\" but `[nil].to_s` is \"[nil]\"), so this is the one spelling that reads the same at both levels.\n\nWhat functions RETURN is unchanged: `Integer.parse` still answers with a Result, `IO.inspect` still shows `Ok(42)`, and both arms are still matchable.","types":[],"urlPath":"kex"},{"anchor":"fn-showvalue","kind":"function","line":82,"name":"showValue","qualifiedName":"Result<X, E>.showValue","signatures":["showValue(@Ok(x))"],"summary":"","types":[],"urlPath":"kex"},{"anchor":"module-kex","kind":"module","line":91,"name":"Kex","qualifiedName":"Kex","signatures":["has?(f) : Feature -> Bool"],"summary":"The running toolchain, which backend, which version, which features.\n\n```kex\nKex.BACKEND                  # => Interpreter\nKex.Kernel.VERSION.release   # => \"0.4.0\"\nKex.Feature.has?(Kex.FS)     # => true\n```","types":["Backend","?","Integer","String?","String","Version","Feature","Bool","[Feature]"],"urlPath":"kex"},{"anchor":"type-backend","kind":"type","line":94,"name":"Backend","qualifiedName":"Kex.Backend","signatures":[],"summary":"Which backend is executing the program: the tree-walking `Interpreter`, or the `Beam` virtual machine.","types":[],"urlPath":"kex"},{"anchor":"type-feature","kind":"type","line":98,"name":"Feature","qualifiedName":"Kex.Feature","signatures":[],"summary":"An optional capability a build may or may not include. Ask about one with `Kex.Feature.has?` before relying on it.","types":[],"urlPath":"kex"},{"anchor":"constant-backend","kind":"constant","line":108,"name":"BACKEND","qualifiedName":"Kex.BACKEND","signatures":[],"summary":"Which backend is executing this program.\n\n`interpreted?` and `underBeam?` below are the readable way to ask.","types":["Backend"],"urlPath":"kex"},{"anchor":"constant-interpreted?","kind":"constant","line":117,"name":"interpreted?","qualifiedName":"Kex.interpreted?","signatures":[],"summary":"Returns `true` when running on the tree-walking interpreter.","types":["?"],"urlPath":"kex"},{"anchor":"constant-underbeam?","kind":"constant","line":128,"name":"underBeam?","qualifiedName":"Kex.underBeam?","signatures":[],"summary":"Returns `true` when running on the BEAM.\n\nThe backend a program is on decides what is available: processes and the web server need the BEAM (`kex -R file.kex`).","types":["?"],"urlPath":"kex"},{"anchor":"module-kex-kernel","kind":"module","line":134,"name":"Kex.Kernel","qualifiedName":"Kex.Kernel","signatures":[],"summary":"Build identity for the compiler and runtime executing this program.\n\nUseful in bug reports, generated artifacts, and compatibility checks where `Kex.BACKEND` alone is not enough to identify the toolchain.","types":["Integer","String?","String","Version"],"urlPath":"kex"},{"anchor":"record-version","kind":"record","line":146,"name":"Version","qualifiedName":"Kex.Kernel.Version","signatures":[],"summary":"The toolchain a program is running on. `kex --version` and the REPL banner report the same numbers.\n\n`revision` is the git commit the compiler was built from: `None` when it was built from a source archive rather than a checkout, which is why it is an Optional rather than a String.","types":["Integer","String?","String"],"urlPath":"kex"},{"anchor":"make-version","kind":"make","line":165,"name":"Version","qualifiedName":"Version","signatures":[],"summary":"","types":[],"urlPath":"kex"},{"anchor":"constant-version","kind":"constant","line":228,"name":"VERSION","qualifiedName":"Kex.Kernel.VERSION","signatures":[],"summary":"This build's version.","types":["Version"],"urlPath":"kex"},{"anchor":"module-kex-feature","kind":"module","line":240,"name":"Kex.Feature","qualifiedName":"Kex.Feature","signatures":["has?(f) : Feature -> Bool"],"summary":"Which optional capabilities this build includes.\n\nOptional non-network capabilities in this build. Networking has its own granular opt-in `Net.Support` report.","types":["Feature","Bool","[Feature]"],"urlPath":"kex"},{"anchor":"function-has?","kind":"function","line":249,"name":"has?","qualifiedName":"Kex.Feature.has?","signatures":["has?(f) : Feature -> Bool"],"summary":"Returns `true` when this build includes `f`.","types":["Feature","Bool"],"urlPath":"kex"},{"anchor":"constant-list","kind":"constant","line":258,"name":"list","qualifiedName":"Kex.Feature.list","signatures":[],"summary":"Every optional capability this build includes.","types":["[Feature]"],"urlPath":"kex"},{"anchor":"module-kex-interface","kind":"module","line":264,"name":"Kex.Interface","qualifiedName":"Kex.Interface","signatures":["read(path) : FS.FilePath -> Any?"],"summary":"Reading the typed public surface of a compiled Kex module.","types":["FS.FilePath","Any?"],"urlPath":"kex"},{"anchor":"function-read","kind":"function","line":279,"name":"read","qualifiedName":"Kex.Interface.read","signatures":["read(path) : FS.FilePath -> Any?"],"summary":"Reads the KexI interface chunk of a compiled Kex module: its typed public surface, and answers the decoded term, or None when the file has no such chunk, does not exist, or is not a BEAM artifact.\n\nThe term is an ordinary tree of tuples, lists, atoms, integers and strings, so it is walked with normal pattern matching and `Tuple.items`. This exists so that reading it needs no `Erlang.*` interop: it is the one intentional entry point rather than a general term decoder.","types":["FS.FilePath","Any?"],"urlPath":"kex"},{"anchor":"function-inspected","kind":"function","line":303,"name":"inspected","qualifiedName":"inspected","signatures":["inspected(value)"],"summary":"Returns the pretty-printed representation of any value as a STRING: the same form the REPL echoes. Universal: reachable on every type through UFCS.\n\nNamed apart from `inspect`, which prints and returns its INPUT so it can be dropped into a pipeline. Both spellings used to be called `inspect`, and which one a call reached depended on whether it was written `x.inspect` or `IO.inspect(x)`, so `[1, 2].inspect.count` answered 24, the length of the rendered string, rather than 2.","types":[],"urlPath":"kex"},{"anchor":"function-inspect","kind":"function","line":316,"name":"inspect","qualifiedName":"inspect","signatures":["inspect(value) : A -> A"],"summary":"Prints the pretty-printed form of `value` to stderr and returns `value` unchanged, so it can be dropped into any pipeline without changing what flows through it. The same operation as `IO.inspect`, reachable by UFCS.","types":["A"],"urlPath":"kex"},{"anchor":"type-list","kind":"type","line":27,"name":"List","qualifiedName":"List","signatures":[],"summary":"An ordered, immutable sequence, written [1, 2, 3].\n\nLists are the default collection in Kex. Every operation answers with a new list rather than changing the receiver, so a chain of transformations is safe to read from either end.\n\n```kex\nlet scores = [7, 2, 9, 4]\nscores.filter { |n| n > 3 }      # => [7, 9, 4]\nscores.sort                      # => [2, 4, 7, 9]\nscores.map { |n| n * 10 }.sum    # => 220\n```\n\nAnything that might not be there: the first element, an element at an index, a search result: answers with an `Optional`, so an empty list is an ordinary case rather than a crash:\n\n```kex\n[].first.or(0)        # => 0\n[1, 2].at(9).or(0)    # => 0\n```\n\nA list is `Enumerable` and `Foldable`, which is where `map`, `filter`, `find`, `all?` and `reduce` come from.","types":[],"urlPath":"list"},{"anchor":"make-[number]","kind":"make","line":29,"name":"[Number]","qualifiedName":"[Number]","signatures":["product(f) : (X -> Number) -> Number"],"summary":"","types":["(X) -> Number","Number"],"urlPath":"list"},{"anchor":"fn-product","kind":"function","line":57,"name":"product","qualifiedName":"[Number].product","signatures":["product(f) : (X -> Number) -> Number"],"summary":"Maps each element through `f` and multiplies the results.","types":["(X) -> Number","Number"],"urlPath":"list"},{"anchor":"make-[x]","kind":"make","line":81,"name":"[X]","qualifiedName":"[X]","signatures":["count(pred) : (X -> Bool) -> Integer","empty? : Bool","find(pred) : (X -> Bool) -> X?","any?(pred) : (X -> Bool) -> Bool","all?(pred) : (X -> Bool) -> Bool","map(f) : (X -> Y) -> [Y]","filter(pred) : (X -> Bool) -> [X]","reject(pred) : (X -> Bool) -> [X]","each(f) : (X -> Void) -> Void","reduce(acc, f) : A -> (A -> X -> A) -> A","flatMap(f) : (X -> [Y]) -> [Y]","at(i) : Integer -> X?","get(i) : Integer -> X?","get(i) : Integer -> X -> X","contains?(elem) : X -> Bool","indexOf(elem) : X -> Integer?","findIndex(pred) : (X -> Bool) -> Integer?","takeWhile : (X -> Bool) -> [X]","dropWhile : (X -> Bool) -> [X]","partition(pred) : (X -> Bool) -> ([X], [X])","collect : (X -> Y?) -> [Y]","take(n) : Integer -> [X]","drop(n) : Integer -> [X]","push(x) : X -> [X]","zip(other) : [Y] -> [(X, Y)]","sort(comp) : (X -> X -> Bool) -> [X]","min(f) : (X -> Y) -> X?","max(f) : (X -> Y) -> X?","sum(f) : (X -> Number) -> Number","join(sep) : String -> String","join : String"],"summary":"","types":["(X) -> Bool","Integer","Bool","X?","(X) -> Y","[Y]","[X]","(X) -> Void","Void","A","(A) -> (X) -> A","(X) -> [Y]","(X) -> X","X","Integer?","((X) -> Bool) -> [X]","([X], [X])","((X) -> Y?) -> [Y]","[(X, Y)]","(X) -> (X) -> Bool","(X) -> Number","Number","String"],"urlPath":"list"},{"anchor":"fn-count","kind":"function","line":166,"name":"count","qualifiedName":"[X].count","signatures":["count(pred) : (X -> Bool) -> Integer"],"summary":"Returns how many elements satisfy `pred`.\n\ncount(pred) is provided by the Enumerable trait.","types":["(X) -> Bool","Integer"],"urlPath":"list"},{"anchor":"fn-empty?","kind":"function","line":175,"name":"empty?","qualifiedName":"[X].empty?","signatures":["empty? : Bool"],"summary":"Returns `true` if the list contains no elements.","types":["Bool"],"urlPath":"list"},{"anchor":"fn-find","kind":"function","line":189,"name":"find","qualifiedName":"[X].find","signatures":["find(pred) : (X -> Bool) -> X?"],"summary":"Returns the first element satisfying the predicate wrapped in `Just`, or `None` if no element matches.\n\nfind/any?/all? are provided by the Enumerable trait.","types":["(X) -> Bool","X?"],"urlPath":"list"},{"anchor":"fn-any?","kind":"function","line":199,"name":"any?","qualifiedName":"[X].any?","signatures":["any?(pred) : (X -> Bool) -> Bool"],"summary":"Returns `true` if at least one element satisfies the predicate.","types":["(X) -> Bool","Bool"],"urlPath":"list"},{"anchor":"fn-all?","kind":"function","line":209,"name":"all?","qualifiedName":"[X].all?","signatures":["all?(pred) : (X -> Bool) -> Bool"],"summary":"Returns `true` if every element satisfies the predicate.","types":["(X) -> Bool","Bool"],"urlPath":"list"},{"anchor":"fn-map","kind":"function","line":219,"name":"map","qualifiedName":"[X].map","signatures":["map(f) : (X -> Y) -> [Y]"],"summary":"Transforms each element by applying `f`.\n\nmap/filter/each are provided by the Enumerable trait (in terms of reduce).","types":["(X) -> Y","[Y]"],"urlPath":"list"},{"anchor":"fn-filter","kind":"function","line":228,"name":"filter","qualifiedName":"[X].filter","signatures":["filter(pred) : (X -> Bool) -> [X]"],"summary":"Returns a new list containing only the elements for which `pred` is `true`.","types":["(X) -> Bool","[X]"],"urlPath":"list"},{"anchor":"fn-reject","kind":"function","line":238,"name":"reject","qualifiedName":"[X].reject","signatures":["reject(pred) : (X -> Bool) -> [X]"],"summary":"Returns a new list with all elements for which `pred` is `true` removed. The inverse of `filter`.","types":["(X) -> Bool","[X]"],"urlPath":"list"},{"anchor":"fn-each","kind":"function","line":247,"name":"each","qualifiedName":"[X].each","signatures":["each(f) : (X -> Void) -> Void"],"summary":"Calls `f` with each element for its side effects. Returns unit.","types":["(X) -> Void","Void"],"urlPath":"list"},{"anchor":"fn-reduce","kind":"function","line":259,"name":"reduce","qualifiedName":"[X].reduce","signatures":["reduce(acc, f) : A -> (A -> X -> A) -> A"],"summary":"Folds the list from the left, starting with `acc` and combining each element via `f`.","types":["A","(A) -> (X) -> A"],"urlPath":"list"},{"anchor":"fn-flatmap","kind":"function","line":266,"name":"flatMap","qualifiedName":"[X].flatMap","signatures":["flatMap(f) : (X -> [Y]) -> [Y]"],"summary":"Maps each element to a list and concatenates the results.","types":["(X) -> [Y]","[Y]"],"urlPath":"list"},{"anchor":"fn-at","kind":"function","line":281,"name":"at","qualifiedName":"[X].at","signatures":["at(i) : Integer -> X?"],"summary":"Returns the element at position `i` (0-based) wrapped in `Just`, or `None` if the index is out of range.","types":["Integer","X?"],"urlPath":"list"},{"anchor":"fn-get","kind":"function","line":293,"name":"get","qualifiedName":"[X].get","signatures":["get(i) : Integer -> X?","get(i) : Integer -> X -> X"],"summary":"Returns the element at position `i` (0-based), or `None` when the index is out of range. The same as `at`.","types":["Integer","X?","(X) -> X"],"urlPath":"list"},{"anchor":"fn-contains?","kind":"function","line":321,"name":"contains?","qualifiedName":"[X].contains?","signatures":["contains?(elem) : X -> Bool"],"summary":"Returns `true` if `elem` is present in the list.","types":["X","Bool"],"urlPath":"list"},{"anchor":"fn-indexof","kind":"function","line":333,"name":"indexOf","qualifiedName":"[X].indexOf","signatures":["indexOf(elem) : X -> Integer?"],"summary":"Returns `true` if the first index at which `elem` appears, wrapped in `Just`, or `None` if the element is not present.","types":["X","Integer?"],"urlPath":"list"},{"anchor":"fn-findindex","kind":"function","line":346,"name":"findIndex","qualifiedName":"[X].findIndex","signatures":["findIndex(pred) : (X -> Bool) -> Integer?"],"summary":"Returns the index of the first element satisfying `pred`, wrapped in `Just`, or `None` if none does. The predicate counterpart of `indexOf`, which searches by value.","types":["(X) -> Bool","Integer?"],"urlPath":"list"},{"anchor":"fn-takewhile","kind":"function","line":363,"name":"takeWhile","qualifiedName":"[X].takeWhile","signatures":["takeWhile : (X -> Bool) -> [X]"],"summary":"Returns the longest leading run of elements satisfying `pred`. Stops at the first element that does not, so it is not `filter`: later matches are dropped with everything after the first failure.","types":["((X) -> Bool) -> [X]"],"urlPath":"list"},{"anchor":"fn-dropwhile","kind":"function","line":377,"name":"dropWhile","qualifiedName":"[X].dropWhile","signatures":["dropWhile : (X -> Bool) -> [X]"],"summary":"Returns what is left after `takeWhile`: everything from the first element that does not satisfy `pred` onwards.","types":["((X) -> Bool) -> [X]"],"urlPath":"list"},{"anchor":"fn-partition","kind":"function","line":401,"name":"partition","qualifiedName":"[X].partition","signatures":["partition(pred) : (X -> Bool) -> ([X], [X])"],"summary":"Splits the list into two lists: those for which `pred` is `true` (first) and those for which it is `false` (second).","types":["(X) -> Bool","([X], [X])"],"urlPath":"list"},{"anchor":"fn-collect","kind":"function","line":421,"name":"collect","qualifiedName":"[X].collect","signatures":["collect : (X -> Y?) -> [Y]"],"summary":"Maps each element through `f` (which returns an `Optional`), keeping and unwrapping the `Just(y)` results and dropping `None`. Filter + map fused.","types":["((X) -> Y?) -> [Y]"],"urlPath":"list"},{"anchor":"fn-take","kind":"function","line":430,"name":"take","qualifiedName":"[X].take","signatures":["take(n) : Integer -> [X]"],"summary":"Returns the first `n` elements.","types":["Integer","[X]"],"urlPath":"list"},{"anchor":"fn-drop","kind":"function","line":440,"name":"drop","qualifiedName":"[X].drop","signatures":["drop(n) : Integer -> [X]"],"summary":"Drops the first `n` elements.","types":["Integer","[X]"],"urlPath":"list"},{"anchor":"fn-push","kind":"function","line":450,"name":"push","qualifiedName":"[X].push","signatures":["push(x) : X -> [X]"],"summary":"Returns a new list with `x` appended at the end.","types":["X","[X]"],"urlPath":"list"},{"anchor":"fn-zip","kind":"function","line":470,"name":"zip","qualifiedName":"[X].zip","signatures":["zip(other) : [Y] -> [(X, Y)]"],"summary":"Pairs each element of this list with the corresponding element of `other`. Stops at the end of the shorter list.","types":["[Y]","[(X, Y)]"],"urlPath":"list"},{"anchor":"fn-sort","kind":"function","line":500,"name":"sort","qualifiedName":"[X].sort","signatures":["sort(comp) : (X -> X -> Bool) -> [X]"],"summary":"Returns the elements sorted using a custom comparator. `comp` should return `true` when its first argument should come before its second.","types":["(X) -> (X) -> Bool","[X]"],"urlPath":"list"},{"anchor":"fn-min","kind":"function","line":511,"name":"min","qualifiedName":"[X].min","signatures":["min(f) : (X -> Y) -> X?"],"summary":"Returns the element with the smallest `f` key wrapped in `Just`, or `None` for an empty list.","types":["(X) -> Y","X?"],"urlPath":"list"},{"anchor":"fn-max","kind":"function","line":522,"name":"max","qualifiedName":"[X].max","signatures":["max(f) : (X -> Y) -> X?"],"summary":"Returns the element with the largest `f` key wrapped in `Just`, or `None` for an empty list.","types":["(X) -> Y","X?"],"urlPath":"list"},{"anchor":"fn-sum","kind":"function","line":532,"name":"sum","qualifiedName":"[X].sum","signatures":["sum(f) : (X -> Number) -> Number"],"summary":"Maps each element through `f` and sums the results.","types":["(X) -> Number","Number"],"urlPath":"list"},{"anchor":"fn-join","kind":"function","line":548,"name":"join","qualifiedName":"[X].join","signatures":["join(sep) : String -> String","join : String"],"summary":"Renders and concatenates the elements, placing `sep` between adjacent values. With no separator, the rendered values are joined directly.\n\nElements do not have to be strings: each is rendered using the same user-facing conversion used by interpolation and printing.","types":["String"],"urlPath":"list"},{"anchor":"type-map","kind":"type","line":27,"name":"Map","qualifiedName":"Map","signatures":[],"summary":"An immutable key-value store, written `{key: value}`.\n\nKeys are compared by structural equality and may be of any type; atom keys get the shorthand `{name: \"Ada\"}`, string keys are written out in full as `{\"name\": \"Ada\"}`. Every method answers with a new map: the `!` forms (`put!`, `delete!`) build a new map and rebind the receiver variable, they do not modify anything in place.\n\nEntries come back in canonical key order, not insertion order, so `keys`, `values`, `entries` and any traversal are stable and comparable across equal maps.\n\n```kex\nlet config = { host: \"localhost\", port: 8080 }\nconfig.get(:host).or(\"0.0.0.0\")     # => \"localhost\"\nconfig.get(:user).or(\"anonymous\")   # => \"anonymous\"\nconfig.put(:port, 9090)             # => { :host: \"localhost\", :port: 9090 }\n```\n\nA map is `Enumerable` and `Foldable`, and the traversal blocks take the key and value as two parameters:\n\n```kex\nconfig.each { |k, v| IO.printLine(\"${k} = ${v}\") }\nconfig.filter { |k, v| k != :port }   # => { :host: \"localhost\" }\n```\n\nDeclared for the same reason list.kex declares `type List<X> = [X]`: it gives the name `Map` a source declaration, so it resolves as a type through the collected interfaces rather than needing to be known to the compiler.","types":[],"urlPath":"map"},{"anchor":"make-map<k,-v>","kind":"make","line":29,"name":"Map<K, V>","qualifiedName":"Map<K, V>","signatures":["reduce(acc, g)","combine(other)","get(key) : K -> V?","get(key) : K -> V -> V","put(k, v) : K -> V -> Map<K, V>","delete(key) : K -> Map<K, V>","has?(key) : K -> Bool","count : (K -> V -> Bool) -> Integer","each : (K -> V -> Void) -> Void","map : (K -> V -> R) -> [R]","mapValues(f) : (V -> W) -> Map<K, W>","mapKeys(f) : (K -> J) -> Map<J, V>","filter(pred) : (K -> V -> Bool) -> Map<K, V>","reject(pred) : (K -> V -> Bool) -> Map<K, V>","merge(other) : Map<K, V> -> Map<K, V>","any? : (K -> V -> Bool) -> Bool","all? : (K -> V -> Bool) -> Bool","find : (K -> V -> Bool) -> (K, V)?"],"summary":"","types":["K","V?","(V) -> V","V","Map<K, V>","Bool","((K) -> (V) -> Bool) -> Integer","((K) -> (V) -> Void) -> Void","((K) -> (V) -> R) -> [R]","(V) -> W","Map<K, W>","(K) -> J","Map<J, V>","(K) -> (V) -> Bool","((K) -> (V) -> Bool) -> Bool","((K) -> (V) -> Bool) -> (K, V)?"],"urlPath":"map"},{"anchor":"fn-reduce","kind":"function","line":49,"name":"reduce","qualifiedName":"Map<K, V>.reduce","signatures":["reduce(acc, g)"],"summary":"Folds over the map's `(key, value)` pairs in canonical key order.\n\nThis is `Map`'s `Enumerable` primitive: `map`, `filter`, `find`, `any?` and the rest are built on it. The block receives the accumulator and one pair; destructure the pair to name its halves.","types":[],"urlPath":"map"},{"anchor":"fn-combine","kind":"function","line":72,"name":"combine","qualifiedName":"Map<K, V>.combine","signatures":["combine(other)"],"summary":"Combines two maps by merging them, with `other`'s values winning on a key conflict. The `Monoid` operation, and the same thing `merge` does.","types":[],"urlPath":"map"},{"anchor":"fn-get","kind":"function","line":90,"name":"get","qualifiedName":"Map<K, V>.get","signatures":["get(key) : K -> V?","get(key) : K -> V -> V"],"summary":"Returns the value stored under `key`, or `None` when the key is absent.\n\nMissing keys are an ordinary answer rather than a failure, so a lookup on data you did not produce is safe by default. Use the two-argument form below when you have a sensible fallback.","types":["K","V?","(V) -> V"],"urlPath":"map"},{"anchor":"fn-put","kind":"function","line":130,"name":"put","qualifiedName":"Map<K, V>.put","signatures":["put(k, v) : K -> V -> Map<K, V>"],"summary":"Returns a new map with `key` mapped to `value`, replacing any previous entry for that key.\n\nThe receiver is untouched. Use `put!` when you want the variable holding the map to be rebound to the result.","types":["K","V","Map<K, V>"],"urlPath":"map"},{"anchor":"fn-delete","kind":"function","line":147,"name":"delete","qualifiedName":"Map<K, V>.delete","signatures":["delete(key) : K -> Map<K, V>"],"summary":"Returns a new map without `key`. A key that is not present is not an error: the map comes back unchanged.\n\nUse `delete!` to rebind the receiver variable.","types":["K","Map<K, V>"],"urlPath":"map"},{"anchor":"fn-has?","kind":"function","line":166,"name":"has?","qualifiedName":"Map<K, V>.has?","signatures":["has?(key) : K -> Bool"],"summary":"Returns `true` when `key` has an entry in the map.\n\nDistinguishes a missing key from one whose value is itself empty, which a `get` with a default cannot.","types":["K","Bool"],"urlPath":"map"},{"anchor":"fn-count","kind":"function","line":199,"name":"count","qualifiedName":"Map<K, V>.count","signatures":["count : (K -> V -> Bool) -> Integer"],"summary":"Returns the number of entries satisfying `pred`.","types":["((K) -> (V) -> Bool) -> Integer"],"urlPath":"map"},{"anchor":"fn-each","kind":"function","line":246,"name":"each","qualifiedName":"Map<K, V>.each","signatures":["each : (K -> V -> Void) -> Void"],"summary":"Calls `f` with each key and value, for its side effects.","types":["((K) -> (V) -> Void) -> Void"],"urlPath":"map"},{"anchor":"fn-map","kind":"function","line":262,"name":"map","qualifiedName":"Map<K, V>.map","signatures":["map : (K -> V -> R) -> [R]"],"summary":"Applies `f` to each key and value and collects the results into a LIST.\n\nNote the return type: `map` comes from `Enumerable`, whose contract is to produce a list, because `f` may return anything at all. Use `mapValues` or `mapKeys` when you want a map back.","types":["((K) -> (V) -> R) -> [R]"],"urlPath":"map"},{"anchor":"fn-mapvalues","kind":"function","line":275,"name":"mapValues","qualifiedName":"Map<K, V>.mapValues","signatures":["mapValues(f) : (V -> W) -> Map<K, W>"],"summary":"Returns a new map with every value replaced by `f(value)`. The keys are left alone.","types":["(V) -> W","Map<K, W>"],"urlPath":"map"},{"anchor":"fn-mapkeys","kind":"function","line":295,"name":"mapKeys","qualifiedName":"Map<K, V>.mapKeys","signatures":["mapKeys(f) : (K -> J) -> Map<J, V>"],"summary":"Returns a new map with every key replaced by `f(key)`. The values are left alone.\n\nIf `f` maps two keys onto the same result, one entry wins: the map cannot hold both.","types":["(K) -> J","Map<J, V>"],"urlPath":"map"},{"anchor":"fn-filter","kind":"function","line":312,"name":"filter","qualifiedName":"Map<K, V>.filter","signatures":["filter(pred) : (K -> V -> Bool) -> Map<K, V>"],"summary":"Returns a new map with only the entries for which `pred` answers `true`.\n\nMap overrides the map-returning HOFs (Enumerable's default returns a list).","types":["(K) -> (V) -> Bool","Map<K, V>"],"urlPath":"map"},{"anchor":"fn-reject","kind":"function","line":329,"name":"reject","qualifiedName":"Map<K, V>.reject","signatures":["reject(pred) : (K -> V -> Bool) -> Map<K, V>"],"summary":"Returns a new map with the entries for which `pred` answers `true` removed. The complement of `filter`.","types":["(K) -> (V) -> Bool","Map<K, V>"],"urlPath":"map"},{"anchor":"fn-merge","kind":"function","line":349,"name":"merge","qualifiedName":"Map<K, V>.merge","signatures":["merge(other) : Map<K, V> -> Map<K, V>"],"summary":"Returns a new map holding the entries of both. When a key appears in both, `other`'s value wins.\n\nThe right-biased rule is what makes this the natural way to apply overrides on top of defaults.","types":["Map<K, V>"],"urlPath":"map"},{"anchor":"fn-any?","kind":"function","line":361,"name":"any?","qualifiedName":"Map<K, V>.any?","signatures":["any? : (K -> V -> Bool) -> Bool"],"summary":"Returns `true` when at least one entry satisfies `pred`. Stops at the first match.","types":["((K) -> (V) -> Bool) -> Bool"],"urlPath":"map"},{"anchor":"fn-all?","kind":"function","line":375,"name":"all?","qualifiedName":"Map<K, V>.all?","signatures":["all? : (K -> V -> Bool) -> Bool"],"summary":"Returns `true` when every entry satisfies `pred`. The empty map answers `true`.","types":["((K) -> (V) -> Bool) -> Bool"],"urlPath":"map"},{"anchor":"fn-find","kind":"function","line":391,"name":"find","qualifiedName":"Map<K, V>.find","signatures":["find : (K -> V -> Bool) -> (K, V)?"],"summary":"Returns the first entry satisfying `pred` as a `(key, value)` tuple, or `None` when nothing matches.\n\n\"First\" means first in canonical key order.","types":["((K) -> (V) -> Bool) -> (K, V)?"],"urlPath":"map"},{"anchor":"make-map<k,-v>","kind":"make","line":394,"name":"Map<K, V>","qualifiedName":"Map<K, V>","signatures":[],"summary":"","types":[],"urlPath":"map"},{"anchor":"module-math","kind":"module","line":19,"name":"Math","qualifiedName":"Math","signatures":["sqrt(x) : Number -> Float","cbrt(x) : Number -> Float","sin(x) : Number -> Float","cos(x) : Number -> Float","tan(x) : Number -> Float","asin(x) : Number -> Float","acos(x) : Number -> Float","atan(x) : Number -> Float","atan2(y, x) : Number -> Number -> Float","sinh(x) : Number -> Float","cosh(x) : Number -> Float","tanh(x) : Number -> Float","log(x) : Number -> Float","log(x) : Number -> Number -> Float","log2(x) : Number -> Float","log10(x) : Number -> Float","exp(x) : Number -> Float","pow(x, y) : Number -> Number -> Float","abs(x) : Number -> Number","floor(x) : Number -> Integer","ceil(x) : Number -> Integer","hypot(x, y) : Number -> Number -> Float"],"summary":"Mathematical constants and functions.\n\nAll trigonometric functions work in radians. Every function here accepts a `Number` (an `Integer` or a `Float`) and the transcendental ones answer with a `Float`.\n\nA Kex `Float` is always finite, so a domain error (`Math.sqrt(-1.0)`) or an overflow (`Math.exp(1000.0)`) raises rather than producing `NaN` or `Infinity`: the same rule the BEAM enforces, where those two values cannot exist at all. There is no non-finite float to test for afterwards.\n\n```kex\nMath.sqrt(2.0)              # => 1.4142135623730951\nMath.hypot(3.0, 4.0)        # => 5.0\nMath.sin(Math.PI / 2.0)     # => 1.0\n```\n\nThe everyday operations on a single number: `abs`, `floor`, `ceil`, `round`, `sqrt`: are also methods on `Integer` and `Float`, which usually reads better in a chain: `x.abs` over `Math.abs(x)`.","types":["?","Number","Float","(Number) -> Float","Integer"],"urlPath":"math"},{"anchor":"constant-pi","kind":"constant","line":29,"name":"PI","qualifiedName":"Math.PI","signatures":[],"summary":"The ratio of a circle's circumference to its diameter.","types":["?"],"urlPath":"math"},{"anchor":"constant-e","kind":"constant","line":38,"name":"E","qualifiedName":"Math.E","signatures":[],"summary":"The base of the natural logarithm.","types":["?"],"urlPath":"math"},{"anchor":"function-sqrt","kind":"function","line":53,"name":"sqrt","qualifiedName":"Math.sqrt","signatures":["sqrt(x) : Number -> Float"],"summary":"Returns the square root of `x`. Raises for a negative `x`, which has no real root.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-cbrt","kind":"function","line":65,"name":"cbrt","qualifiedName":"Math.cbrt","signatures":["cbrt(x) : Number -> Float"],"summary":"Returns the cube root of `x`. Unlike `sqrt`, negative input is fine: a negative number has a real cube root.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-sin","kind":"function","line":79,"name":"sin","qualifiedName":"Math.sin","signatures":["sin(x) : Number -> Float"],"summary":"Returns the sine of `x`, given in radians.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-cos","kind":"function","line":90,"name":"cos","qualifiedName":"Math.cos","signatures":["cos(x) : Number -> Float"],"summary":"Returns the cosine of `x`, given in radians.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-tan","kind":"function","line":101,"name":"tan","qualifiedName":"Math.tan","signatures":["tan(x) : Number -> Float"],"summary":"Returns the tangent of `x`, given in radians.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-asin","kind":"function","line":112,"name":"asin","qualifiedName":"Math.asin","signatures":["asin(x) : Number -> Float"],"summary":"Returns the arc sine of `x` in radians, in the range -π/2 to π/2.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-acos","kind":"function","line":123,"name":"acos","qualifiedName":"Math.acos","signatures":["acos(x) : Number -> Float"],"summary":"Returns the arc cosine of `x` in radians, in the range 0 to π.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-atan","kind":"function","line":137,"name":"atan","qualifiedName":"Math.atan","signatures":["atan(x) : Number -> Float"],"summary":"Returns the arc tangent of `x` in radians, in the range -π/2 to π/2.\n\nUse `atan2` when you have both coordinates of a vector: it can tell the quadrant apart, and this cannot.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-atan2","kind":"function","line":156,"name":"atan2","qualifiedName":"Math.atan2","signatures":["atan2(y, x) : Number -> Number -> Float"],"summary":"Returns the angle of the vector `(x, y)` in radians, from -π to π.\n\nBoth signs are taken into account, so the result lands in the correct quadrant, which is why this, not `atan`, is what you want for converting a vector to an angle. Note the argument order: `y` first.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-sinh","kind":"function","line":167,"name":"sinh","qualifiedName":"Math.sinh","signatures":["sinh(x) : Number -> Float"],"summary":"Returns the hyperbolic sine of `x`.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-cosh","kind":"function","line":178,"name":"cosh","qualifiedName":"Math.cosh","signatures":["cosh(x) : Number -> Float"],"summary":"Returns the hyperbolic cosine of `x`.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-tanh","kind":"function","line":189,"name":"tanh","qualifiedName":"Math.tanh","signatures":["tanh(x) : Number -> Float"],"summary":"Returns the hyperbolic tangent of `x`, always between -1 and 1.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-log","kind":"function","line":207,"name":"log","qualifiedName":"Math.log","signatures":["log(x) : Number -> Float","log(x) : Number -> Number -> Float"],"summary":"Returns the natural logarithm of `x`: its logarithm to base `e`. With a second argument, returns the logarithm to that base instead.\n\nRaises for `x` of zero or less, which has no real logarithm.","types":["Number","Float","(Number) -> Float"],"urlPath":"math"},{"anchor":"function-log2","kind":"function","line":224,"name":"log2","qualifiedName":"Math.log2","signatures":["log2(x) : Number -> Float"],"summary":"Returns the base-2 logarithm of `x`. The same as `Math.log(x, 2.0)`, and more direct.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-log10","kind":"function","line":235,"name":"log10","qualifiedName":"Math.log10","signatures":["log10(x) : Number -> Float"],"summary":"Returns the base-10 logarithm of `x`.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-exp","kind":"function","line":251,"name":"exp","qualifiedName":"Math.exp","signatures":["exp(x) : Number -> Float"],"summary":"Returns `e` raised to the power `x`: the inverse of `Math.log`.\n\nRaises on overflow, which for a double happens a little past `x` of 709.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-pow","kind":"function","line":270,"name":"pow","qualifiedName":"Math.pow","signatures":["pow(x, y) : Number -> Number -> Float"],"summary":"Returns `x` raised to the power `y`.\n\nThe result is always a `Float`, even for whole arguments, so round it when you need an integer back.","types":["Number","Float"],"urlPath":"math"},{"anchor":"function-abs","kind":"function","line":284,"name":"abs","qualifiedName":"Math.abs","signatures":["abs(x) : Number -> Number"],"summary":"Returns the magnitude of `x`, discarding its sign. The type is preserved: an `Integer` in gives an `Integer` out.\n\n`x.abs` is the same thing as a method, and usually reads better.","types":["Number"],"urlPath":"math"},{"anchor":"function-floor","kind":"function","line":296,"name":"floor","qualifiedName":"Math.floor","signatures":["floor(x) : Number -> Integer"],"summary":"Returns the largest integer that is not greater than `x`: rounding toward negative infinity.","types":["Number","Integer"],"urlPath":"math"},{"anchor":"function-ceil","kind":"function","line":308,"name":"ceil","qualifiedName":"Math.ceil","signatures":["ceil(x) : Number -> Integer"],"summary":"Returns the smallest integer that is not less than `x`: rounding toward positive infinity.","types":["Number","Integer"],"urlPath":"math"},{"anchor":"function-hypot","kind":"function","line":324,"name":"hypot","qualifiedName":"Math.hypot","signatures":["hypot(x, y) : Number -> Number -> Float"],"summary":"Returns the Euclidean distance `sqrt(x*x ` y*y)+, computed so that large values do not overflow on the way.","types":["Number","Float"],"urlPath":"math"},{"anchor":"type-reader","kind":"type","line":27,"name":"Reader","qualifiedName":"Reader","signatures":[],"summary":"Mock: deterministic stand-ins for the world outside the program: the filesystem, environment, and console. Networking mocks live under Mock.Net.\n\nThese are STATEFUL: `Mock.FS.File(path, content)` writes into a store the real `FS.File` then reads back, so a test can write and read again, and `clear()` undoes it. That state is global and lives until cleared, which is what makes hook ordering and write/read round trips testable, and also what makes two tests able to interfere.\n\nWhen a test only needs canned ANSWERS, replacing the capability is the better tool: `with FS.File = MyFake { ... } do ... end` swaps the implementation for one lexical region, holds no global state, needs no clearing, and cannot leak into another test. See spec/capability_stdlib_fs.kex for the shape of a stand-in (kexhq/kex#143).\n\nOpt-in on purpose (issue #144): this module used to ride along inside a prelude networking file, so importing the prelude made every Mock.* reachable from every program without anyone asking for it. Reachable is still not callable: the runtime denies the mock intrinsics outside spec files, the REPL, and --allow-mocks, but it should also not be in scope by accident. A qualified `Mock.FS.File(...)` auto-loads this file like any other opt-in module.\n\nAll Mock.* sub-modules live in a single `module Mock` block so merged compilation units never see duplicate top-level `Mock` modules. A stand-in's read hook: a path in, its content or `None` out.","types":[],"urlPath":"mock"},{"anchor":"type-lookup","kind":"type","line":30,"name":"Lookup","qualifiedName":"Lookup","signatures":[],"summary":"An environment stand-in's lookup hook: a name in, its value or `None` out.","types":[],"urlPath":"mock"},{"anchor":"type-writer","kind":"type","line":38,"name":"Writer","qualifiedName":"Writer","signatures":[],"summary":"An environment stand-in's WRITE hook: the name and the value a program set. A `Mock.Env` is an immutable record, so a write cannot land in its `vars`, and it must not reach the real environment, or a test would leak a variable into the next one and into anything the suite starts. The hook is how a test observes the write instead: record it, assert on it, or ignore it, which is what `None` does.","types":[],"urlPath":"mock"},{"anchor":"module-mock","kind":"module","line":40,"name":"Mock","qualifiedName":"Mock","signatures":["File(path, content) : FS.FilePath -> String -> Void","Directory(path) : FS.FilePath -> Void","clear() : Void","files(entries) : Map<FS.FilePath, String> -> Void","onRead(reader) : Reader -> Void","set(name, value) : String -> String -> Void","unset(name) : String -> Void","clear() : Void","vars(entries) : Map<String, String> -> Void","start()","stop()","output()","clear()","input(lines)","cannedRead(path)","read(path)","readBytes(path)","readLines(path)","feed(path)","size(path)","exists?(path)","file?(path)","directory?(path)","absolute(path)","open(path, mode)","write(path, content)","writeBytes(path, content)","append(path, content)","delete(path)","copy(src, dst)","rename(src, dst)","lookup(key)","get(key)","has?(key)","keys()","values()","count()","each(f)","entries()","set(name, value)","unset(name)"],"summary":"","types":["Map<FS.FilePath, String>","Reader?","Map<String, String>","Lookup?","Writer?","FS.FilePath","String","Void","Reader","[Any]","{String: [String]}","[Binary]"],"urlPath":"mock"},{"anchor":"record-files","kind":"record","line":55,"name":"Files","qualifiedName":"Mock.Files","signatures":[],"summary":"A stand-in for the `FS.File` capability, for `with FS.File = ...`. Unlike the `Mock.*` functions below it holds no global state, needs no `clear()`, and cannot leak past its block. What it cannot do is change: `write` then `read` back is not something a value does, so a test needing that round trip still wants `Mock.FS` (kexhq/kex#143).\n\n```kex\nwith FS.File = Mock.Files { files: {\"kex.toml\": \"name = \\\"demo\\\"\"} } do\n  assert(loadConfig() == \"demo\")\nend\n```\n\n`onRead` takes over when a test needs an ANSWER rather than a fixture: content derived from the path, a failure on the third call, a record of what was asked for. It is consulted first and its `None` means \"no such file\", so a callback can model absence too.","types":["Map<FS.FilePath, String>","Reader?"],"urlPath":"mock"},{"anchor":"record-env","kind":"record","line":71,"name":"Env","qualifiedName":"Mock.Env","signatures":[],"summary":"A stand-in for the `ENV` capability. A name simply left out of `vars` reads as unset, which is the whole reason `Mock.ENV.unset` exists: absence is an answer programs act on. `onGet` answers instead of the map when a test wants a rule rather than a fixture.\n\n```kex\nwith ENV = Mock.Env { vars: {\"HOME\": \"/fake\"} } do\n  assert(configHome() == \"/fake/.config\")\nend\n```","types":["Map<String, String>","Lookup?","Writer?"],"urlPath":"mock"},{"anchor":"module-mock-fs","kind":"module","line":103,"name":"Mock.FS","qualifiedName":"Mock.FS","signatures":["File(path, content) : FS.FilePath -> String -> Void","Directory(path) : FS.FilePath -> Void","clear() : Void","files(entries) : Map<FS.FilePath, String> -> Void","onRead(reader) : Reader -> Void"],"summary":"A stateful stand-in for the filesystem: files a test declares, that the real `FS.File` then reads back.\n\nThe store is global and lives until `clear`, which is what makes a write followed by a read testable, and what makes two tests able to interfere. Clear it in an `after` hook.\n\n```kex\ndescribe \"the config loader\" do\n  before do\n    Mock.FS.files({ \"app.conf\": \"port = 8080\\n\" })\n  end\n\n  after do\n    Mock.FS.clear()\n  end\n\n  it \"reads the port\" do\n    Assert.equal(loadPort(), 8080)\n  end\nend\n```","types":["FS.FilePath","String","Void","Map<FS.FilePath, String>","Reader"],"urlPath":"mock"},{"anchor":"function-file","kind":"function","line":112,"name":"File","qualifiedName":"Mock.FS.File","signatures":["File(path, content) : FS.FilePath -> String -> Void"],"summary":"Declares one file and its content.","types":["FS.FilePath","String","Void"],"urlPath":"mock"},{"anchor":"function-directory","kind":"function","line":121,"name":"Directory","qualifiedName":"Mock.FS.Directory","signatures":["Directory(path) : FS.FilePath -> Void"],"summary":"Declares a directory at `path`.","types":["FS.FilePath","Void"],"urlPath":"mock"},{"anchor":"function-clear","kind":"function","line":134,"name":"clear","qualifiedName":"Mock.FS.clear","signatures":["clear() : Void"],"summary":"Empties the store, so nothing declared so far is visible any more.\n\nCall it in an `after` hook: the store is global, and what one test leaves behind the next one sees.","types":[],"urlPath":"mock"},{"anchor":"function-files","kind":"function","line":149,"name":"files","qualifiedName":"Mock.FS.files","signatures":["files(entries) : Map<FS.FilePath, String> -> Void"],"summary":"Declares the whole fixture in one call.\n\nThe same shape `Mock.Files { files: ... }` takes: one line instead of one per file (kexhq/kex#143).","types":["Map<FS.FilePath, String>","Void"],"urlPath":"mock"},{"anchor":"function-onread","kind":"function","line":164,"name":"onRead","qualifiedName":"Mock.FS.onRead","signatures":["onRead(reader) : Reader -> Void"],"summary":"Answers reads by RULE rather than from a fixture.\n\nContent derived from the path, a failure on the third call, a record of what was asked for. Consulted before the map, and returning `None` means \"no such file\", so absence is expressible too.","types":["Reader","Void"],"urlPath":"mock"},{"anchor":"module-mock-env","kind":"module","line":196,"name":"Mock.ENV","qualifiedName":"Mock.ENV","signatures":["set(name, value) : String -> String -> Void","unset(name) : String -> Void","clear() : Void","vars(entries) : Map<String, String> -> Void"],"summary":"Overlays the process environment, so a test can say what `ENV` holds instead of depending on how it was launched.\n\nThe overlay is global and lives until `clear`: clear it in an `after` hook.\n\n```kex\nMock.ENV.vars({ \"HOME\": \"/fake\", \"LOG_LEVEL\": \"debug\" })\nassert(configPath() == \"/fake/.config\")\nMock.ENV.clear()\n```","types":["String","Void","Map<String, String>"],"urlPath":"mock"},{"anchor":"function-set","kind":"function","line":205,"name":"set","qualifiedName":"Mock.ENV.set","signatures":["set(name, value) : String -> String -> Void"],"summary":"Sets one variable in the overlay.","types":["String","Void"],"urlPath":"mock"},{"anchor":"function-unset","kind":"function","line":218,"name":"unset","qualifiedName":"Mock.ENV.unset","signatures":["unset(name) : String -> Void"],"summary":"Removes one variable from the overlay, so it reads as unset.\n\nSeparate from `set` because a variable being ABSENT is an answer programs act on, and there is no value that means it.","types":["String","Void"],"urlPath":"mock"},{"anchor":"function-clear","kind":"function","line":228,"name":"clear","qualifiedName":"Mock.ENV.clear","signatures":["clear() : Void"],"summary":"Removes the whole overlay, restoring the real environment.","types":[],"urlPath":"mock"},{"anchor":"function-vars","kind":"function","line":242,"name":"vars","qualifiedName":"Mock.ENV.vars","signatures":["vars(entries) : Map<String, String> -> Void"],"summary":"Declares the whole overlay in one call.\n\nThe same shape `Mock.Env { vars: ... }` takes (kexhq/kex#143). There is no `onGet` here: global `ENV` is a materialised Map, so there is nothing for a callback to intercept: use `with ENV = Mock.Env { onGet: ... }` when a rule is what you want.","types":["Map<String, String>","Void"],"urlPath":"mock"},{"anchor":"module-mock-io","kind":"module","line":271,"name":"Mock.IO","qualifiedName":"Mock.IO","signatures":["start()","stop()","output()","clear()","input(lines)"],"summary":"A stateful stand-in for the console: captures what a program prints, and feeds it lines as if they had been typed.\n\nThe way to test a program that talks to a person without one being there.\n\n```kex\nMock.IO.start()\nMock.IO.input(\"Ada\", \"42\")\ngreet()\nAssert.equal(Mock.IO.output(), \"hello, Ada\\n\")\nMock.IO.stop()\n```","types":[],"urlPath":"mock"},{"anchor":"function-start","kind":"function","line":280,"name":"start","qualifiedName":"Mock.IO.start","signatures":["start()"],"summary":"Starts capturing output and serving queued input.","types":[],"urlPath":"mock"},{"anchor":"function-stop","kind":"function","line":292,"name":"stop","qualifiedName":"Mock.IO.stop","signatures":["stop()"],"summary":"Stops capturing, and restores the real console.","types":[],"urlPath":"mock"},{"anchor":"function-output","kind":"function","line":307,"name":"output","qualifiedName":"Mock.IO.output","signatures":["output()"],"summary":"Everything the program has printed since capturing started.\n\nNewlines are included, so a single `IO.printLine(\"hi\")` gives `\"hi\\n\"`.","types":[],"urlPath":"mock"},{"anchor":"function-clear","kind":"function","line":322,"name":"clear","qualifiedName":"Mock.IO.clear","signatures":["clear()"],"summary":"Discards the captured output, while continuing to capture.\n\nUseful between phases of one test, when only the later output matters.","types":[],"urlPath":"mock"},{"anchor":"function-input","kind":"function","line":340,"name":"input","qualifiedName":"Mock.IO.input","signatures":["input(lines)"],"summary":"Queues the lines `IO.getLine` will return, in order.\n\nTakes a list, or up to four lines as separate arguments. Once they run out, `IO.getLine` answers `None`: end of input, exactly as a closed stdin would.","types":[],"urlPath":"mock"},{"anchor":"make-files","kind":"make","line":357,"name":"Files","qualifiedName":"Files","signatures":["cannedRead(path)","read(path)","readBytes(path)","readLines(path)","feed(path)","size(path)","exists?(path)","file?(path)","directory?(path)","absolute(path)","open(path, mode)","write(path, content)","writeBytes(path, content)","append(path, content)","delete(path)","copy(src, dst)","rename(src, dst)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-cannedread","kind":"function","line":360,"name":"cannedRead","qualifiedName":"Files.cannedRead","signatures":["cannedRead(path)"],"summary":"Named apart from `read`: `this.read(path)` would bind to the capability's own `read : FilePath -> String?`, not to this method.","types":[],"urlPath":"mock"},{"anchor":"fn-read","kind":"function","line":365,"name":"read","qualifiedName":"Files.read","signatures":["read(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-readbytes","kind":"function","line":369,"name":"readBytes","qualifiedName":"Files.readBytes","signatures":["readBytes(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-readlines","kind":"function","line":373,"name":"readLines","qualifiedName":"Files.readLines","signatures":["readLines(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-feed","kind":"function","line":374,"name":"feed","qualifiedName":"Files.feed","signatures":["feed(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-size","kind":"function","line":375,"name":"size","qualifiedName":"Files.size","signatures":["size(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-exists?","kind":"function","line":376,"name":"exists?","qualifiedName":"Files.exists?","signatures":["exists?(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-file?","kind":"function","line":377,"name":"file?","qualifiedName":"Files.file?","signatures":["file?(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-directory?","kind":"function","line":378,"name":"directory?","qualifiedName":"Files.directory?","signatures":["directory?(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-absolute","kind":"function","line":379,"name":"absolute","qualifiedName":"Files.absolute","signatures":["absolute(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-open","kind":"function","line":384,"name":"open","qualifiedName":"Files.open","signatures":["open(path, mode)"],"summary":"A fake is a value, so there is nowhere for a write to go. Refusing is the honest answer and the useful one: a test that did not expect a write sees it fail rather than silently succeed.","types":[],"urlPath":"mock"},{"anchor":"fn-write","kind":"function","line":385,"name":"write","qualifiedName":"Files.write","signatures":["write(path, content)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-writebytes","kind":"function","line":386,"name":"writeBytes","qualifiedName":"Files.writeBytes","signatures":["writeBytes(path, content)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-append","kind":"function","line":387,"name":"append","qualifiedName":"Files.append","signatures":["append(path, content)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-delete","kind":"function","line":388,"name":"delete","qualifiedName":"Files.delete","signatures":["delete(path)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-copy","kind":"function","line":389,"name":"copy","qualifiedName":"Files.copy","signatures":["copy(src, dst)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-rename","kind":"function","line":390,"name":"rename","qualifiedName":"Files.rename","signatures":["rename(src, dst)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"make-env","kind":"make","line":393,"name":"Env","qualifiedName":"Env","signatures":["lookup(key)","get(key)","has?(key)","keys()","values()","count()","each(f)","entries()","set(name, value)","unset(name)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-lookup","kind":"function","line":396,"name":"lookup","qualifiedName":"Env.lookup","signatures":["lookup(key)"],"summary":"Named apart from `get`: `this.get(key)` would bind to the capability's own `get`, not to this method.","types":[],"urlPath":"mock"},{"anchor":"fn-get","kind":"function","line":401,"name":"get","qualifiedName":"Env.get","signatures":["get(key)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-has?","kind":"function","line":403,"name":"has?","qualifiedName":"Env.has?","signatures":["has?(key)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-keys","kind":"function","line":404,"name":"keys","qualifiedName":"Env.keys","signatures":["keys()"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-values","kind":"function","line":405,"name":"values","qualifiedName":"Env.values","signatures":["values()"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-count","kind":"function","line":406,"name":"count","qualifiedName":"Env.count","signatures":["count()"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-each","kind":"function","line":407,"name":"each","qualifiedName":"Env.each","signatures":["each(f)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-entries","kind":"function","line":408,"name":"entries","qualifiedName":"Env.entries","signatures":["entries()"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"fn-set","kind":"function","line":416,"name":"set","qualifiedName":"Env.set","signatures":["set(name, value)"],"summary":"A write goes to `onSet` or nowhere. It must NOT reach the real environment: a substituted ENV is the whole point of the mock, and a test that set a variable would otherwise leak it into the next one and into every process the suite starts. `vars` cannot take it either: a record is immutable, so a test that cares about writes supplies the hook, and one that does not gets a write that goes quietly nowhere.","types":[],"urlPath":"mock"},{"anchor":"fn-unset","kind":"function","line":420,"name":"unset","qualifiedName":"Env.unset","signatures":["unset(name)"],"summary":"","types":[],"urlPath":"mock"},{"anchor":"module-mock-net","kind":"module","line":428,"name":"Mock.Net","qualifiedName":"Mock.Net","signatures":[],"summary":"Scriptable networking values are namespaced so importing Mock does not recreate any of the removed global HTTP types.","types":["[Any]","{String: [String]}","[Binary]"],"urlPath":"mock"},{"anchor":"module-mock-net-http","kind":"module","line":430,"name":"Mock.Net.HTTP","qualifiedName":"Mock.Net.HTTP","signatures":[],"summary":"Canned HTTP transport state for networking specifications.","types":["[Any]"],"urlPath":"mock"},{"anchor":"record-transport","kind":"record","line":432,"name":"Transport","qualifiedName":"Mock.Net.HTTP.Transport","signatures":[],"summary":"Responses returned in order by a scripted transport.","types":["[Any]"],"urlPath":"mock"},{"anchor":"module-mock-net-dns","kind":"module","line":438,"name":"Mock.Net.DNS","qualifiedName":"Mock.Net.DNS","signatures":[],"summary":"Canned DNS resolver state for networking specifications.","types":["{String: [String]}"],"urlPath":"mock"},{"anchor":"record-resolverscript","kind":"record","line":440,"name":"ResolverScript","qualifiedName":"Mock.Net.DNS.ResolverScript","signatures":[],"summary":"Hostname-to-address answers supplied without touching the network.","types":["{String: [String]}"],"urlPath":"mock"},{"anchor":"module-mock-net-socket","kind":"module","line":446,"name":"Mock.Net.Socket","qualifiedName":"Mock.Net.Socket","signatures":[],"summary":"Canned byte-stream state for socket specifications.","types":["[Binary]"],"urlPath":"mock"},{"anchor":"record-script","kind":"record","line":448,"name":"Script","qualifiedName":"Mock.Net.Socket.Script","signatures":[],"summary":"Binary chunks delivered in order as incoming socket data.","types":["[Binary]"],"urlPath":"mock"},{"anchor":"module-mock-net-websocket","kind":"module","line":454,"name":"Mock.Net.WebSocket","qualifiedName":"Mock.Net.WebSocket","signatures":[],"summary":"Canned message state for WebSocket specifications.","types":["[Any]"],"urlPath":"mock"},{"anchor":"record-script","kind":"record","line":456,"name":"Script","qualifiedName":"Mock.Net.WebSocket.Script","signatures":[],"summary":"High-level messages delivered in order by a scripted connection.","types":["[Any]"],"urlPath":"mock"},{"anchor":"module-net","kind":"module","line":7,"name":"Net","qualifiedName":"Net","signatures":["from(value) : Integer -> Result<Port, NetError>"],"summary":"Shared networking values, capability discovery, and typed failures.\n\n```kex\nusing Net\n\nlet https = Port.from(443).try\nif Support.current.tls.usable? then https.string else \"TLS unavailable\" end\n```","types":["Integer","Bool","SupportValue","Result<Port, NetError>","SupportReport","NetErrorKind","NetOperation","String","String?","Integer?"],"urlPath":"net"},{"anchor":"record-port","kind":"record","line":14,"name":"Port","qualifiedName":"Net.Port","signatures":[],"summary":"A validated TCP or UDP port number in `0..65535`.\n\nUse `Port.from` at input boundaries. Port zero requests an ephemeral port where a listening API permits it; after binding, ask the server or socket for the concrete port the operating system chose.","types":["Integer"],"urlPath":"net"},{"anchor":"record-supportvalue","kind":"record","line":24,"name":"SupportValue","qualifiedName":"Net.SupportValue","signatures":[],"summary":"Whether a feature was compiled into this backend and is usable now.\n\n`compiled?` describes the build; `usable?` also accounts for the environment it is running in. A browser build may contain an HTTP client, for example, while browser policy still prevents a particular lower-level capability.","types":["Bool"],"urlPath":"net"},{"anchor":"record-supportreport","kind":"record","line":37,"name":"SupportReport","qualifiedName":"Net.SupportReport","signatures":[],"summary":"Granular networking capabilities for the current backend and environment.\n\nRead this before choosing a transport dynamically. Applications that require one capability can instead check that single field during startup and fail with a useful message.","types":["SupportValue"],"urlPath":"net"},{"anchor":"module-net-port","kind":"module","line":50,"name":"Net.Port","qualifiedName":"Net.Port","signatures":["from(value) : Integer -> Result<Port, NetError>"],"summary":"Validated `Port` construction.","types":["Integer","Result<Port, NetError>"],"urlPath":"net"},{"anchor":"function-from","kind":"function","line":59,"name":"from","qualifiedName":"Net.Port.from","signatures":["from(value) : Integer -> Result<Port, NetError>"],"summary":"Validates a port number.","types":["Integer","Result<Port, NetError>"],"urlPath":"net"},{"anchor":"module-net-support","kind":"module","line":64,"name":"Net.Support","qualifiedName":"Net.Support","signatures":[],"summary":"Runtime discovery for optional network transports and protocols.","types":["SupportReport"],"urlPath":"net"},{"anchor":"constant-current","kind":"constant","line":73,"name":"current","qualifiedName":"Net.Support.current","signatures":[],"summary":"Reports compiled and currently usable networking features.","types":["SupportReport"],"urlPath":"net"},{"anchor":"type-netoperation","kind":"type","line":78,"name":"NetOperation","qualifiedName":"Net.NetOperation","signatures":[],"summary":"The subsystem or operation that produced a networking error.","types":[],"urlPath":"net"},{"anchor":"type-neterrorkind","kind":"type","line":81,"name":"NetErrorKind","qualifiedName":"Net.NetErrorKind","signatures":[],"summary":"Stable, backend-independent networking failure categories.","types":[],"urlPath":"net"},{"anchor":"record-neterror","kind":"record","line":89,"name":"NetError","qualifiedName":"Net.NetError","signatures":[],"summary":"A typed networking failure shared by every network module.\n\n`kind` is the stable category to branch on. `message` is for a person, while `phase` adds protocol context such as a TLS handshake and `progress` records bytes transferred before a partial-operation failure. Keeping those roles separate lets programs recover without matching backend-specific prose.","types":["NetErrorKind","NetOperation","String","String?","Integer?"],"urlPath":"net"},{"anchor":"make-port","kind":"make","line":97,"name":"Port","qualifiedName":"Port","signatures":[],"summary":"","types":[],"urlPath":"net"},{"anchor":"make-integer","kind":"make","line":15,"name":"Integer","qualifiedName":"Integer","signatures":["modulo(n) : Integer -> Integer","in?(range) : Range<Integer> -> Bool","times(block) : (Integer -> Void) -> Void"],"summary":"Whole numbers, of arbitrary size.\n\n`Integer` has no width limit: factorials and cryptographic moduli are ordinary values, not a special big-number type you have to opt into.\n\n`Integer` and `Float` are one numeric tower: they compare and order across the boundary, so `0 == 0.0` is `true` and `[1, 2.5, 3].sort` works. What differs is the arithmetic. `/` on two integers is integer division, and `sqrt` answers a `Float` because a square root generally is one.\n\n```kex\n7 / 2          # => 3      (integer division)\n7.0 / 2.0      # => 3.5\n16.sqrt        # => 4.0    (a Float, even from an Integer)\n(-7).modulo(3) # => 2      (mathematical modulo, not C remainder)\n```","types":["Integer","Range<Integer>","Bool","(Integer) -> Void","Void"],"urlPath":"number"},{"anchor":"fn-modulo","kind":"function","line":85,"name":"modulo","qualifiedName":"Integer.modulo","signatures":["modulo(n) : Integer -> Integer"],"summary":"Returns `this` modulo `n`.\n\nThe result takes the sign of `n`, which is mathematical modulo rather than the C-style remainder other languages give. That is what makes it safe for wrapping an index that may have gone negative.","types":["Integer"],"urlPath":"number"},{"anchor":"fn-in?","kind":"function","line":99,"name":"in?","qualifiedName":"Integer.in?","signatures":["in?(range) : Range<Integer> -> Bool"],"summary":"Returns `true` when the integer falls inside `range`, endpoints included.","types":["Range<Integer>","Bool"],"urlPath":"number"},{"anchor":"fn-times","kind":"function","line":115,"name":"times","qualifiedName":"Integer.times","signatures":["times(block) : (Integer -> Void) -> Void"],"summary":"Calls `f` exactly `this` times, passing the 0-based iteration index.\n\nThis is the counting loop. When you want the numbers themselves rather than a count of repetitions, `(1..n).items.each` often reads better.","types":["(Integer) -> Void","Void"],"urlPath":"number"},{"anchor":"make-float","kind":"make","line":163,"name":"Float","qualifiedName":"Float","signatures":["in?(range) : Range<Float> -> Bool"],"summary":"Double-precision floating-point numbers.\n\nA Kex `Float` is always finite. An operation that would produce `NaN` or `Infinity` raises instead: the same rule the BEAM enforces, where those two values cannot exist at all. So a `Float` you are holding is always a real number, and there is no `nan?` to check for.\n\n`Float` and `Integer` compare and order across the boundary; see `Integer` for the rest of the numeric tower.\n\n```kex\n3.7.floor      # => 3\n3.7.round      # => 4\n(-3.7).toInteger  # => -3   (truncates toward zero)\n```","types":["Range<Float>","Bool"],"urlPath":"number"},{"anchor":"fn-in?","kind":"function","line":196,"name":"in?","qualifiedName":"Float.in?","signatures":["in?(range) : Range<Float> -> Bool"],"summary":"Returns `true` when the float falls inside `range`, endpoints included.","types":["Range<Float>","Bool"],"urlPath":"number"},{"anchor":"module-integer","kind":"module","line":257,"name":"Integer","qualifiedName":"Integer","signatures":["parse(s) : String -> Result<Integer, ParseError>","parse(s) : String -> Integer -> Result<Integer, ParseError>","parsePrefix(s) : String -> (Integer, String)?"],"summary":"Reading integers out of text.\n\nUse `parse` when a failure needs explaining and `\"42\".to(Integer)` when it does not: `to` answers a plain `Optional`, `parse` answers a `Result` carrying a `ParseError` that says where it stopped and what it had read so far.","types":["String","Result<Integer, ParseError>","(Integer) -> Result<Integer, ParseError>","(Integer, String)?"],"urlPath":"number"},{"anchor":"function-parse","kind":"function","line":280,"name":"parse","qualifiedName":"Integer.parse","signatures":["parse(s) : String -> Result<Integer, ParseError>","parse(s) : String -> Integer -> Result<Integer, ParseError>"],"summary":"Parses the whole string as a base-10 integer.\n\nThe string must be entirely consumed: leading or trailing characters make it an `Error`, with a `ParseError` describing where parsing stopped. Use `parsePrefix` when a trailing remainder is expected.","types":["String","Result<Integer, ParseError>","(Integer) -> Result<Integer, ParseError>"],"urlPath":"number"},{"anchor":"function-parseprefix","kind":"function","line":322,"name":"parsePrefix","qualifiedName":"Integer.parsePrefix","signatures":["parsePrefix(s) : String -> (Integer, String)?"],"summary":"Parses an integer from the front of the string and returns it together with whatever text was left over.\n\nThis is the building block for hand-written scanners: parse a number, keep going from the remainder. Answers `None` when the string does not begin with a number at all.","types":["String","(Integer, String)?"],"urlPath":"number"},{"anchor":"module-float","kind":"module","line":327,"name":"Float","qualifiedName":"Float","signatures":["parse(s) : String -> Result<Float, ParseError>","parsePrefix(s) : String -> (Float, String)?"],"summary":"Reading floating-point numbers out of text.","types":["String","Result<Float, ParseError>","(Float, String)?"],"urlPath":"number"},{"anchor":"function-parse","kind":"function","line":343,"name":"parse","qualifiedName":"Float.parse","signatures":["parse(s) : String -> Result<Float, ParseError>"],"summary":"Parses the whole string as a float.\n\nThe string must be entirely consumed; anything left over makes it an `Error` carrying a `ParseError`.","types":["String","Result<Float, ParseError>"],"urlPath":"number"},{"anchor":"function-parseprefix","kind":"function","line":359,"name":"parsePrefix","qualifiedName":"Float.parsePrefix","signatures":["parsePrefix(s) : String -> (Float, String)?"],"summary":"Parses a float from the front of the string and returns it together with the unconsumed remainder. Answers `None` when the string does not begin with a number.","types":["String","(Float, String)?"],"urlPath":"number"},{"anchor":"module-number","kind":"module","line":365,"name":"Number","qualifiedName":"Number","signatures":["parse(s) : String -> Result<Number, ParseError>"],"summary":"Reading a number out of text without deciding in advance which half of the numeric tower it belongs to.","types":["String","Result<Number, ParseError>"],"urlPath":"number"},{"anchor":"function-parse","kind":"function","line":379,"name":"parse","qualifiedName":"Number.parse","signatures":["parse(s) : String -> Result<Number, ParseError>"],"summary":"Parses the whole string as an `Integer` or a `Float`, whichever the text describes.\n\nUse it when the input's shape is not known ahead of time: a config value, a CSV column that may hold either.","types":["String","Result<Number, ParseError>"],"urlPath":"number"},{"anchor":"type-optional","kind":"type","line":20,"name":"Optional","qualifiedName":"Optional","signatures":[],"summary":"An optional value: `Just(x)` carries a value, `None` says there is none.\n\n`X?` is shorthand for `Optional<X>`, and is the spelling you will normally write. Kex has no `null`: anything that might not produce a value returns an `Optional` instead, so the compiler makes you say what happens when it is empty. Most of the time that is a single `.or(default)` at the end of a chain.\n\n```kex\nlet names = [\"ada\", \"grace\"]\nnames.first.or(\"nobody\")        # => \"ada\"\nnames.at(9).or(\"nobody\")        # => \"nobody\"\nnames.first.map(~upperCase)        # => Just(\"ADA\")\n```\n\nPattern matching handles the cases that need more than a default:\n\n```kex\nmatch config.get(\"port\") do\n  Just(port) => IO.printLine(\"listening on ${port}\")\n  None       => IO.printLine(\"no port configured\")\nend\n```","types":["X"],"urlPath":"optional"},{"anchor":"type-result","kind":"type","line":36,"name":"Result","qualifiedName":"Result","signatures":[],"summary":"The outcome of an operation that can fail: `Ok(x)` on success, `Error(e)` on failure with a reason.\n\nUse `Result` over `Optional` when the *reason* for failure matters to the caller. Parsing is the standard example: `\"12x\".to(Integer)` answers `None`, while `Integer.parse(\"12x\")` answers an `Error` that says where it stopped.\n\n```kex\nInteger.parse(\"42\").or(0)     # => 42\nInteger.parse(\"4x\").or(0)     # => 0\n\nmatch Integer.parse(input) do\n  Ok(n)    => IO.printLine(\"got ${n}\")\n  Error(e) => IO.printError(\"bad number: ${e}\")\nend\n```","types":["X","E"],"urlPath":"optional"},{"anchor":"type-either","kind":"type","line":51,"name":"Either","qualifiedName":"Either","signatures":[],"summary":"One of two values, of possibly different types: `Left(l)` or `Right(r)`.\n\nUnlike `Result`, neither side means failure: `Either` is for a value that is legitimately one of two shapes.\n\n```kex\ntype Id = Either<Integer, String>\n\nlet describe(id: Id) -> String do\n  match id do\n    Left(n)  => \"numeric id ${n}\"\n    Right(s) => \"slug id ${s}\"\n  end\nend\n```","types":["L","R"],"urlPath":"optional"},{"anchor":"trait-optionable","kind":"trait","line":55,"name":"Optionable","qualifiedName":"Optionable","signatures":[],"summary":"Marker trait for `Optional`. Constrain a generic parameter with it when a function accepts any optional value.","types":[],"urlPath":"optional"},{"anchor":"trait-resultable","kind":"trait","line":60,"name":"Resultable","qualifiedName":"Resultable","signatures":[],"summary":"Marker trait for `Result`. Constrain a generic parameter with it when a function accepts any result value.","types":[],"urlPath":"optional"},{"anchor":"trait-eitherable","kind":"trait","line":65,"name":"Eitherable","qualifiedName":"Eitherable","signatures":[],"summary":"Marker trait for `Either`. Constrain a generic parameter with it when a function accepts any either value.","types":[],"urlPath":"optional"},{"anchor":"make-optional<x>","kind":"make","line":68,"name":"Optional<X>","qualifiedName":"Optional<X>","signatures":["set? : Bool","none? : Bool","or : X -> X","map : (X -> Y) -> Y?","flatMap : (X -> Y?) -> Y?"],"summary":"","types":["Bool","(X) -> X","((X) -> Y) -> Y?","((X) -> Y?) -> Y?"],"urlPath":"optional"},{"anchor":"fn-set?","kind":"function","line":82,"name":"set?","qualifiedName":"Optional<X>.set?","signatures":["set? : Bool"],"summary":"Returns `true` when a value is present.","types":["Bool"],"urlPath":"optional"},{"anchor":"fn-none?","kind":"function","line":98,"name":"none?","qualifiedName":"Optional<X>.none?","signatures":["none? : Bool"],"summary":"Returns `true` when there is no value. The opposite of `set?`.","types":["Bool"],"urlPath":"optional"},{"anchor":"fn-or","kind":"function","line":118,"name":"or","qualifiedName":"Optional<X>.or","signatures":["or : X -> X"],"summary":"Returns the wrapped value, or `default` when there is none.\n\nThis is the usual way an optional leaves the optional world: put `.or` at the end of a chain and the rest of your code works with a plain value.","types":["(X) -> X"],"urlPath":"optional"},{"anchor":"fn-map","kind":"function","line":135,"name":"map","qualifiedName":"Optional<X>.map","signatures":["map : (X -> Y) -> Y?"],"summary":"Applies `f` to the wrapped value, keeping the result wrapped. `None` is returned unchanged, so `f` never sees a missing value.","types":["((X) -> Y) -> Y?"],"urlPath":"optional"},{"anchor":"fn-flatmap","kind":"function","line":158,"name":"flatMap","qualifiedName":"Optional<X>.flatMap","signatures":["flatMap : (X -> Y?) -> Y?"],"summary":"Applies `f`, which itself returns an optional, and flattens the result.\n\nUse it instead of `map` when the step can also fail: `map` would give you a doubly wrapped `Just(Just(x))`, `flatMap` gives a single layer. A `None` anywhere in the chain short-circuits the rest.","types":["((X) -> Y?) -> Y?"],"urlPath":"optional"},{"anchor":"make-result<x,-e>","kind":"make","line":163,"name":"Result<X, E>","qualifiedName":"Result<X, E>","signatures":["ok? : Bool","error? : Bool","or : X -> X","map : (X -> Y) -> Result<Y, E>","flatMap : (X -> Result<Y, E>) -> Result<Y, E>","optional : X?"],"summary":"","types":["Bool","(X) -> X","((X) -> Y) -> Result<Y, E>","((X) -> Result<Y, E>) -> Result<Y, E>","X?"],"urlPath":"optional"},{"anchor":"fn-ok?","kind":"function","line":174,"name":"ok?","qualifiedName":"Result<X, E>.ok?","signatures":["ok? : Bool"],"summary":"Returns `true` when the result is `Ok`.","types":["Bool"],"urlPath":"optional"},{"anchor":"fn-error?","kind":"function","line":185,"name":"error?","qualifiedName":"Result<X, E>.error?","signatures":["error? : Bool"],"summary":"Returns `true` when the result is `Error`. The opposite of `ok?`.","types":["Bool"],"urlPath":"optional"},{"anchor":"fn-or","kind":"function","line":203,"name":"or","qualifiedName":"Result<X, E>.or","signatures":["or : X -> X"],"summary":"Returns the `Ok` value, or `default` when the result is an `Error`.\n\nThe error payload is discarded. Match on the result instead when you need to report why it failed.","types":["(X) -> X"],"urlPath":"optional"},{"anchor":"fn-map","kind":"function","line":219,"name":"map","qualifiedName":"Result<X, E>.map","signatures":["map : (X -> Y) -> Result<Y, E>"],"summary":"Applies `f` to the `Ok` value. An `Error` passes through untouched, so a chain of `map` calls describes the success path only.","types":["((X) -> Y) -> Result<Y, E>"],"urlPath":"optional"},{"anchor":"fn-flatmap","kind":"function","line":239,"name":"flatMap","qualifiedName":"Result<X, E>.flatMap","signatures":["flatMap : (X -> Result<Y, E>) -> Result<Y, E>"],"summary":"Applies `f`, which itself returns a `Result`, and flattens the result.\n\nThe step-by-step form of `map` for stages that can fail on their own. The first `Error` ends the chain and is the answer.","types":["((X) -> Result<Y, E>) -> Result<Y, E>"],"urlPath":"optional"},{"anchor":"fn-optional","kind":"function","line":259,"name":"optional","qualifiedName":"Result<X, E>.optional","signatures":["optional : X?"],"summary":"Converts the result to an `Optional`, dropping the error payload.\n\nUseful when a caller only needs to know whether there is a value, and everything downstream already speaks `Optional`.","types":["X?"],"urlPath":"optional"},{"anchor":"function-or","kind":"function","line":277,"name":"or","qualifiedName":"or","signatures":["or(value, _)"],"summary":"Returns the value unchanged.\n\nThe catch-all clause of `or`: a value that is neither an `Optional` nor a `Result` has already succeeded, so there is nothing to fall back to. This is what lets `.or(default)` be written after a call whose return type may later stop being optional, without the call site changing.","types":[],"urlPath":"optional"},{"anchor":"function-to","kind":"function","line":303,"name":"to","qualifiedName":"to","signatures":["to(value, String)"],"summary":"Converts `value` to the type `t`, or `None` if it cannot be represented.\n\n`t` is a runtime type value: write the type name itself: `String`, `Integer`, `Float`, `List`. Conversion to `String` goes through the `Showable` protocol, so it works for every value.\n\nThe result is an `Optional`, not a `Result`, and deliberately so: the reason a conversion failed is usually implied by the types alone. When the reason carries information (where a parse gave up and on what) reach for `Integer.parse` or `Float.parse`, which answer with `Result<_, ParseError>`. So: `to` for every day, `parse` when the failure needs handling.","types":[],"urlPath":"optional"},{"anchor":"make-either<l,-r>","kind":"make","line":326,"name":"Either<L, R>","qualifiedName":"Either<L, R>","signatures":[],"summary":"","types":[],"urlPath":"optional"},{"anchor":"type-optionkind","kind":"type","line":33,"name":"OptionKind","qualifiedName":"OptionKind","signatures":[],"summary":"Declarative command-line parsing, shared by Kex tools and applications.\n\nYou describe the options and commands a tool accepts, and the parser turns an argument list into typed values, dispatches to the right command, and renders the help text.\n\n```kex\nfoul greet(options: ParsedOptions) -> Integer do\n  IO.printLine(\"hello, ${options.value(\"name\", \"world\")}\")\n  return 0\nend\n\nmain(args) do\n  let cli = OptionParser.define(\"demo\", \"a small demo tool\")\n    .string(\"name\", Just('n'), \"who to greet\", Just(\"world\"), false)\n    .flag(\"help\", Just('h'), \"show this help\")\n    .command(\"greet\", \"\", \"print a greeting\", ~greet)\n  System.exit(cli.run(args))\nend\n\n$ demo greet --name Ada\nhello, Ada\n```\n\nNote that `--help` is not automatic: `run` shows the help when a flag named `help` is set, so declare that flag if you want it.\n\nStart at `OptionParser.define`, chain the `string` / `integer` / `flag` / `command` builders, and finish with `run` (dispatch and exit code) or `parse` (the parsed values, and nothing else).\n\nWhat kind of value an option carries: `StringValue` takes any text, `IntegerValue` must parse as a number, and `FlagValue` takes none at all and reads as `\"true\"` when present.","types":[],"urlPath":"optionparser"},{"anchor":"record-optionspec","kind":"record","line":37,"name":"OptionSpec","qualifiedName":"OptionSpec","signatures":[],"summary":"One declared option. Built for you by the `OptionConfig` builders: you rarely construct one by hand.","types":["String","Char?","OptionKind","String?","Bool"],"urlPath":"optionparser"},{"anchor":"type-commandhandler","kind":"type","line":60,"name":"CommandHandler","qualifiedName":"CommandHandler","signatures":[],"summary":"What a command does once the line has been parsed. It receives the parsed options with the command's own words already removed, so `tey add greet` hands its handler `[\"greet\"]`, and returns the process exit code.","types":[],"urlPath":"optionparser"},{"anchor":"record-commandspec","kind":"record","line":63,"name":"CommandSpec","qualifiedName":"CommandSpec","signatures":[],"summary":"One declared command. Built for you by the `OptionConfig.command` builders.","types":["String","CommandHandler"],"urlPath":"optionparser"},{"anchor":"record-optionconfig","kind":"record","line":92,"name":"OptionConfig","qualifiedName":"OptionConfig","signatures":[],"summary":"A tool's whole command-line interface: its name, its options, and its commands.\n\nBuild one with `OptionParser.define` and add to it with the chainable `string`, `integer`, `flag` and `command` methods. Each returns a new `OptionConfig`, so the chain reads as one declaration.","types":["String","[OptionSpec]","[CommandSpec]"],"urlPath":"optionparser"},{"anchor":"record-parsedoptions","kind":"record","line":108,"name":"ParsedOptions","qualifiedName":"ParsedOptions","signatures":[],"summary":"The result of a successful parse: the option values, and the words that were not options.","types":["{String: String}","[String]"],"urlPath":"optionparser"},{"anchor":"type-optionparseerror","kind":"type","line":122,"name":"OptionParseError","qualifiedName":"OptionParseError","signatures":[],"summary":"Why a command line could not be parsed.\n\n`OptionParser.errorMessage` turns one into a sentence for the user; `OptionConfig.run` does that for you.","types":["String","String","String","String","String","String"],"urlPath":"optionparser"},{"anchor":"make-parsedoptions","kind":"make","line":128,"name":"ParsedOptions","qualifiedName":"ParsedOptions","signatures":["value(name)","flagEnabled?(name)","integerValue(name)"],"summary":"","types":[],"urlPath":"optionparser"},{"anchor":"fn-value","kind":"function","line":138,"name":"value","qualifiedName":"ParsedOptions.value","signatures":["value(name)"],"summary":"Returns the value given for the option named `name`, or `None` when it was neither supplied nor defaulted.","types":[],"urlPath":"optionparser"},{"anchor":"fn-flagenabled?","kind":"function","line":160,"name":"flagEnabled?","qualifiedName":"ParsedOptions.flagEnabled?","signatures":["flagEnabled?(name)"],"summary":"Returns `true` when the flag named `name` was given.","types":[],"urlPath":"optionparser"},{"anchor":"fn-integervalue","kind":"function","line":173,"name":"integerValue","qualifiedName":"ParsedOptions.integerValue","signatures":["integerValue(name)"],"summary":"Returns the value of an integer option as an `Integer`, or `None` when it is absent or does not parse.\n\nAn option declared with `integer` has already been checked at parse time, so `None` here means absent rather than malformed.","types":[],"urlPath":"optionparser"},{"anchor":"make-optionconfig","kind":"make","line":184,"name":"OptionConfig","qualifiedName":"OptionConfig","signatures":["string(long, short, description, default, required?)","integer(long, short, description, default, required?)","flag(long, short, description)","command(name, description, handler)","parse(args)","run(args)","printHelp()"],"summary":"","types":[],"urlPath":"optionparser"},{"anchor":"fn-string","kind":"function","line":200,"name":"string","qualifiedName":"OptionConfig.string","signatures":["string(long, short, description, default, required?)"],"summary":"Declares an option that takes a text value.","types":[],"urlPath":"optionparser"},{"anchor":"fn-integer","kind":"function","line":223,"name":"integer","qualifiedName":"OptionConfig.integer","signatures":["integer(long, short, description, default, required?)"],"summary":"Declares an option that takes a whole number.\n\nA value that does not parse is rejected at parse time with `InvalidInteger`, so a handler never sees a malformed number. Read it back with `ParsedOptions.integerValue`.","types":[],"urlPath":"optionparser"},{"anchor":"fn-flag","kind":"function","line":249,"name":"flag","qualifiedName":"OptionConfig.flag","signatures":["flag(long, short, description)"],"summary":"Declares an option that takes no value and is either present or not.\n\nIt defaults to `\"false\"` and reads as `\"true\"` when given. Read it back with `ParsedOptions.flagEnabled?`. Giving it a value is an error (`UnexpectedValue`).\n\nDeclare a flag named `help` if you want `--help` to print the help text: `run` looks for exactly that name, and adds nothing on its own.","types":[],"urlPath":"optionparser"},{"anchor":"fn-command","kind":"function","line":276,"name":"command","qualifiedName":"OptionConfig.command","signatures":["command(name, description, handler)"],"summary":"Declares a command and the function that runs it.\n\nA command name may be several words (`\"docs build\"`) and the longest match wins, so a group and its subcommands can both be declared. The handler receives the parsed options with the command's own words already removed, and returns the process exit code.\n\nDeclares a command. `usage` is what the help line shows after the name (`<name>`, `[args...]`); leave it empty for a command that takes none.","types":[],"urlPath":"optionparser"},{"anchor":"fn-parse","kind":"function","line":341,"name":"parse","qualifiedName":"OptionConfig.parse","signatures":["parse(args)"],"summary":"Parses `args` into option values and leftover words, without dispatching to a command.\n\nUse it when the tool has no commands, or when you want to inspect the parse before deciding what to do. `run` is the one-call alternative.","types":[],"urlPath":"optionparser"},{"anchor":"fn-run","kind":"function","line":365,"name":"run","qualifiedName":"OptionConfig.run","signatures":["run(args)"],"summary":"Parses `args`, runs the command they name, and returns its exit code.\n\nThis is the whole of a tool's `main`: hand it the argument list and pass the result to `System.exit`.\n\nA parse failure, an unknown command, or a line with no command at all reports the problem along with the help text and returns 1: the one place that policy has to live for every tool to behave the same way. A set `help` flag prints the help and returns 0.","types":[],"urlPath":"optionparser"},{"anchor":"fn-printhelp","kind":"function","line":400,"name":"printHelp","qualifiedName":"OptionConfig.printHelp","signatures":["printHelp()"],"summary":"Prints the help text and returns 0, the exit code for a successful run.\n\n`run` calls this for you when the `help` flag is set; call it directly when a tool decides on its own that help is the right answer.","types":[],"urlPath":"optionparser"},{"anchor":"module-optionparser","kind":"module","line":465,"name":"OptionParser","qualifiedName":"OptionParser","signatures":["define(name, description)","commandLabel(command)","commandFor(commands, arguments)","opensWith?(arguments, name)","errorMessage(error)","parse(options, commands, args)"],"summary":"","types":[],"urlPath":"optionparser"},{"anchor":"function-define","kind":"function","line":480,"name":"define","qualifiedName":"OptionParser.define","signatures":["define(name, description)"],"summary":"Starts an immutable command-line interface definition.\n\nChain options and commands onto the returned `OptionConfig`, then call `parse` with the process arguments. `name` appears in usage text and `description` introduces the generated help page.","types":[],"urlPath":"optionparser"},{"anchor":"function-commandlabel","kind":"function","line":492,"name":"commandLabel","qualifiedName":"OptionParser.commandLabel","signatures":["commandLabel(command)"],"summary":"Returns the label a command is listed under in the help text: its name, followed by its usage when it has one.","types":[],"urlPath":"optionparser"},{"anchor":"function-commandfor","kind":"function","line":510,"name":"commandFor","qualifiedName":"OptionParser.commandFor","signatures":["commandFor(commands, arguments)"],"summary":"Finds the declared command whose words open `arguments`, and returns it with whatever is left after them.\n\nLongest match first, so `\"kex install\"` is preferred over a `\"kex\"` that also exists. Answers `None` when no command matches.","types":[],"urlPath":"optionparser"},{"anchor":"function-openswith?","kind":"function","line":534,"name":"opensWith?","qualifiedName":"OptionParser.opensWith?","signatures":["opensWith?(arguments, name)"],"summary":"Returns `true` when `arguments` begins with the words of `name`.\n\nThe word-wise prefix test `commandFor` matches with: `\"docs build\"` opens `[\"docs\", \"build\", \"src\"]` but not `[\"docs\"]`.","types":[],"urlPath":"optionparser"},{"anchor":"function-errormessage","kind":"function","line":553,"name":"errorMessage","qualifiedName":"OptionParser.errorMessage","signatures":["errorMessage(error)"],"summary":"Renders a parse error as a sentence for the user.\n\n`OptionConfig.run` does this for you; call it directly when handling a `parse` result yourself.","types":[],"urlPath":"optionparser"},{"anchor":"function-parse","kind":"function","line":570,"name":"parse","qualifiedName":"OptionParser.parse","signatures":["parse(options, commands, args)"],"summary":"Parses `args` against a list of option and command specs.\n\nThe engine behind `OptionConfig.parse`, which is the form to call from ordinary code.","types":[],"urlPath":"optionparser"},{"anchor":"module-parsing","kind":"module","line":24,"name":"Parsing","qualifiedName":"Parsing","signatures":["peekAt(offset)","advanceBy(count)","charWhen(pred)","char(expected)","many(f)","some(f)","string(expected)","takeWhile(pred)","choice(alts)"],"summary":"Parser combinators, for reading a text format you define yourself.\n\nOpt-in: nothing here is in scope until `using Parsing`.\n\nAn `Input` is an immutable cursor over a string. A parser takes one and answers either the value it read together with an advanced cursor, or a `ParseError` saying where it gave up. Because the cursor is immutable, a failed attempt costs nothing: the caller still holds the position it started from and can try something else.\n\n```kex\nusing Parsing\n\nmain do\n  let cursor = Input { input: \"abc 123\" }\n  let word = cursor.takeWhile(~alpha?)\n  let (text, rest) = word\n  IO.printLine(text)              # prints: abc\n  IO.printLine(rest.pos)          # prints: 3\nend\n```\n\nThe building blocks are `char` and `charWhen` for one character, `string` for a literal, `takeWhile` for a run, and `many` / `some` / `choice` for repetition and alternatives. `JSON` in this same stdlib is written with them.","types":["String","Integer"],"urlPath":"parsing"},{"anchor":"type-parseerror","kind":"type","line":34,"name":"ParseError","qualifiedName":"Parsing.ParseError","signatures":[],"summary":"Why a parser gave up, and at which position.\n\n`Expected` carries what the grammar WANTED rather than what it found: the difference between \"unexpected `d`\" and \"expected `version(`\". It is what `label` and `string` report.\n\n```kex\nInput { input: \"abc\" }.char('z')      # => Error(Unexpected(\"a\", 0))\nInput { input: \"abc\" }.string(\"abd\")  # => Error(Expected(\"abd\", 0))\n```","types":["String","Integer","String","Integer","Integer"],"urlPath":"parsing"},{"anchor":"record-input","kind":"record","line":43,"name":"Input","qualifiedName":"Parsing.Input","signatures":[],"summary":"An immutable cursor over a string: the text, and where in it you are.\n\nCreate one with `Input { input: text }` and pass it to a parser. Every operation that consumes input answers a NEW cursor rather than moving this one, which is what makes backtracking free.","types":["String","Integer"],"urlPath":"parsing"},{"anchor":"make-input","kind":"make","line":51,"name":"Input","qualifiedName":"Input","signatures":["peekAt(offset)","advanceBy(count)","charWhen(pred)","char(expected)","many(f)","some(f)","string(expected)","takeWhile(pred)","choice(alts)"],"summary":"","types":[],"urlPath":"parsing"},{"anchor":"fn-peekat","kind":"function","line":75,"name":"peekAt","qualifiedName":"Input.peekAt","signatures":["peekAt(offset)"],"summary":"The character `offset` positions ahead of the cursor, or `None` when that is outside the input.\n\nThe lookahead a grammar needs when one character is not enough to decide. A negative offset looks backwards.","types":[],"urlPath":"parsing"},{"anchor":"fn-advanceby","kind":"function","line":108,"name":"advanceBy","qualifiedName":"Input.advanceBy","signatures":["advanceBy(count)"],"summary":"A cursor `count` characters further on.","types":[],"urlPath":"parsing"},{"anchor":"fn-charwhen","kind":"function","line":135,"name":"charWhen","qualifiedName":"Input.charWhen","signatures":["charWhen(pred)"],"summary":"Reads one character, if it satisfies `pred`.\n\nAnswers the character and the advanced cursor, or an `Unexpected` error naming what was there instead: `\"EOF\"` at the end of the input.","types":[],"urlPath":"parsing"},{"anchor":"fn-char","kind":"function","line":154,"name":"char","qualifiedName":"Input.char","signatures":["char(expected)"],"summary":"Reads one specific character.","types":[],"urlPath":"parsing"},{"anchor":"fn-many","kind":"function","line":197,"name":"many","qualifiedName":"Input.many","signatures":["many(f)"],"summary":"Applies `f` as many times as it succeeds, collecting the results.\n\nCannot fail: zero matches is an empty list, which is what makes it right for the optional parts of a grammar. A parser that succeeds without consuming anything stops the loop rather than spinning forever.","types":[],"urlPath":"parsing"},{"anchor":"fn-some","kind":"function","line":224,"name":"some","qualifiedName":"Input.some","signatures":["some(f)"],"summary":"Applies `f` at least once, then as many more times as it succeeds.\n\nThe one-or-more counterpart of `many`: the first failure IS a failure, so use it where the grammar requires something to be there.","types":[],"urlPath":"parsing"},{"anchor":"fn-string","kind":"function","line":246,"name":"string","qualifiedName":"Input.string","signatures":["string(expected)"],"summary":"Reads an exact literal.\n\nA keyword grammar is mostly literals: `version(` is one token to a reader and eight calls to `char`, and matching it here reports the failure at the START of the literal, which is where a person looking at the error expects the caret.","types":[],"urlPath":"parsing"},{"anchor":"fn-takewhile","kind":"function","line":273,"name":"takeWhile","qualifiedName":"Input.takeWhile","signatures":["takeWhile(pred)"],"summary":"Reads every character while `pred` holds, as a `String`.\n\n`many(charWhen(...))` gives a [Char] the caller has to join, and a run of characters is almost always wanted as text. Cannot fail: an empty run is an empty String, which is what makes it safe for the optional parts of a grammar.","types":[],"urlPath":"parsing"},{"anchor":"fn-choice","kind":"function","line":301,"name":"choice","qualifiedName":"Input.choice","signatures":["choice(alts)"],"summary":"Tries each parser in `alts` in turn, and answers the first that succeeds.\n\nThis is alternation: how a grammar says \"a value is a string, or a number, or an object\". Because the cursor is immutable, a failed alternative costs nothing. When none of them match, the answer is `NoMatch` at the position they all started from.","types":[],"urlPath":"parsing"},{"anchor":"type-pid","kind":"type","line":41,"name":"Pid","qualifiedName":"Pid","signatures":[],"summary":"Processes, tasks and external commands.\n\nKex's concurrency is the BEAM's: lightweight processes that share nothing and communicate by message. There are three things here, and they answer different questions.\n\n`Task` runs one piece of work somewhere else and gives you the answer back:\n\n```kex\nlet a = Task.start do expensiveThing(1) end\nlet b = Task.start do expensiveThing(2) end\na.await ` b.await\n```\n\n`serving` plus `Process.spawn` gives a piece of state its own process, with typed calls into it:\n\n```kex\nrecord RateLimiter do\n  remaining : Integer\nend\n\nserving RateLimiter do\n  slot allowed? -> Reply<Bool> do\n    let allowed = @remaining > 0\n    new.remaining = allowed then @remaining - 1 else @remaining\n    return { new, reply: allowed }\n  end\nend\n\nlet api = Process.spawn(RateLimiter { remaining: 2 })\napi.allowed?()   # => Ok(true)\n```\n\n`Process.run` and `Process.stream` run an external program.\n\n```kex\nProcess.run(\"git\", [\"rev-parse\", \"HEAD\"])\n```\n\nBacked by Kex.Intrinsic.Process and the BEAM runtime.\n\nAn opaque BEAM process identifier.\n\nObtained from `Process.self` or `Process.whereis+. Send it messages, link to it, monitor it, or ask whether it is still alive.","types":[],"urlPath":"process"},{"anchor":"type-task","kind":"type","line":46,"name":"Task","qualifiedName":"Task","signatures":[],"summary":"A handle on work running in another process, with a result you can await.\n\nCreated by `Task.start`.","types":[],"urlPath":"process"},{"anchor":"type-reference","kind":"type","line":49,"name":"Reference","qualifiedName":"Reference","signatures":[],"summary":"A monitor reference, returned by `monitor` and passed back to `demonitor`.","types":[],"urlPath":"process"},{"anchor":"type-process","kind":"type","line":53,"name":"Process","qualifiedName":"Process","signatures":[],"summary":"A typed process handle: like a `Pid`, but it remembers what kind of message the process accepts.","types":[],"urlPath":"process"},{"anchor":"type-processexitreason","kind":"type","line":58,"name":"ProcessExitReason","qualifiedName":"ProcessExitReason","signatures":[],"summary":"A process termination reason. This remains open because the BEAM permits any term as an exit reason; conventional values include `:normal`, `:shutdown`, and structured application errors.","types":[],"urlPath":"process"},{"anchor":"record-reply","kind":"record","line":76,"name":"Reply","qualifiedName":"Reply","signatures":[],"summary":"What a synchronous `slot` returns: an answer for the caller, and optionally a new state and a reason to stop.\n\n`reply` answers the caller immediately. A slot may omit it only when it sends a deferred response with `from.reply(...)`. `new` installs the next serving state; omitting it preserves the current state. `stop` terminates the server after applying the transition. Within a `serving X` block, the checker narrows `new` from `Any?` to `X?`.\n\n```kex\nslot allowed? -> Reply<Bool> do\n  let allowed = @remaining > 0\n  new.remaining = allowed then @remaining - 1 else @remaining\n  return { new, reply: allowed }\nend\n\nslot stop -> Reply<Integer> = { stop: :normal, reply: @remaining }\n```","types":["A","Any?","ProcessExitReason?"],"urlPath":"process"},{"anchor":"type-from","kind":"type","line":90,"name":"From","qualifiedName":"From","signatures":[],"summary":"The identity of a pending caller, for a slot that answers later rather than immediately. Hand it `reply` when the answer is ready.","types":[],"urlPath":"process"},{"anchor":"type-callerror","kind":"type","line":94,"name":"CallError","qualifiedName":"CallError","signatures":[],"summary":"Why a call into a server failed: it took too long, the process is gone, or it crashed handling the call.","types":[],"urlPath":"process"},{"anchor":"record-server","kind":"record","line":100,"name":"Server","qualifiedName":"Server","signatures":[],"summary":"A running server and the default timeout for calls into it.\n\nReturned by `Process.spawn`. Every `slot` declared in the type's `serving` block becomes a method on it, answering a `Result`.","types":["Process<X>","Integer"],"urlPath":"process"},{"anchor":"module-process","kind":"module","line":111,"name":"Process","qualifiedName":"Process","signatures":["spawn(state) : X -> Server<X>","run(command, args) : String -> [String] -> Result<ProcessResult, String>","stream(command, args) : String -> [String] -> Result<Integer, String>","exec(command, args) : String -> [String] -> Integer","self() : Pid","exit(pid, reason) : Pid -> X -> Void","register(pid, name) : Pid -> Atom -> Void","whereis(name) : Atom -> Pid?"],"summary":"Spawning servers, running external commands, and the ambient process operations.","types":["X","Server<X>","String","[String]","Result<ProcessResult, String>","Result<Integer, String>","Integer","Pid","Void","Atom","Pid?"],"urlPath":"process"},{"anchor":"function-spawn","kind":"function","line":128,"name":"spawn","qualifiedName":"Process.spawn","signatures":["spawn(state) : X -> Server<X>"],"summary":"Starts a process running the `serving` implementation attached to `state`'s type, and returns a handle on it.\n\nThe state you pass is the server's initial state. Every `slot` in the `serving` block becomes a method on the returned `Server`, and each answers a `Result`: a call can time out or find the process gone.","types":["X","Server<X>"],"urlPath":"process"},{"anchor":"function-run","kind":"function","line":161,"name":"run","qualifiedName":"Process.run","signatures":["run(command, args) : String -> [String] -> Result<ProcessResult, String>"],"summary":"Runs an executable with an argument vector and captures its output.\n\nNo shell is involved, so nothing is glob-expanded or word-split and arguments containing spaces need no quoting. Output is captured as UTF-8 strings.\n\nA non-zero exit status is still `Ok`: the program ran and said something, which is information, not a failure to run it. `Error` means the child could not be started at all.","types":["String","[String]","Result<ProcessResult, String>"],"urlPath":"process"},{"anchor":"function-stream","kind":"function","line":182,"name":"stream","qualifiedName":"Process.stream","signatures":["stream(command, args) : String -> [String] -> Result<Integer, String>"],"summary":"Runs an executable with the CALLER's stdout and stderr, so its output appears as it is produced rather than in one block when it exits.\n\nAnswers the exit code; nothing is captured: that is the trade, and it is what a long-running child a person is watching needs (kexhq/kex#187).\n\n`run` remains the one to use when the output is data to be READ.","types":["String","[String]","Result<Integer, String>"],"urlPath":"process"},{"anchor":"function-exec","kind":"function","line":202,"name":"exec","qualifiedName":"Process.exec","signatures":["exec(command, args) : String -> [String] -> Integer"],"summary":"Runs an executable and returns just its exit code, discarding its output.\n\nA command that could not be started answers `127`, the shell's convention for \"command not found\". Use `run` when you need the output or want to tell a failed start from a failed run.","types":["String","[String]","Integer"],"urlPath":"process"},{"anchor":"function-self","kind":"function","line":214,"name":"self","qualifiedName":"Process.self","signatures":["self() : Pid"],"summary":"Returns the calling process's own `Pid`.","types":[],"urlPath":"process"},{"anchor":"function-exit","kind":"function","line":227,"name":"exit","qualifiedName":"Process.exit","signatures":["exit(pid, reason) : Pid -> X -> Void"],"summary":"Sends an exit signal carrying `reason` to `pid`.\n\n`:normal` is the ordinary shutdown reason; `:kill` cannot be trapped.","types":["Pid","X","Void"],"urlPath":"process"},{"anchor":"function-register","kind":"function","line":239,"name":"register","qualifiedName":"Process.register","signatures":["register(pid, name) : Pid -> Atom -> Void"],"summary":"Registers `pid` under the atom `name`, so it can be found by name rather than by passing the `Pid` around.","types":["Pid","Atom","Void"],"urlPath":"process"},{"anchor":"function-whereis","kind":"function","line":253,"name":"whereis","qualifiedName":"Process.whereis","signatures":["whereis(name) : Atom -> Pid?"],"summary":"Returns the `Pid` registered under `name`, or `None` when nothing is.","types":["Atom","Pid?"],"urlPath":"process"},{"anchor":"record-processresult","kind":"record","line":260,"name":"ProcessResult","qualifiedName":"ProcessResult","signatures":[],"summary":"What an external command left behind: its exit status and its output.\n\nReturned inside `Ok` by `Process.run`, whatever the exit status.","types":["Integer","String"],"urlPath":"process"},{"anchor":"make-pid","kind":"make","line":271,"name":"Pid","qualifiedName":"Pid","signatures":["send(msg) : X -> Void","sendFrom(msg) : X -> Void"],"summary":"","types":["X","Void"],"urlPath":"process"},{"anchor":"fn-send","kind":"function","line":284,"name":"send","qualifiedName":"Pid.send","signatures":["send(msg) : X -> Void"],"summary":"Sends `msg` to the process, unchanged, as a raw BEAM term.\n\nSending never blocks and never fails, even when the process is gone: that is the BEAM's model, not an oversight. Monitor the process when delivery matters.","types":["X","Void"],"urlPath":"process"},{"anchor":"fn-sendfrom","kind":"function","line":295,"name":"sendFrom","qualifiedName":"Pid.sendFrom","signatures":["sendFrom(msg) : X -> Void"],"summary":"Sends the conventional Erlang sender-bearing pair `{Process.self, msg}`, so the receiver knows where to answer.","types":["X","Void"],"urlPath":"process"},{"anchor":"make-process<x>","kind":"make","line":352,"name":"Process<X>","qualifiedName":"Process<X>","signatures":["send(msg) : X -> Void","sendFrom(msg) : X -> Void"],"summary":"A spawned Process<X> is a typed process handle backed by the same runtime pid. It therefore supports the ordinary pid lifecycle operations without erasing its message type.","types":["X","Void"],"urlPath":"process"},{"anchor":"fn-send","kind":"function","line":358,"name":"send","qualifiedName":"Process<X>.send","signatures":["send(msg) : X -> Void"],"summary":"Sends `msg` to the process. Unlike `Pid.send`, the message type is checked.","types":["X","Void"],"urlPath":"process"},{"anchor":"fn-sendfrom","kind":"function","line":366,"name":"sendFrom","qualifiedName":"Process<X>.sendFrom","signatures":["sendFrom(msg) : X -> Void"],"summary":"Sends the sender-bearing pair `{Process.self, msg}`, with the message type checked.","types":["X","Void"],"urlPath":"process"},{"anchor":"make-server<x>","kind":"make","line":394,"name":"Server<X>","qualifiedName":"Server<X>","signatures":["within(timeout) : Integer -> Server<X>"],"summary":"","types":["Integer","Server<X>"],"urlPath":"process"},{"anchor":"fn-within","kind":"function","line":406,"name":"within","qualifiedName":"Server<X>.within","signatures":["within(timeout) : Integer -> Server<X>"],"summary":"Returns the same server with a different default call timeout, in milliseconds.\n\nThe server is untouched: this is a new view of it, so one slow call can be given more room without changing anything for other callers.","types":["Integer","Server<X>"],"urlPath":"process"},{"anchor":"make-from<x>","kind":"make","line":440,"name":"From<X>","qualifiedName":"From<X>","signatures":["reply(value) : X -> Void"],"summary":"","types":["X","Void"],"urlPath":"process"},{"anchor":"fn-reply","kind":"function","line":453,"name":"reply","qualifiedName":"From<X>.reply","signatures":["reply(value) : X -> Void"],"summary":"Answers the pending call this value identifies.\n\nA slot that cannot answer immediately, because it is waiting on something else: omits `reply` from its transition and calls this later instead.","types":["X","Void"],"urlPath":"process"},{"anchor":"module-task","kind":"module","line":464,"name":"Task","qualifiedName":"Task","signatures":["sleep(duration) : Duration -> Void","start(f) : Block<X> -> Task","awaitAll(tasks) : [Task] -> [X]"],"summary":"Running work in another process and collecting the answer.","types":["Duration","Void","Block<X>","Task","[Task]","[X]"],"urlPath":"process"},{"anchor":"function-sleep","kind":"function","line":466,"name":"sleep","qualifiedName":"Task.sleep","signatures":["sleep(duration) : Duration -> Void"],"summary":"Suspends for an elapsed duration. Negative durations are treated as zero.","types":["Duration","Void"],"urlPath":"process"},{"anchor":"function-start","kind":"function","line":485,"name":"start","qualifiedName":"Task.start","signatures":["start(f) : Block<X> -> Task"],"summary":"Runs `f` in a new process and returns a handle on its result.\n\nThe block starts immediately, so starting several tasks and awaiting them afterwards is what makes them run at the same time.","types":["Block<X>","Task"],"urlPath":"process"},{"anchor":"function-awaitall","kind":"function","line":504,"name":"awaitAll","qualifiedName":"Task.awaitAll","signatures":["awaitAll(tasks) : [Task] -> [X]"],"summary":"Waits for every task in `tasks` and returns their results, in the order the tasks were given.\n\nEach result comes back wrapped in a `Result`, so one task failing does not cost you the others' answers. That differs from `Task.await` on a single task, which hands back the value itself.","types":["[Task]","[X]"],"urlPath":"process"},{"anchor":"make-task","kind":"make","line":508,"name":"Task","qualifiedName":"Task","signatures":["await(timeout) : Integer -> X"],"summary":"","types":["Integer","X"],"urlPath":"process"},{"anchor":"fn-await","kind":"function","line":529,"name":"await","qualifiedName":"Task.await","signatures":["await(timeout) : Integer -> X"],"summary":"Waits for the task's result, giving up after `timeout` milliseconds.\n\nAnswers `None` if the task has not finished in time. The task itself is not stopped.","types":["Integer","X"],"urlPath":"process"},{"anchor":"function-worker","kind":"function","line":537,"name":"worker","qualifiedName":"worker","signatures":["worker : Block<Pid> -> (Atom, Block<Pid>)"],"summary":"Wraps a spawn block into a worker spec for `Supervisor.start`.","types":["(Block<Pid>) -> (Atom, Block<Pid>)"],"urlPath":"process"},{"anchor":"make-reference","kind":"make","line":539,"name":"Reference","qualifiedName":"Reference","signatures":[],"summary":"","types":[],"urlPath":"process"},{"anchor":"type-range","kind":"type","line":17,"name":"Range","qualifiedName":"Range","signatures":[],"summary":"A span between two bounds, written `(1..10)` or `('a'..'z')`.\n\nA range stores only its two endpoints and computes everything else from them, so `(1..1000000)` costs nothing to make. Both ends are included.\n\n```kex\n(1..5).items          # => [1, 2, 3, 4, 5]\n(1..5).sum            # => 15\n5.in?(1..10)          # => true\n('a'..'e').items      # => ['a', 'b', 'c', 'd', 'e']\n```\n\nIt is `Enumerable` and `Foldable`, so the traversal methods work directly, and the list operations below answer in list terms. Use `items` when you want a real list to hand to something else.\n\n```kex\n(1..10).items.filter(~even?)   # => [2, 4, 6, 8, 10]\n(1..3).items.each { |n| IO.printLine(n) }\n```","types":[],"urlPath":"range"},{"anchor":"make-range","kind":"make","line":19,"name":"Range","qualifiedName":"Range","signatures":["reduce(acc, f)","contains?(value) : A -> Bool","sort(comparator) : (A -> A -> Bool) -> [A]","join(separator) : String -> String","at(index) : Integer -> A?","get(index) : Integer -> A?","get(index) : Integer -> A -> A","take(n) : Integer -> [A]","drop(n) : Integer -> [A]","indexOf(value) : A -> Integer?","zip(other) : [B] -> [(A, B)]","partition(pred) : (A -> Bool) -> ([A], [A])","push(value) : A -> [A]","reject(pred) : (A -> Bool) -> [A]"],"summary":"","types":["A","Bool","(A) -> (A) -> Bool","[A]","String","Integer","A?","(A) -> A","Integer?","[B]","[(A, B)]","(A) -> Bool","([A], [A])"],"urlPath":"range"},{"anchor":"fn-reduce","kind":"function","line":32,"name":"reduce","qualifiedName":"Range.reduce","signatures":["reduce(acc, f)"],"summary":"Folds over the range's elements in ascending order.\n\nThis is `Range`'s `Enumerable` primitive; the traversal methods are built on it. A range stays structurally minimal, so the fold runs over its materialized items.","types":[],"urlPath":"range"},{"anchor":"fn-contains?","kind":"function","line":62,"name":"contains?","qualifiedName":"Range.contains?","signatures":["contains?(value) : A -> Bool"],"summary":"Returns `true` when `value` falls inside the range, endpoints included.\n\n`5.in?(1..10)` says the same thing from the value's side, and often reads better.","types":["A","Bool"],"urlPath":"range"},{"anchor":"fn-sort","kind":"function","line":207,"name":"sort","qualifiedName":"Range.sort","signatures":["sort(comparator) : (A -> A -> Bool) -> [A]"],"summary":"Returns the elements as a list, ordered by `comparator`.","types":["(A) -> (A) -> Bool","[A]"],"urlPath":"range"},{"anchor":"fn-join","kind":"function","line":230,"name":"join","qualifiedName":"Range.join","signatures":["join(separator) : String -> String"],"summary":"Renders the elements as text, with `separator` between them.","types":["String"],"urlPath":"range"},{"anchor":"fn-at","kind":"function","line":245,"name":"at","qualifiedName":"Range.at","signatures":["at(index) : Integer -> A?"],"summary":"Returns the element at index `index`, counting from 0, or `None` when out of range.\n\nNote that the index counts positions, not values: `(10..20).at(0)` is `Just(10)`.","types":["Integer","A?"],"urlPath":"range"},{"anchor":"fn-get","kind":"function","line":257,"name":"get","qualifiedName":"Range.get","signatures":["get(index) : Integer -> A?","get(index) : Integer -> A -> A"],"summary":"Returns the element at index `index`, counting from 0, or `None` when out of range. The same as `at`, named to match `List` and `Map`.","types":["Integer","A?","(A) -> A"],"urlPath":"range"},{"anchor":"fn-take","kind":"function","line":284,"name":"take","qualifiedName":"Range.take","signatures":["take(n) : Integer -> [A]"],"summary":"Returns the first `n` elements as a list.","types":["Integer","[A]"],"urlPath":"range"},{"anchor":"fn-drop","kind":"function","line":294,"name":"drop","qualifiedName":"Range.drop","signatures":["drop(n) : Integer -> [A]"],"summary":"Returns everything after the first `n` elements, as a list.","types":["Integer","[A]"],"urlPath":"range"},{"anchor":"fn-indexof","kind":"function","line":306,"name":"indexOf","qualifiedName":"Range.indexOf","signatures":["indexOf(value) : A -> Integer?"],"summary":"Returns the position of `value` in the range, or `None` when it is not in it.","types":["A","Integer?"],"urlPath":"range"},{"anchor":"fn-zip","kind":"function","line":320,"name":"zip","qualifiedName":"Range.zip","signatures":["zip(other) : [B] -> [(A, B)]"],"summary":"Pairs each element with the element at the same position in `other`, stopping at the shorter of the two.","types":["[B]","[(A, B)]"],"urlPath":"range"},{"anchor":"fn-partition","kind":"function","line":330,"name":"partition","qualifiedName":"Range.partition","signatures":["partition(pred) : (A -> Bool) -> ([A], [A])"],"summary":"Splits the elements into those satisfying `pred` and those that do not.","types":["(A) -> Bool","([A], [A])"],"urlPath":"range"},{"anchor":"fn-push","kind":"function","line":340,"name":"push","qualifiedName":"Range.push","signatures":["push(value) : A -> [A]"],"summary":"Returns the elements as a list with `value` added at the end.","types":["A","[A]"],"urlPath":"range"},{"anchor":"fn-reject","kind":"function","line":350,"name":"reject","qualifiedName":"Range.reject","signatures":["reject(pred) : (A -> Bool) -> [A]"],"summary":"Returns the elements that do NOT satisfy `pred`, as a list.","types":["(A) -> Bool","[A]"],"urlPath":"range"},{"anchor":"module-regex","kind":"module","line":1,"name":"Regex","qualifiedName":"Regex","signatures":["regex(source) : String -> Result<Regex, RegexError>","regex(source) : [String] -> [Any] -> Regex","validateRegex(source)","re(source)","validateRe(source)","quote(s) : String -> String","get(key) : Atom | Integer -> String?","get(key) : Atom | Integer -> String -> String","matches(s, re) : String -> Regex -> Match?","matches?(s, re) : String -> Regex -> Bool","scan(s, re) : String -> Regex -> [Match]","replace : String -> Regex -> String | (Match) -> String -> String","splitLimit : String -> Regex -> Integer -> [String]"],"summary":"","types":["String","Integer","Result<Regex, RegexError>","[String]","([Any]) -> Regex","Map<Any, String>","Atom | Integer","String?","(String) -> String","Regex","Match?","Bool","[Match]","(String) -> (Regex) -> (String | (Match) -> String) -> String","(String) -> (Regex) -> (Integer) -> [String]"],"urlPath":"regex"},{"anchor":"record-regex","kind":"record","line":37,"name":"Regex","qualifiedName":"Regex.Regex","signatures":[],"summary":"Regular expressions, backed by PCRE2 in the interpreter and Erlang's `re` on BEAM: the same PCRE pattern language on both.\n\nOpt-in, not prelude: nothing here is in scope until `using Regex`.\n\nThere are two ways to write a pattern. The tagged literal is the everyday one: it is checked when your program is compiled, so it cannot fail at run time and gives you a bare `Regex`.\n\n```kex\nusing Regex\n\nmain do\n  let line = \"order #4271 shipped\"\n  IO.printLine(line.matches?(re`#\\d``))                     # => true\n  IO.printLine(line.matches(re`#(\\d`)`).map { |m| m.get(1) })  # => 4271\nend\n```\n\nThe call form takes a pattern built at run time and answers a `Result`, because an arbitrary string may not be a valid pattern:\n\n```kex\nmatch regex(userSupplied) do\n  Ok(pattern) => IO.printLine(text.matches?(pattern))\n  Error(e)    => IO.printError(\"bad pattern at ${e.position}: ${e.message}\")\nend\n```\n\nThe operations are `matches?` (is it there), `matches` (find the first), `scan` (find them all), `replace`, and `split`.\n\nA compiled regular expression.\n\n`Regex` carries only its pattern source. The compiled engine object lives in a runtime cache keyed by that source, deliberately: a compiled pattern bakes in the host's PCRE version and must never be embedded in a distributed artifact, so the source string is the value's identity.","types":["String"],"urlPath":"regex"},{"anchor":"record-regexerror","kind":"record","line":51,"name":"RegexError","qualifiedName":"Regex.RegexError","signatures":[],"summary":"A pattern that failed to compile.\n\nMirrors `ParseError`: the `position` is a *character* offset into the pattern, not a byte offset, so it agrees across backends on patterns containing non-ASCII.\n\n```kex\nregex(\"(\")\n# => Error(RegexError { source: \"(\", position: 1,\n#                       message: \"missing closing parenthesis\" })\n```","types":["String","Integer"],"urlPath":"regex"},{"anchor":"function-regex","kind":"function","line":91,"name":"regex","qualifiedName":"Regex.regex","signatures":["regex(source) : String -> Result<Regex, RegexError>","regex(source) : [String] -> [Any] -> Regex"],"summary":"NOTE: deliberately does NOT `implement: Errorable`, even though the trait exists for exactly this shape. Declaring a `message` method alongside the `message` field makes `e.message` dispatch to the method instead of reading the field, which fails with `undef` on the BEAM backend. `ParseError`: the prelude's equivalent error record: carries a bare `message` field for the same reason; nothing in the tree implements Errorable today.\n\nCompiles `source` into a `Regex`.\n\nAnswers a `Result` because an arbitrary string may not be a valid pattern. Use this form when the pattern is built at run time: from a config file, from user input. For a pattern you write yourself, the tag form `` regex`\\d`` `` is checked at compile time and hands back a bare `Regex`.","types":["String","Result<Regex, RegexError>","[String]","([Any]) -> Regex"],"urlPath":"regex"},{"anchor":"function-validateregex","kind":"function","line":129,"name":"validateRegex","qualifiedName":"Regex.validateRegex","signatures":["validateRegex(source)"],"summary":"Validates a `regex` tagged literal at compile time.\n\nFound by the compiler through the standard `validate<Tag>` naming convention (nothing about regex is special-cased). A raw `` regex`(` `` is a compile error, with the caret pointing at the byte offset PCRE2 reported inside the literal.\n\nMust stay pure: the compiler evaluates it on the tree-walk interpreter at compile time, so marking it `foul` would break builds, not just calls.","types":[],"urlPath":"regex"},{"anchor":"function-re","kind":"function","line":148,"name":"re","qualifiedName":"Regex.re","signatures":["re(source)"],"summary":"A short alias for `regex`, in both its forms.\n\nA second name bound to the same two functions, not a lexer synonym, so every property of `regex` (escaping, backend mapping, error shape) applies to it unchanged. Most code uses the tag form, where the shorter name reads better.","types":[],"urlPath":"regex"},{"anchor":"function-validatere","kind":"function","line":157,"name":"validateRe","qualifiedName":"Regex.validateRe","signatures":["validateRe(source)"],"summary":"Validates an `re` tagged literal at compile time, delegating to `validateRegex`. The compiler finds it by the same `validate<Tag>` convention.","types":[],"urlPath":"regex"},{"anchor":"function-quote","kind":"function","line":180,"name":"quote","qualifiedName":"Regex.quote","signatures":["quote(s) : String -> String"],"summary":"Escapes every regex metacharacter in `s`, so the result matches `s` literally.\n\nThis is how to search for text that may itself contain pattern syntax: a search term typed by a user, a filename, a version string. It escapes per character rather than wrapping in `\\Q...\\E`, which a value containing `\\E` would break out of.","types":["String"],"urlPath":"regex"},{"anchor":"record-match","kind":"record","line":192,"name":"Match","qualifiedName":"Regex.Match","signatures":[],"summary":"A successful match, and the groups it captured.\n\nIt behaves like a map keyed by both group number (`0` is the whole match) and group name (`:year`), but is a named type so it can grow spans and surrounding context later without breaking existing `get` call sites.\n\nA group that did not participate is an absent key, so `get` answers `None`. This differs from a group that matched the empty string, which answers `Just(\"\")`.","types":["Map<Any, String>"],"urlPath":"regex"},{"anchor":"make-match","kind":"make","line":205,"name":"Match","qualifiedName":"Match","signatures":["get(key) : Atom | Integer -> String?","get(key) : Atom | Integer -> String -> String"],"summary":"NOTE: the accessor is `get`, not `get`. A `make` block on a user type that defines a method name the prelude also uses breaks that name's dispatch for every OTHER type on the BEAM backend: with `get` here, merely saying `using Regex` made a plain `someMap.get(k)` fail with function_clause. `get` also matches Python's `m.get(1)` and Java's `matcher.get(1)`. A plain Map's `get` is unaffected: resolution picks a local method by receiver type, name and arity.","types":["Atom | Integer","String?","(String) -> String"],"urlPath":"regex"},{"anchor":"fn-get","kind":"function","line":224,"name":"get","qualifiedName":"Match.get","signatures":["get(key) : Atom | Integer -> String?","get(key) : Atom | Integer -> String -> String"],"summary":"Returns the text captured by a group, by number or by name.\n\nGroup `0` is the whole match; `1` is the first parenthesised group. A named group `(?<year>...)` is reached with the atom `:year`. A group that did not participate answers `None`.","types":["Atom | Integer","String?","(String) -> String"],"urlPath":"regex"},{"anchor":"function-matches","kind":"function","line":260,"name":"matches","qualifiedName":"Regex.matches","signatures":["matches(s, re) : String -> Regex -> Match?"],"summary":"Finds the first occurrence of `re` in `s` and returns what it matched.\n\nUnanchored: the pattern may match anywhere in the string. Anchor it with `^...$` when a whole-string match is what you mean. Answers `None` when the pattern does not occur.","types":["String","Regex","Match?"],"urlPath":"regex"},{"anchor":"function-matches?","kind":"function","line":278,"name":"matches?","qualifiedName":"Regex.matches?","signatures":["matches?(s, re) : String -> Regex -> Bool"],"summary":"Returns `true` when `re` occurs anywhere in `s`.\n\nThe boolean form of `matches`, and unanchored for the same reason. Use it when you only need to know whether the pattern is there.","types":["String","Regex","Bool"],"urlPath":"regex"},{"anchor":"function-scan","kind":"function","line":299,"name":"scan","qualifiedName":"Regex.scan","signatures":["scan(s, re) : String -> Regex -> [Match]"],"summary":"Returns every match of `re` in `s`, left to right.\n\nAlways answers `[Match]`, whether or not the pattern has capture groups: adding a group to a pattern must not change the type flowing out of `scan`. An empty list means nothing matched.","types":["String","Regex","[Match]"],"urlPath":"regex"},{"anchor":"function-replace","kind":"function","line":324,"name":"replace","qualifiedName":"Regex.replace","signatures":["replace : String -> Regex -> String | (Match) -> String -> String"],"summary":"Replaces EVERY match of `re` in `s`: this is `gsub`, not `sub`.\n\nThe replacement is either a literal `String`, inserted verbatim with no `$1` / `\\1` backreference syntax, or a block receiving the `Match`, which is how a replacement built from what was captured is written.","types":["(String) -> (Regex) -> (String | (Match) -> String) -> String"],"urlPath":"regex"},{"anchor":"function-splitlimit","kind":"function","line":357,"name":"splitLimit","qualifiedName":"Regex.splitLimit","signatures":["splitLimit : String -> Regex -> Integer -> [String]"],"summary":"Splits `s` on `re`, capping the number of fields.\n\nA positive `limit` caps the field count, leaving the remainder unsplit in the last field; a negative `limit` keeps trailing empty fields instead of dropping them.\n\nPlain `s.split(re)` needs no function here at all: it resolves to `String.split`, which dispatches to this engine when handed a Regex (in both backends), and follows Ruby's semantics: trailing empty fields are dropped, leading ones are kept, and capture groups are interleaved into the result.\n\nOnly the limit form needs a name, and it deliberately is NOT `split`: this module must not export that name. On BEAM, a module in scope via `using` captures a method name for EVERY receiver, so exporting `split` here would route `\"a,b\".split(\",\")` and even the no-argument `\"hi\".split` through this module and break them.","types":["(String) -> (Regex) -> (Integer) -> [String]"],"urlPath":"regex"},{"anchor":"type-stream","kind":"type","line":19,"name":"Stream","qualifiedName":"Stream","signatures":[],"summary":"A lazy, potentially infinite sequence.\n\nA stream describes how to produce its elements rather than holding them, so an infinite one is an ordinary value. Nothing is computed until you ask for elements with `take`.\n\n```kex\nlet naturals = Stream.Sequence(from: 0) { |n| n ` 1 }\nnaturals.take(5)                              # => [0, 1, 2, 3, 4]\nnaturals.map { |n| n * n }.take(4)            # => [0, 1, 4, 9]\nnaturals.filter { |n| n.even? }.take(3)       # => [0, 2, 4]\n```\n\n`map`, `filter` and `drop` all answer with another stream, so a pipeline stays lazy end to end; `take` is what turns it into a list.\n\nStreams are best for generated sequences you may revisit. A file or socket is different: it can only be consumed once, so those APIs return a `Feed`. Convert a small feed with `toStream+ only when replaying it is worth keeping every value already read.","types":[],"urlPath":"stream"},{"anchor":"module-stream","kind":"module","line":22,"name":"Stream","qualifiedName":"Stream","signatures":["Sequence(from, step)","Iterate(seed, step)"],"summary":"Constructors for `Stream`.","types":["?"],"urlPath":"stream"},{"anchor":"function-sequence","kind":"function","line":43,"name":"Sequence","qualifiedName":"Stream.Sequence","signatures":["Sequence(from, step)"],"summary":"Builds an infinite stream from a first element and a step function.\n\nThe stream is `from`, then `step(from)`, then `step(step(from))`, and so on: nothing is computed until you take from it.","types":[],"urlPath":"stream"},{"anchor":"function-iterate","kind":"function","line":55,"name":"Iterate","qualifiedName":"Stream.Iterate","signatures":["Iterate(seed, step)"],"summary":"Builds an infinite stream from a seed and a step function. The same thing as `Sequence`: use whichever reads better where you are.","types":[],"urlPath":"stream"},{"anchor":"constant-empty","kind":"constant","line":63,"name":"empty","qualifiedName":"Stream.empty","signatures":[],"summary":"The stream with no elements.","types":["?"],"urlPath":"stream"},{"anchor":"make-stream<a>","kind":"make","line":66,"name":"Stream<A>","qualifiedName":"Stream<A>","signatures":["take(n) : Integer -> [A]","drop(n) : Integer -> Stream<A>","map(f) : (A -> B) -> Stream<B>","filter(pred) : (A -> Bool) -> Stream<A>","each(f) : (A -> Void) -> Void"],"summary":"","types":["Integer","[A]","Stream<A>","(A) -> B","Stream<B>","(A) -> Bool","(A) -> Void","Void"],"urlPath":"stream"},{"anchor":"fn-take","kind":"function","line":86,"name":"take","qualifiedName":"Stream<A>.take","signatures":["take(n) : Integer -> [A]"],"summary":"Returns the first `n` elements as a list, computing the stream up to that point.\n\nThis is the operation that ends a lazy pipeline and gives you real data.","types":["Integer","[A]"],"urlPath":"stream"},{"anchor":"fn-drop","kind":"function","line":102,"name":"drop","qualifiedName":"Stream<A>.drop","signatures":["drop(n) : Integer -> Stream<A>"],"summary":"Returns a new stream that skips the first `n` elements.\n\nStill a stream, so the result stays lazy: pair it with `take` to get a window out of the middle.","types":["Integer","Stream<A>"],"urlPath":"stream"},{"anchor":"fn-map","kind":"function","line":122,"name":"map","qualifiedName":"Stream<A>.map","signatures":["map(f) : (A -> B) -> Stream<B>"],"summary":"Returns a new stream with `f` applied to each element.\n\n`f` is not called until elements are taken, and then only for those that are.","types":["(A) -> B","Stream<B>"],"urlPath":"stream"},{"anchor":"fn-filter","kind":"function","line":144,"name":"filter","qualifiedName":"Stream<A>.filter","signatures":["filter(pred) : (A -> Bool) -> Stream<A>"],"summary":"Returns a new stream with only the elements `pred` accepts.\n\nProducing `n` filtered elements may require walking many more upstream ones, so a predicate that almost never holds makes `take` run for a long time, and one that never holds makes it run forever.","types":["(A) -> Bool","Stream<A>"],"urlPath":"stream"},{"anchor":"fn-each","kind":"function","line":162,"name":"each","qualifiedName":"Stream<A>.each","signatures":["each(f) : (A -> Void) -> Void"],"summary":"Applies `f` to every element.\n\nOnly ever finishes on a stream that ends: a file's lines converted with `Feed.toStream`, or anything `take` has bounded. On `Stream.Sequence` this runs forever, exactly as writing the same loop by hand would.","types":["(A) -> Void","Void"],"urlPath":"stream"},{"anchor":"make-string","kind":"make","line":20,"name":"String","qualifiedName":"String","signatures":["reduce(acc, f) : A -> (A -> Char -> A) -> A","mapChars(f) : (Char -> Char) -> String","filter(pred) : (Char -> Bool) -> String","get(i) : Integer -> Char?","get(i) : Integer -> Char -> Char","take(n) : Integer -> String","drop(n) : Integer -> String","reject(pred) : (Char -> Bool) -> String","indexOf(c) : Char -> Integer?","findIndex(pred) : (Char -> Bool) -> Integer?","zip(other) : [Y] -> [(Char, Y)]","partition(pred) : (Char -> Bool) -> (String, String)","enclose(wrapper) : String -> String","enclose(wrapper) : String -> String -> String","at(i) : Integer -> Char?","split(sep) : String | Regex -> [String]","split : [String]","indentRest(prefix) : String -> String","replace(pattern, replacement) : String -> String -> String","substitute(replacements) : {String: String} -> String","contains?(sub) : String -> Bool","startsWith?(prefix) : String -> Bool","endsWith?(suffix) : String -> Bool"],"summary":"Text. A `String` is a sequence of Unicode characters, immutable like every other Kex value: every method here answers with a new string rather than changing the receiver.\n\nA `String` is its own type, not a list of characters. It is `Enumerable`, so `each`, `reduce`, `find` and friends walk it one `Char` at a time, and the sequence operations that could reasonably answer in either currency pick the useful one: `take`, `drop` and `sort` hand back a `String`, while `first` and `last` hand back a `Char`. Use `chars` to cross over to a real list.\n\n```kex\nlet line = \"  Hello, World  \"\nline.trim.lowerCase.split(\", \")   # => [\"hello\", \"world\"]\nline.trim.take(5)                 # => \"Hello\"\nline.trim.chars.count(~upper?)    # => 2\n```\n\nStrings interpolate with `${...}`:\n\n```kex\nlet name = \"Ada\"\n\"hello, ${name}\"                  # => \"hello, Ada\"\n```","types":["A","(A) -> (Char) -> A","(Char) -> Char","String","(Char) -> Bool","Integer","Char?","Char","Integer?","[Y]","[(Char, Y)]","(String, String)","(String) -> String","String | Regex","[String]","{String: String}","Bool"],"urlPath":"string"},{"anchor":"fn-reduce","kind":"function","line":39,"name":"reduce","qualifiedName":"String.reduce","signatures":["reduce(acc, f) : A -> (A -> Char -> A) -> A"],"summary":"Folds the string from the left, one `Char` at a time.\n\nThis is `String`'s `Enumerable` primitive: `map`, `filter`, `find`, `any?` and the rest are defined in terms of it. Reach for it directly when you are accumulating something that is neither a string nor a list.","types":["A","(A) -> (Char) -> A"],"urlPath":"string"},{"anchor":"fn-mapchars","kind":"function","line":61,"name":"mapChars","qualifiedName":"String.mapChars","signatures":["mapChars(f) : (Char -> Char) -> String"],"summary":"Applies `f` to every character and joins the results back into a `String`.\n\nThis is the string-preserving counterpart of `map`. `map` comes from `Enumerable` and collects into a list, because its type says `(Char -> B) -> [B]` and `B` need not be a `Char` at all. When you want a string out, name the operation that produces one.","types":["(Char) -> Char","String"],"urlPath":"string"},{"anchor":"fn-filter","kind":"function","line":74,"name":"filter","qualifiedName":"String.filter","signatures":["filter(pred) : (Char -> Bool) -> String"],"summary":"Returns the characters satisfying `pred`, as a `String`.","types":["(Char) -> Bool","String"],"urlPath":"string"},{"anchor":"fn-get","kind":"function","line":129,"name":"get","qualifiedName":"String.get","signatures":["get(i) : Integer -> Char?","get(i) : Integer -> Char -> Char"],"summary":"Returns the character at index `i`, counting from 0.\n\nAnswers `None` for an index past either end rather than failing, so it is safe to index with a computed position.","types":["Integer","Char?","(Char) -> Char"],"urlPath":"string"},{"anchor":"fn-take","kind":"function","line":159,"name":"take","qualifiedName":"String.take","signatures":["take(n) : Integer -> String"],"summary":"Returns the first `n` characters. A short string is returned whole, so `take` never fails on an `n` that is too large.","types":["Integer","String"],"urlPath":"string"},{"anchor":"fn-drop","kind":"function","line":175,"name":"drop","qualifiedName":"String.drop","signatures":["drop(n) : Integer -> String"],"summary":"Returns everything after the first `n` characters. The complement of `take`: `s.take(n) ` s.drop(n)` is `s+.","types":["Integer","String"],"urlPath":"string"},{"anchor":"fn-reject","kind":"function","line":189,"name":"reject","qualifiedName":"String.reject","signatures":["reject(pred) : (Char -> Bool) -> String"],"summary":"Returns the characters that do NOT satisfy `pred`: the complement of `filter`.","types":["(Char) -> Bool","String"],"urlPath":"string"},{"anchor":"fn-indexof","kind":"function","line":218,"name":"indexOf","qualifiedName":"String.indexOf","signatures":["indexOf(c) : Char -> Integer?"],"summary":"Returns the index of the first occurrence of `c`, or `None` when the character does not appear.","types":["Char","Integer?"],"urlPath":"string"},{"anchor":"fn-findindex","kind":"function","line":235,"name":"findIndex","qualifiedName":"String.findIndex","signatures":["findIndex(pred) : (Char -> Bool) -> Integer?"],"summary":"Returns the index of the first character satisfying `pred`, or `None`.\n\nThe predicate counterpart of `indexOf`, which searches for one known character.","types":["(Char) -> Bool","Integer?"],"urlPath":"string"},{"anchor":"fn-zip","kind":"function","line":250,"name":"zip","qualifiedName":"String.zip","signatures":["zip(other) : [Y] -> [(Char, Y)]"],"summary":"Pairs each character with the element at the same index in `other`, stopping at the shorter of the two.","types":["[Y]","[(Char, Y)]"],"urlPath":"string"},{"anchor":"fn-partition","kind":"function","line":266,"name":"partition","qualifiedName":"String.partition","signatures":["partition(pred) : (Char -> Bool) -> (String, String)"],"summary":"Splits the string in two: the characters satisfying `pred`, then those that do not. One pass, both answers.","types":["(Char) -> Bool","(String, String)"],"urlPath":"string"},{"anchor":"fn-enclose","kind":"function","line":341,"name":"enclose","qualifiedName":"String.enclose","signatures":["enclose(wrapper) : String -> String","enclose(wrapper) : String -> String -> String"],"summary":"Returns the string with `wrapper` added at both ends.","types":["String","(String) -> String"],"urlPath":"string"},{"anchor":"fn-at","kind":"function","line":368,"name":"at","qualifiedName":"String.at","signatures":["at(i) : Integer -> Char?"],"summary":"Returns the character at index `i`, counting from 0, or `None` when the index is out of range. The same as the one-argument `get`.","types":["Integer","Char?"],"urlPath":"string"},{"anchor":"fn-split","kind":"function","line":476,"name":"split","qualifiedName":"String.split","signatures":["split(sep) : String | Regex -> [String]","split : [String]"],"summary":"Splits the string on every occurrence of `sep`, which may be a literal string or a `Regex`.\n\nSeparators at the ends produce empty parts, so splitting `\",a,\"` on `\",\"` gives three parts. Filter or trim afterwards when that is not wanted.","types":["String | Regex","[String]"],"urlPath":"string"},{"anchor":"fn-indentrest","kind":"function","line":525,"name":"indentRest","qualifiedName":"String.indentRest","signatures":["indentRest(prefix) : String -> String"],"summary":"Indents every line but the first by `prefix`.\n\nThis is what splicing a multi-line value into an indented `${...}` hole needs: the hole's own indentation already covers line one, and the rest have to catch up. Blank lines stay blank, and (through `lines`) a single trailing newline is dropped, so a block does not push a stray empty line into its slot.","types":["String"],"urlPath":"string"},{"anchor":"fn-replace","kind":"function","line":552,"name":"replace","qualifiedName":"String.replace","signatures":["replace(pattern, replacement) : String -> String -> String"],"summary":"Replaces every literal occurrence of `pattern` with `replacement`.\n\nThe pattern is matched literally, not as a regular expression. An empty pattern matches at every character boundary, including both ends.","types":["String"],"urlPath":"string"},{"anchor":"fn-substitute","kind":"function","line":588,"name":"substitute","qualifiedName":"String.substitute","signatures":["substitute(replacements) : {String: String} -> String"],"summary":"Replaces every key of `replacements` with its value, in one pass over the map.\n\nThe keys are the placeholders exactly as written: no syntax is imposed and nothing is reserved, so `$NAME$`, `__NAME__`, `{{name}}` and `%name%` are all equally valid, and a template needs no escaping to be a template. `$NAME$` is the convention to reach for when there is no reason to prefer another.\n\nNote that a placeholder is NOT `${name}`: that is interpolation, which the compiler resolves before this ever sees the string.\n\nThis is what a template wants instead of a chain of `replace` calls: the chain reads as a pipeline when it is really a substitution table, and it quietly depends on its own order, since an earlier replacement's OUTPUT is still visible to every later one.\n\nSubstitutions are applied in the map's canonical key order and each is applied to the result of the last, so a value that itself contains a key can still be rewritten by a later one. Keep values placeholder-free when that matters.","types":["{String: String}","String"],"urlPath":"string"},{"anchor":"fn-contains?","kind":"function","line":711,"name":"contains?","qualifiedName":"String.contains?","signatures":["contains?(sub) : String -> Bool"],"summary":"Returns `true` when `sub` appears anywhere in the string.\n\nThe search is literal and case-sensitive. Lower-case both sides to ignore case; use `Regex` when the needle is a pattern.","types":["String","Bool"],"urlPath":"string"},{"anchor":"fn-startswith?","kind":"function","line":725,"name":"startsWith?","qualifiedName":"String.startsWith?","signatures":["startsWith?(prefix) : String -> Bool"],"summary":"Returns `true` when the string begins with `prefix`.","types":["String","Bool"],"urlPath":"string"},{"anchor":"fn-endswith?","kind":"function","line":739,"name":"endsWith?","qualifiedName":"String.endsWith?","signatures":["endsWith?(suffix) : String -> Bool"],"summary":"Returns `true` when the string ends with `suffix`.","types":["String","Bool"],"urlPath":"string"},{"anchor":"make-char","kind":"make","line":756,"name":"Char","qualifiedName":"Char","signatures":["in?(range) : Range<Char> -> Bool"],"summary":"A single Unicode character.\n\nCharacter literals are written with single quotes (`'a'`) and are a different type from the one-character string `\"a\"`. The classification methods (`digit?`, `alpha?`, `space?` and the rest) are what most character code needs; `codepoint` and `String.fromCodepoint` are the escape hatch to raw Unicode values.\n\n```kex\n\"hello world\".chars.count(~alpha?)   # => 10\n'a'.upperCase                        # => 'A'\n'a'.string                           # => \"a\"\n```","types":["Range<Char>","Bool"],"urlPath":"string"},{"anchor":"fn-in?","kind":"function","line":906,"name":"in?","qualifiedName":"Char.in?","signatures":["in?(range) : Range<Char> -> Bool"],"summary":"Returns `true` when the character falls inside `range`, endpoints included.","types":["Range<Char>","Bool"],"urlPath":"string"},{"anchor":"module-string","kind":"module","line":912,"name":"String","qualifiedName":"String","signatures":["fromCodepoint(value) : Integer -> String?","fromBytes(values) : [Byte] -> String?"],"summary":"Constructors for `String` values that are built from something other than text: a Unicode codepoint, or raw UTF-8 bytes.","types":["Integer","String?","[Byte]"],"urlPath":"string"},{"anchor":"function-fromcodepoint","kind":"function","line":930,"name":"fromCodepoint","qualifiedName":"String.fromCodepoint","signatures":["fromCodepoint(value) : Integer -> String?"],"summary":"Builds a one-character string from a Unicode codepoint.\n\nAnswers `None` for a surrogate or a value outside the Unicode scalar range, so the result is always valid text. `Char.codepoint` is the inverse.","types":["Integer","String?"],"urlPath":"string"},{"anchor":"function-frombytes","kind":"function","line":949,"name":"fromBytes","qualifiedName":"String.fromBytes","signatures":["fromBytes(values) : [Byte] -> String?"],"summary":"Rebuilds a string from its UTF-8 bytes: the inverse of `bytes`.\n\nA `String` is TEXT, so bytes outside 0..255 or a malformed encoding answer `None` rather than a string that would decode to replacement characters. That makes it a decoding step you can check, not a lossy cast.","types":["[Byte]","String?"],"urlPath":"string"},{"anchor":"make-tuple","kind":"make","line":960,"name":"Tuple","qualifiedName":"Tuple","signatures":[],"summary":"A fixed-size group of values, written `(a, b)`. Unlike a list, a tuple's size and the type of each position are part of its type, so the `List` methods do not apply to it: destructure it, match on it, or convert it with `items`.\n\n```kex\nlet (name, age) = (\"Ada\", 36)\n[1, 2, 3].partition { |n| n.even? }   # => ([2], [1, 3])\n```","types":[],"urlPath":"string"},{"anchor":"module-system","kind":"module","line":15,"name":"System","qualifiedName":"System","signatures":["exit(code) : Integer -> Void"],"summary":"The running process and the machine under it: exiting, and asking what platform this is.\n\n```kex\nSystem.OS          # => :macos\nSystem.posix?      # => true\nSystem.exit(1)     # ends the program with status 1\n```\n\nDeliberately NOT a capability, and `OS`/`BITWIDTH` stay pure. Faking the reported OS only exercises a program's branching, not the platform behaviour behind it: the file semantics, path rules and process handling that actually differ are unaffected by the atom. Testing those means running on the platform, which CI does across macOS, Ubuntu and Alpine. `Mock.System` existed for this and had exactly one caller: the spec that tested it (kexhq/kex#143).","types":["Integer","Void","OperatingSystem","Bool"],"urlPath":"system"},{"anchor":"type-operatingsystem","kind":"type","line":28,"name":"OperatingSystem","qualifiedName":"System.OperatingSystem","signatures":[],"summary":"The operating system families a program may be running on.\n\nA union of atoms rather than an ADT: these are plain tags, and a `match` over them is still exhaustive. Anything unmodelled is `:unknown`: the union is closed, so callers can cover it.","types":[],"urlPath":"system"},{"anchor":"function-exit","kind":"function","line":49,"name":"exit","qualifiedName":"System.exit","signatures":["exit(code) : Integer -> Void"],"summary":"Ends the process immediately with exit status `code`.\n\nThe invoking shell receives the code, so this is how a command-line tool reports success or failure to whatever ran it: 0 means success, anything else means failure.\n\nNothing after the call runs, and no cleanup happens: close what needs closing first.","types":["Integer","Void"],"urlPath":"system"},{"anchor":"constant-os","kind":"constant","line":70,"name":"OS","qualifiedName":"System.OS","signatures":[],"summary":"The operating system family this program is running on.\n\nBoth backends answer with the same atom for the same machine. Prefer the `macOS?` / `linux?` / `windows?` / `posix?` predicates below when you are asking one yes-or-no question; use `OS` when you need to branch several ways.","types":["OperatingSystem"],"urlPath":"system"},{"anchor":"constant-bitwidth","kind":"constant","line":83,"name":"BITWIDTH","qualifiedName":"System.BITWIDTH","signatures":[],"summary":"The machine's pointer width in bits: 64 on anything current, 32 on a small target.\n\nReported by the emulator on BEAM and by the pointer size in the tree walker, so both agree for one machine.","types":["Integer"],"urlPath":"system"},{"anchor":"constant-macos?","kind":"constant","line":92,"name":"macOS?","qualifiedName":"System.macOS?","signatures":[],"summary":"Returns `true` when running on macOS.","types":["Bool"],"urlPath":"system"},{"anchor":"constant-linux?","kind":"constant","line":103,"name":"linux?","qualifiedName":"System.linux?","signatures":[],"summary":"Returns `true` when running on Linux.","types":["Bool"],"urlPath":"system"},{"anchor":"constant-windows?","kind":"constant","line":112,"name":"windows?","qualifiedName":"System.windows?","signatures":[],"summary":"Returns `true` when running on Windows.","types":["Bool"],"urlPath":"system"},{"anchor":"constant-posix?","kind":"constant","line":132,"name":"posix?","qualifiedName":"System.posix?","signatures":[],"summary":"Returns `true` when the platform follows POSIX conventions for paths, separators and shell behaviour.\n\nEverything the toolchain runs on except Windows behaves POSIX-ly enough for paths, separators and shell conventions. An unknown system is NOT assumed to be POSIX.","types":["Bool"],"urlPath":"system"},{"anchor":"function-die","kind":"function","line":157,"name":"die","qualifiedName":"die","signatures":["die() : Never","die(message) : String -> Never"],"summary":"Ends the program with a fatal error message.\n\n`message` goes to stderr behind a `\"fatal: \"` prefix and the exit status is 1. This is an abort, not an exception, so `trying` / `rescue` cannot catch it. Use it only where there is no recoverable answer. The prelude uses it for a negative `repeat` count, for instance.\n\nIts result type is `Never`, the bottom type: `die` does not return, so a branch that dies takes the other branch's type, and `if b == 0 then die(\"divide by zero\") else a end` is an `Integer`.\n\n`die` is declared bare, like `assert`, so it is always in scope.","types":["String","Never"],"urlPath":"system"},{"anchor":"module-taggedvalidation","kind":"module","line":20,"name":"TaggedValidation","qualifiedName":"TaggedValidation","signatures":["fatal(message) : String -> Issue","fatalAt(offset, message) : Integer -> String -> Issue","fatalBetween : Integer -> Integer -> String -> Issue","warn(message) : String -> Issue","warnAt(offset, message) : Integer -> String -> Issue","warnBetween : Integer -> Integer -> String -> Issue"],"summary":"Diagnostics returned by compile-time validators for tagged literals.\n\nA tagged literal (`` re`\\d`` ``, `` sql`SELECT ...` ``) can be checked while your program is compiled rather than when it runs. The compiler finds the checker by name: a tag `foo` is validated by a function named `validateFoo` taking the literal's text and returning a list of `Issue` values. An empty list means the literal is fine.\n\n```kex\nlet validateHex(source: String) -> [TaggedValidation.Issue] do\n  match source.chars.findIndex { |c| !c.digit? && !c.in?('a'..'f') } do\n    Just(offset) => [TaggedValidation.fatalAt(offset, \"not a hex digit\")]\n    None         => []\n  end\nend\n```\n\nA `Fatal` issue stops the build with the message, pointing the caret at the offset; a `Warn+ reports without stopping.\n\nByte offsets are zero-based and relative to the cooked literal body.","types":["Integer","ByteSpan?","String","Issue","(Integer) -> (Integer) -> (String) -> Issue"],"urlPath":"taggedvalidation"},{"anchor":"type-bytespan","kind":"type","line":25,"name":"ByteSpan","qualifiedName":"TaggedValidation.ByteSpan","signatures":[],"summary":"Where in the literal an issue applies: a single offset, or a range.\n\nBoth are byte offsets into the literal body, counted from zero.","types":["Integer","Integer","Integer"],"urlPath":"taggedvalidation"},{"anchor":"type-issue","kind":"type","line":31,"name":"Issue","qualifiedName":"TaggedValidation.Issue","signatures":[],"summary":"One diagnostic about a tagged literal.\n\n`Fatal` stops the build; `Warn` reports and lets it continue. The span is optional: an issue about the literal as a whole carries `None`.","types":["ByteSpan?","String","ByteSpan?","String"],"urlPath":"taggedvalidation"},{"anchor":"function-fatal","kind":"function","line":41,"name":"fatal","qualifiedName":"TaggedValidation.fatal","signatures":["fatal(message) : String -> Issue"],"summary":"A fatal issue about the literal as a whole, with no particular position.","types":["String","Issue"],"urlPath":"taggedvalidation"},{"anchor":"function-fatalat","kind":"function","line":61,"name":"fatalAt","qualifiedName":"TaggedValidation.fatalAt","signatures":["fatalAt(offset, message) : Integer -> String -> Issue"],"summary":"A fatal issue at one offset in the literal.\n\nThe offset is where the caret points in the compiler's error, so use the position the underlying checker reported.","types":["Integer","String","Issue"],"urlPath":"taggedvalidation"},{"anchor":"function-fatalbetween","kind":"function","line":73,"name":"fatalBetween","qualifiedName":"TaggedValidation.fatalBetween","signatures":["fatalBetween : Integer -> Integer -> String -> Issue"],"summary":"A fatal issue spanning a range of the literal.","types":["(Integer) -> (Integer) -> (String) -> Issue"],"urlPath":"taggedvalidation"},{"anchor":"function-warn","kind":"function","line":85,"name":"warn","qualifiedName":"TaggedValidation.warn","signatures":["warn(message) : String -> Issue"],"summary":"A warning about the literal as a whole. Reported, but the build continues.","types":["String","Issue"],"urlPath":"taggedvalidation"},{"anchor":"function-warnat","kind":"function","line":96,"name":"warnAt","qualifiedName":"TaggedValidation.warnAt","signatures":["warnAt(offset, message) : Integer -> String -> Issue"],"summary":"A warning at one offset in the literal.","types":["Integer","String","Issue"],"urlPath":"taggedvalidation"},{"anchor":"function-warnbetween","kind":"function","line":108,"name":"warnBetween","qualifiedName":"TaggedValidation.warnBetween","signatures":["warnBetween : Integer -> Integer -> String -> Issue"],"summary":"A warning spanning a range of the literal.","types":["(Integer) -> (Integer) -> (String) -> Issue"],"urlPath":"taggedvalidation"},{"anchor":"module-template","kind":"module","line":63,"name":"Template","qualifiedName":"Template","signatures":["scan(source)"],"summary":"An ERB-shaped template scanner: template text in, a template AST out.\n\nOpt-in: nothing here is in scope until `using Template`.\n\nThis is the scanning stage only (see the Template proposal for the fuller design): it turns template source into a flat list of `Node`s: plain text, and the four kinds of `<% %>` region, plus whatever frontmatter tags sit ahead of the body. It does not evaluate anything and does not know Kex syntax; the text inside a hole is kept as-is, for a later stage to parse and lower into real Kex.\n\n```kex\nusing Template\n\nlet source = \"---\\nlayout: page\\nparams: [name, library: Bool]\\n---\\nHi <%= name %>!\"\nlet parsed = Template.scan(source).try\nparsed.frontmatter.get(\"layout\")   # => Just(Scalar(\"page\"))\nparsed.parameters                  # => [TemplateParam { name: \"name\", type: \"\" },\n                                    #     TemplateParam { name: \"library\", type: \"Bool\" }]\nparsed.nodes                       # => [Text(\"Hi \"), Interpolate(\"name\"), Text(\"!\")]\n```\n\n## Syntax\n\n```kex\n<%= expr %>     interpolate (escaping is a later stage's job)\n<%== expr %>    interpolate raw, no escaping\n<% ... %>       a Kex control region: `if`/`match` arms, block bodies, `let`\n<%# ... %>      comment, emits nothing\n<%- ... -%>     whitespace control: trims the line's leading indent before\n                the tag, and the newline right after it\n<%%             a literal `<%`, for a template that generates ERB-shaped\n                output itself\n```\n\n## Frontmatter\n\nA template file may open with a `---` line, generic `key: value` tags up to a closing `---` line, and then the body. This is metadata for whatever reads the template (a title, a layout name, a list of tags) and there is nothing template-specific about it: it is scanned the same generic way whatever key is used.\n\n```kex\n---\ntitle: Release notes\ntags: [changelog, public]\n---\n# <%= title %>\n```\n\nA template's parameters, when a later stage needs them declared rather than inferred, are just another frontmatter key: conventionally `params`, a list of names each with an optional `: Type`:\n\n```kex\n---\nparams: [name, library: Bool, dependencies: [Dependency]]\n---\n# <%= name %>\n```\n\nNothing in the scanner treats `params` specially while scanning: it is metadata like any other key, a `[a, b, c]` list same as `tags` above. `Parsed#parameters` is a convenience reader for it: it splits each entry on its first colon into a `TemplateParam { name, type }` (`type` is `\"\"` for a bare name), so a caller does not need to know the key name, match on `Tag` itself, or split each entry by hand. `type` is raw text, same as everywhere else in this module: turning `[Dependency]` into a real, resolved Kex type is later work, not this module's.","types":["String","[String]","Integer","Node","Bool","{String: Tag}","[Node]"],"urlPath":"template"},{"anchor":"type-node","kind":"type","line":72,"name":"Node","qualifiedName":"Template.Node","signatures":[],"summary":"One piece of a scanned template.\n\nHole and control content is carried as raw text: the Kex source that was between the delimiters, trimmed of surrounding whitespace. Turning that text into real, type-checked Kex expressions is later work; a `Node` only records what KIND of region it is and what text it held.","types":["String","String","String","String","String"],"urlPath":"template"},{"anchor":"type-tag","kind":"type","line":79,"name":"Tag","qualifiedName":"Template.Tag","signatures":[],"summary":"A frontmatter value: a plain scalar, or a `[a, b, c]` list.","types":["String","[String]"],"urlPath":"template"},{"anchor":"type-templateerror","kind":"type","line":83,"name":"TemplateError","qualifiedName":"Template.TemplateError","signatures":[],"summary":"Why a template's text could not be scanned, and where.","types":["Integer","String"],"urlPath":"template"},{"anchor":"record-templateparam","kind":"record","line":91,"name":"TemplateParam","qualifiedName":"Template.TemplateParam","signatures":[],"summary":"One entry from a `params: [...]` frontmatter list: a name, and its optional `: Type` annotation. `type` is raw text: `\"\"` for a bare name, never a resolved Kex type: the same way a frontmatter `Tag` is text and not a parsed value.","types":["String"],"urlPath":"template"},{"anchor":"record-tagscan","kind":"record","line":102,"name":"TagScan","qualifiedName":"Template.TagScan","signatures":[],"summary":"One scanned `<% ... %>` region: its node, and the whitespace trims its delimiters asked for. It is internal to scanning, not part of the public API, but declared at module scope rather than inside `private do`. Nested there, this record's fields lose their names under BEAM codegen and the value round-trips as an untagged tuple (`Undefined method: leftTrim for Tuple`), even though the same code is fine on the tree-walking interpreter.","types":["Node","Bool"],"urlPath":"template"},{"anchor":"record-parsed","kind":"record","line":109,"name":"Parsed","qualifiedName":"Template.Parsed","signatures":[],"summary":"A scanned template: its frontmatter tags, and its body as a node list.","types":["{String: Tag}","[Node]"],"urlPath":"template"},{"anchor":"make-parsed","kind":"make","line":114,"name":"Parsed","qualifiedName":"Parsed","signatures":[],"summary":"","types":[],"urlPath":"template"},{"anchor":"function-scan","kind":"function","line":141,"name":"scan","qualifiedName":"Template.scan","signatures":["scan(source)"],"summary":"Scans template source into a `Parsed` template, or says where it broke.","types":[],"urlPath":"template"},{"anchor":"function-describe","kind":"function","line":45,"name":"describe","qualifiedName":"describe","signatures":["describe : String -> Block<Void> -> Void"],"summary":"The built-in testing DSL: `describe`, `it`, `before`, `after`, and assertion helpers.\n\nEverything here is always in scope: no import, and no separate test runner. Write a test file and run it with `kex`:\n\n```kex\ndescribe \"arithmetic\" do\n  it \"adds numbers\" do\n    assert(1 ` 1 == 2)\n  end\n\n  it \"multiplies numbers\" do\n    Assert.equal(3 * 4, 12)\n  end\nend\n\n$ kex my_test.kex\narithmetic\n  ✓ adds numbers\n  ✓ multiplies numbers\n```\n\nOutput uses ✓ / ✗ markers and nests by `describe` depth. A failing `assert` marks its test failed and moves on; the rest of the suite still runs.\n\nA file named `<name>.spec.kex` automatically loads the declarations of `<name>.kex` beside it, so a spec needs no import and no `main` wrapper.\n\nGroups related test cases under a label, and runs them.\n\nThe block is called immediately. `describe+ blocks nest, and the output is indented to match. This is a foul function: it prints.","types":["(String) -> (Block<Void>) -> Void"],"urlPath":"test"},{"anchor":"function-it","kind":"function","line":71,"name":"it","qualifiedName":"it","signatures":["it : String -> Block<Void> -> Void"],"summary":"Defines a single test case, and runs it.\n\nThe block runs, and anything thrown inside it: typically a failed `assert`: marks the test failed without aborting the rest of the suite. This is a foul function: it prints.","types":["(String) -> (Block<Void>) -> Void"],"urlPath":"test"},{"anchor":"function-before","kind":"function","line":92,"name":"before","qualifiedName":"before","signatures":["before : Block<Void> -> Void","before : Atom -> Block<Void> -> Void"],"summary":"Registers setup to run before the current group's tests.\n\n`before { ... }` and `before(:each) { ... }` run before every test in the group; `before(:all) { ... }` runs once before the group's first test, following RSpec's scope convention.","types":["(Block<Void>) -> Void","(Atom) -> (Block<Void>) -> Void"],"urlPath":"test"},{"anchor":"function-after","kind":"function","line":129,"name":"after","qualifiedName":"after","signatures":["after : Block<Void> -> Void","after : Atom -> Block<Void> -> Void"],"summary":"Registers cleanup to run after the current group's tests.\n\nDefaults to `:each`. Cleanup is unconditional: it runs whether the test passed or failed, and inner per-test hooks run before outer hooks.","types":["(Block<Void>) -> Void","(Atom) -> (Block<Void>) -> Void"],"urlPath":"test"},{"anchor":"function-assert","kind":"function","line":156,"name":"assert","qualifiedName":"assert","signatures":["assert : Bool -> Bool","assert : Bool -> String -> Bool"],"summary":"Fails the enclosing `it` when `value` is falsy.\n\nThe primitive every other assertion is built on. Prefer the `Assert` helpers where one fits: they report what was expected and what arrived, which a bare `assert` cannot.","types":["(Bool) -> Bool","(Bool) -> (String) -> Bool"],"urlPath":"test"},{"anchor":"module-assert","kind":"module","line":181,"name":"Assert","qualifiedName":"Assert","signatures":["equal(actual, expected)","notEqual(actual, expected)","truthy(value)","falsy(value)","some(value)","none(value)","ok(value)","error(value)"],"summary":"Focused assertions, each reporting what was expected and what arrived.\n\nThese are ordinary Kex stdlib functions layered on the primitive `assert`, so adding another helper does not require compiler or runtime work.\n\n```kex\nAssert.equal(\"hi\".upperCase, \"HI\")\nAssert.some(users.first)\nAssert.ok(Integer.parse(\"42\"))\n```","types":[],"urlPath":"test"},{"anchor":"function-equal","kind":"function","line":195,"name":"equal","qualifiedName":"Assert.equal","signatures":["equal(actual, expected)"],"summary":"Fails unless `actual` equals `expected`, reporting both.\n\nThe assertion to reach for by default: a failure tells you what arrived, which `assert(a == b)` does not.","types":[],"urlPath":"test"},{"anchor":"function-notequal","kind":"function","line":205,"name":"notEqual","qualifiedName":"Assert.notEqual","signatures":["notEqual(actual, expected)"],"summary":"Fails when `actual` equals `expected`.","types":[],"urlPath":"test"},{"anchor":"function-truthy","kind":"function","line":215,"name":"truthy","qualifiedName":"Assert.truthy","signatures":["truthy(value)"],"summary":"Fails unless `value` is truthy: anything except `false`, `None` and `()`.","types":[],"urlPath":"test"},{"anchor":"function-falsy","kind":"function","line":224,"name":"falsy","qualifiedName":"Assert.falsy","signatures":["falsy(value)"],"summary":"Fails unless `value` is falsy: `false`, `None` or `()`.","types":[],"urlPath":"test"},{"anchor":"function-some","kind":"function","line":234,"name":"some","qualifiedName":"Assert.some","signatures":["some(value)"],"summary":"Fails unless `value` is a `Just`.","types":[],"urlPath":"test"},{"anchor":"function-none","kind":"function","line":244,"name":"none","qualifiedName":"Assert.none","signatures":["none(value)"],"summary":"Fails unless `value` is `None`.","types":[],"urlPath":"test"},{"anchor":"function-ok","kind":"function","line":253,"name":"ok","qualifiedName":"Assert.ok","signatures":["ok(value)"],"summary":"Fails unless `value` is an `Ok`.","types":[],"urlPath":"test"},{"anchor":"function-error","kind":"function","line":262,"name":"error","qualifiedName":"Assert.error","signatures":["error(value)"],"summary":"Fails unless `value` is an `Error`.","types":[],"urlPath":"test"},{"anchor":"type-weekday","kind":"type","line":44,"name":"Weekday","qualifiedName":"Weekday","signatures":[],"summary":"Calendar dates, wall-clock times, and instants.\n\nThree civil types, each a plain record:\n\n```kex\nDate      a calendar day, no time and no zone       (2026-07-30)\nTime      a time of day, no date and no zone        (14:03:00)\nDateTime  both, plus a fixed offset from UTC        (2026-07-30T14:03:00`02:00)\n```\n\nTwo span types connect them, and which one you want depends on whether the calendar gets a say:\n\n```kex\nDuration  fixed elapsed time, a count of seconds   (36.hours, 10.days)\nPeriod    a calendar step, resolved by the calendar (1.months, 2.years)\n```\n\n`36.hours` is always 129600 seconds; `1.months` is however long that particular month turns out to be. So `date ` 1.months` clamps January 31st to the last day of February, while `date ` 30.days` counts thirty days.\n\nA time `Measure` such as `5.sec` is a third thing and deliberately NOT a Duration: a Measure describes a measurement, a Duration describes elapsed time. The plural `5.seconds` builds the Duration.\n\nValues are built through their own module and used through methods:\n\n```kex\nlet due = Date.of(2026, 7, 30).try           # Result<Date, TimeError>\ndue.weekday.name                             # \"Thursday\"\n(due ` 10.days).iso                          # \"2026-08-09\"\n(due ` 1.months).iso                         # \"2026-08-30\"\nTime.now().iso                               # \"2026-07-30T14:03:00`02:00\"\n```\n\nAnything that reads the clock is mockable: see the test clock section in `module Time` for `Time.frozenAt`.\n\nZones are fixed offsets: UTC, an explicit `+02:00`, or whatever this machine's zone resolves to at a given instant. Named IANA zones and their DST rules are not modeled: `Time.now()` asks the host for the offset in effect at that moment, so it is right now, but it cannot say what the offset WILL be for some future local time.\n\nThe records and the two ADTs stay at file level so `make` blocks, callers, and every module here can see them.\n\nWeekday names in ISO order (Monday is day 1).","types":[],"urlPath":"time"},{"anchor":"type-timeerror","kind":"type","line":48,"name":"TimeError","qualifiedName":"TimeError","signatures":[],"summary":"A field out of range, or text that is not a date/time.","types":["Integer","Integer","Integer","Integer","Integer","Integer","String"],"urlPath":"time"},{"anchor":"record-date","kind":"record","line":61,"name":"Date","qualifiedName":"Date","signatures":[],"summary":"A calendar day: a year, a month and a day, with no time and no zone.\n\n```kex\nlet due = Date.of(2026, 7, 30).try\ndue.iso              # => \"2026-07-30\"\ndue.weekday.name     # => \"Thursday\"\n(due ` 10.days).iso  # => \"2026-08-09\"\n```\n\nBuild one with `Date.of+, which validates, rather than with the record literal, which does not.","types":["Integer"],"urlPath":"time"},{"anchor":"record-time","kind":"record","line":80,"name":"Time","qualifiedName":"Time","signatures":[],"summary":"A time of day, with no date and no zone.\n\n```kex\nlet t = Time.of(14, 3, 0).try\nt.iso                  # => \"14:03:00\"\n(t ` 2.hours).iso      # => \"16:03:00\"\n```\n\nArithmetic wraps within the day: there is no date to carry into. Reach for `DateTime+ when the day rolling over matters.","types":["Integer"],"urlPath":"time"},{"anchor":"record-datetime","kind":"record","line":102,"name":"DateTime","qualifiedName":"DateTime","signatures":[],"summary":"An instant: a calendar date, a time of day, and a fixed offset from UTC.\n\n```kex\nlet m = DateTime.parse(\"2026-07-30T14:03:00`02:00\").try\nm.iso        # => \"2026-07-30T14:03:00`02:00\"\nm.utc.iso    # => \"2026-07-30T12:03:00Z\"\n```\n\nTwo `DateTime` values that name the same instant compare equal whatever offsets they are written at: comparison goes through `epochSeconds`.","types":["Date","Time","Duration"],"urlPath":"time"},{"anchor":"record-period","kind":"record","line":123,"name":"Period","qualifiedName":"Period","signatures":[],"summary":"A calendar span. Months and years have no fixed length: February is 28 days or 29, a year 365 or 366, so they cannot live in a `Duration`, which is a count of seconds and nothing else. A Period carries the calendar fields themselves and lets the calendar resolve them:\n\n```kex\nDate.of(2026, 1, 31).try ` 1.months        # 2026-02-28, not 2026-03-03\nDate.of(2024, 2, 29).try ` 1.years         # 2025-02-28\n```\n\nUse a Duration for elapsed time (`36.hours` is always 129600 seconds) and a Period for calendar steps (`1.months` is however long that month is).","types":["Integer"],"urlPath":"time"},{"anchor":"module-time","kind":"module","line":136,"name":"Time","qualifiedName":"Time","signatures":["of(hour, minute, second)","midnight()","fromSecondsSinceMidnight(count)","parse(text)","now()","utcNow()","parseOffset(text)","nanosOf(moment)","settable?(moment)","freeze(moment)","travel(moment)","frozenAt(moment, body)","travellingFrom(moment, body)","release()","controlled?()","frozen?()","leapYear?(year)","daysInMonth(year, month)","daysInValidMonth(year, month)","daysFromCivil(year, month, day)","civilFromDays(epochDay)","weekdayFromEpochDay(epochDay)","weekdayNumber(@Monday)","weekdayName(@Monday)","errorMessage(@InvalidDate(y, m, d))","formatDate(value)","formatDateTime(value)","formatTime(value)","formatFraction(nanosecond)","formatOffset(offset)","withNanosecond(moment, nanosecond)","floorDiv(value, divisor)","truncatedBy(seconds, unit)","pad2(value)","padTo(value, width)","padYear(value)","digitsIn(fragment, whole)","digitsToInteger(text)","parseFraction(text, whole)","splitOffset(text)"],"summary":"Building times of day, controlling the clock in tests, and the calendar arithmetic the rest of this file is written on.","types":["?"],"urlPath":"time"},{"anchor":"function-of","kind":"function","line":156,"name":"of","qualifiedName":"Time.of","signatures":["of(hour, minute, second)"],"summary":"Builds a validated time of day.\n\nEvery field is range-checked, so a `Time` you hold is always a real time. Leap seconds are not modeled, so a second of 60 is rejected.","types":[],"urlPath":"time"},{"anchor":"function-midnight","kind":"function","line":175,"name":"midnight","qualifiedName":"Time.midnight","signatures":["midnight()"],"summary":"Midnight, 00:00:00. The start of a day.","types":[],"urlPath":"time"},{"anchor":"function-fromsecondssincemidnight","kind":"function","line":194,"name":"fromSecondsSinceMidnight","qualifiedName":"Time.fromSecondsSinceMidnight","signatures":["fromSecondsSinceMidnight(count)"],"summary":"Builds a time of day from a count of seconds since midnight.\n\nWraps, so 86400 is midnight again and -1 is 23:59:59, which is what makes it total where `Time.of` is fallible.\n\nDeclared before the two-argument form: the interpreter resolves an overloaded module function to its LAST definition regardless of arity, so a delegating overload has to come first or it recurses into itself.","types":[],"urlPath":"time"},{"anchor":"function-parse","kind":"function","line":212,"name":"parse","qualifiedName":"Time.parse","signatures":["parse(text)"],"summary":"Parses an ISO 8601 time of day.\n\nAccepts `14:03`, `14:03:00`, or `14:03:00.123456789`. Anything else is `InvalidFormat`.","types":[],"urlPath":"time"},{"anchor":"function-now","kind":"function","line":232,"name":"now","qualifiedName":"Time.now","signatures":["now()"],"summary":"The current time of day, in this machine's zone.\n\nReads the same clock primitive everything else here does, so it is pinned by `Time.frozenAt` in a test.","types":[],"urlPath":"time"},{"anchor":"function-utcnow","kind":"function","line":243,"name":"utcNow","qualifiedName":"Time.utcNow","signatures":["utcNow()"],"summary":"The current time of day in UTC, whatever this machine's zone is.","types":[],"urlPath":"time"},{"anchor":"function-parseoffset","kind":"function","line":259,"name":"parseOffset","qualifiedName":"Time.parseOffset","signatures":["parseOffset(text)"],"summary":"Parses an ISO 8601 zone designator into an offset.\n\nAccepts `Z`, ``02:00`, `-05:30`, or the empty string (all meaning UTC for the first and last).","types":[],"urlPath":"time"},{"anchor":"function-nanosof","kind":"function","line":293,"name":"nanosOf","qualifiedName":"Time.nanosOf","signatures":["nanosOf(moment)"],"summary":"Anything that asks what time it is: `Time.now`, `Date.today`, `DateTime.utcNow`: reads one primitive, so pinning that primitive pins the whole calendar. This is what makes code that calls `Date.today()` testable: freeze the clock, assert against a date you chose.\n\n```kex\nTime.freeze(DateTime.parse(\"2026-07-30T14:03:00Z\").try)\nDate.today().iso                            # \"2026-07-30\": always\nTime.release()\n```\n\nThe clock is global, not per-process: a frozen clock stays frozen inside spawned processes, which is the only behavior that matches a real one. `release` is not automatic, so a test that freezes must also release: otherwise every later test in the run inherits the frozen clock.\n\nNanoseconds since the Unix epoch for a civil datetime. A plain function rather than a `DateTime` method: on BEAM a method named `epochNanos` flattens onto the same name as the `DateTime.epochNanos()` module function, and the arity-0 one wins: silently, answering for the host clock instead of for `moment`.","types":[],"urlPath":"time"},{"anchor":"constant-clock_min_nanos","kind":"constant","line":303,"name":"CLOCK_MIN_NANOS","qualifiedName":"Time.CLOCK_MIN_NANOS","signatures":[],"summary":"The clock counts nanoseconds in a 64-bit integer, on both backends and in the host clock they stand in for. That is the whole of the instants it can name: 1677-09-21 to 2262-04-11. A Kex Integer keeps going past that: it promotes to arbitrary precision, so a date outside the range produces a number the clock cannot hold, and the check below is what stops it being truncated into some other instant entirely.","types":["?"],"urlPath":"time"},{"anchor":"constant-clock_max_nanos","kind":"constant","line":304,"name":"CLOCK_MAX_NANOS","qualifiedName":"Time.CLOCK_MAX_NANOS","signatures":[],"summary":"","types":["?"],"urlPath":"time"},{"anchor":"function-settable?","kind":"function","line":306,"name":"settable?","qualifiedName":"Time.settable?","signatures":["settable?(moment)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-freeze","kind":"function","line":328,"name":"freeze","qualifiedName":"Time.freeze","signatures":["freeze(moment)"],"summary":"Pins the clock: every reading returns this exact instant until `release`.\n\nThis is what makes code that calls `Date.today()` testable. Returns the moment it pinned, so `Time.freeze(m).try` both sets the clock and fails loudly on an instant the clock cannot represent.\n\nPrefer `Time.frozenAt`, which releases for you: a test that fails between a `freeze` and its `release` leaves the clock frozen for everything after it.","types":[],"urlPath":"time"},{"anchor":"function-travel","kind":"function","line":346,"name":"travel","qualifiedName":"Time.travel","signatures":["travel(moment)"],"summary":"Moves the clock to an instant and lets it run from there.\n\nReadings advance normally, they just start somewhere else. Use this over `freeze` when the code under test measures elapsed time: a frozen clock makes every interval zero.","types":[],"urlPath":"time"},{"anchor":"function-frozenat","kind":"function","line":372,"name":"frozenAt","qualifiedName":"Time.frozenAt","signatures":["frozenAt(moment, body)"],"summary":"Freezes the clock for the length of `body`, then releases it.\n\nThis is the form to reach for: `freeze` and `release` have to be paired by hand, and a test that returns early (or fails an assertion) between them leaves the clock frozen for every test that runs after it.\n\nResult carries whatever `body` returned. An instant the clock cannot represent is an Error, and then the clock is never touched and the body never runs.\n\nNot nestable: `release` restores the HOST clock, not whatever control was in effect on entry, so an inner scope ending un-freezes the outer one too.","types":[],"urlPath":"time"},{"anchor":"function-travellingfrom","kind":"function","line":393,"name":"travellingFrom","qualifiedName":"Time.travellingFrom","signatures":["travellingFrom(moment, body)"],"summary":"Runs `body` with the clock started at `moment`, then releases it.\n\nThe same scoping as `frozenAt`, for `travel`: readings start at `moment` and advance normally, and the clock is released when `body` ends.","types":[],"urlPath":"time"},{"anchor":"function-release","kind":"function","line":413,"name":"release","qualifiedName":"Time.release","signatures":["release()"],"summary":"Returns the clock to the host's.\n\nNot automatic: a test that froze the clock must also release it, or every later test in the run inherits the frozen clock. `frozenAt` and `travellingFrom` do this for you.","types":[],"urlPath":"time"},{"anchor":"function-controlled?","kind":"function","line":423,"name":"controlled?","qualifiedName":"Time.controlled?","signatures":["controlled?()"],"summary":"Returns `true` while `freeze` or `travel` is in effect.","types":[],"urlPath":"time"},{"anchor":"function-frozen?","kind":"function","line":433,"name":"frozen?","qualifiedName":"Time.frozen?","signatures":["frozen?()"],"summary":"Returns `true` while `freeze` is in effect: not merely `travel`.","types":[],"urlPath":"time"},{"anchor":"function-leapyear?","kind":"function","line":450,"name":"leapYear?","qualifiedName":"Time.leapYear?","signatures":["leapYear?(year)"],"summary":"Public because the `make` blocks below live at file level and reach them by qualification; they are equally useful on their own.\n\nReturns `true` when `year` is a leap year in the proleptic Gregorian calendar.","types":[],"urlPath":"time"},{"anchor":"function-daysinmonth","kind":"function","line":474,"name":"daysInMonth","qualifiedName":"Time.daysInMonth","signatures":["daysInMonth(year, month)"],"summary":"The number of days in a month.\n\nYear first, matching `Date.of(year, month, day)` and every other date-shaped signature in this file.\n\nA month outside 1..12 has no answer, so this is a Result rather than an Integer: the old version fell through its month tests and returned 28, which quietly turned `Time.daysInMonth(1, 2026)`: the arguments the wrong way round: into a plausible-looking wrong number.","types":[],"urlPath":"time"},{"anchor":"function-daysinvalidmonth","kind":"function","line":490,"name":"daysInValidMonth","qualifiedName":"Time.daysInValidMonth","signatures":["daysInValidMonth(year, month)"],"summary":"The number of days in a month, with the range check already done.\n\nEvery caller inside this file has a month it built or validated itself. Use `daysInMonth` for a month that came from outside.","types":[],"urlPath":"time"},{"anchor":"function-daysfromcivil","kind":"function","line":512,"name":"daysFromCivil","qualifiedName":"Time.daysFromCivil","signatures":["daysFromCivil(year, month, day)"],"summary":"The number of days from 1970-01-01 to a calendar date, negative before it.\n\nHoward Hinnant's civil-calendar algorithms: exact across the whole proleptic Gregorian range, and they need only truncating integer division: the semantics Kex's `/` already has.","types":[],"urlPath":"time"},{"anchor":"function-civilfromdays","kind":"function","line":531,"name":"civilFromDays","qualifiedName":"Time.civilFromDays","signatures":["civilFromDays(epochDay)"],"summary":"The calendar date a count of days since 1970-01-01 lands on. The inverse of `daysFromCivil`.","types":[],"urlPath":"time"},{"anchor":"function-weekdayfromepochday","kind":"function","line":552,"name":"weekdayFromEpochDay","qualifiedName":"Time.weekdayFromEpochDay","signatures":["weekdayFromEpochDay(epochDay)"],"summary":"The weekday a count of days since 1970-01-01 falls on.","types":[],"urlPath":"time"},{"anchor":"function-weekdaynumber","kind":"function","line":572,"name":"weekdayNumber","qualifiedName":"Time.weekdayNumber","signatures":["weekdayNumber(@Monday)"],"summary":"The ISO number of a weekday: Monday is 1, Sunday is 7.\n\n`weekday.number` is the readable way to ask.","types":[],"urlPath":"time"},{"anchor":"function-weekdayname","kind":"function","line":589,"name":"weekdayName","qualifiedName":"Time.weekdayName","signatures":["weekdayName(@Monday)"],"summary":"The English name of a weekday.\n\n`weekday.name` is the readable way to ask.","types":[],"urlPath":"time"},{"anchor":"make-weekday","kind":"make","line":597,"name":"Weekday","qualifiedName":"Weekday","signatures":[],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-errormessage","kind":"function","line":646,"name":"errorMessage","qualifiedName":"Time.errorMessage","signatures":["errorMessage(@InvalidDate(y, m, d))"],"summary":"Renders a `TimeError` as a sentence for the user.\n\nA plain function rather than an `Errorable` implementation: a `message` method here joins the same BEAM dispatcher as ParseError's `message` FIELD and breaks it (spec/record_field_method_collision.kex).","types":[],"urlPath":"time"},{"anchor":"function-formatdate","kind":"function","line":651,"name":"formatDate","qualifiedName":"Time.formatDate","signatures":["formatDate(value)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-formatdatetime","kind":"function","line":653,"name":"formatDateTime","qualifiedName":"Time.formatDateTime","signatures":["formatDateTime(value)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-formattime","kind":"function","line":655,"name":"formatTime","qualifiedName":"Time.formatTime","signatures":["formatTime(value)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-formatfraction","kind":"function","line":665,"name":"formatFraction","qualifiedName":"Time.formatFraction","signatures":["formatFraction(nanosecond)"],"summary":"Fractional seconds, in the 3/6/9-digit groupings ISO 8601 output conventionally uses, whichever is the shortest that loses nothing. A whole second renders no fraction at all, so `14:03:00` is unchanged.\n\nWithout this the nanosecond field was kept on the value and compared, but never rendered: `Time.parse(\"14:03:00.5\")` and `Time.parse(\"14:03:00\")` produced different values that printed identically, and every parse/format round-trip silently dropped sub-second precision.","types":[],"urlPath":"time"},{"anchor":"function-formatoffset","kind":"function","line":673,"name":"formatOffset","qualifiedName":"Time.formatOffset","signatures":["formatOffset(offset)"],"summary":"±HH:MM, the shape an ISO 8601 offset takes. UTC renders as \"Z\".","types":[],"urlPath":"time"},{"anchor":"function-withnanosecond","kind":"function","line":682,"name":"withNanosecond","qualifiedName":"Time.withNanosecond","signatures":["withNanosecond(moment, nanosecond)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-floordiv","kind":"function","line":693,"name":"floorDiv","qualifiedName":"Time.floorDiv","signatures":["floorDiv(value, divisor)"],"summary":"Kex's `/` truncates toward zero; instants before the epoch need the floor.","types":[],"urlPath":"time"},{"anchor":"function-truncatedby","kind":"function","line":700,"name":"truncatedBy","qualifiedName":"Time.truncatedBy","signatures":["truncatedBy(seconds, unit)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-pad2","kind":"function","line":705,"name":"pad2","qualifiedName":"Time.pad2","signatures":["pad2(value)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-padto","kind":"function","line":713,"name":"padTo","qualifiedName":"Time.padTo","signatures":["padTo(value, width)"],"summary":"Left-pad with zeros to a fixed width. A value already that wide is left alone rather than truncated: losing digits would be worse than a field one character too long.","types":[],"urlPath":"time"},{"anchor":"function-padyear","kind":"function","line":723,"name":"padYear","qualifiedName":"Time.padYear","signatures":["padYear(value)"],"summary":"Years keep four digits where they fit; ISO 8601 has no fixed spelling beyond that, so wider years render as-is.","types":[],"urlPath":"time"},{"anchor":"function-digitsin","kind":"function","line":731,"name":"digitsIn","qualifiedName":"Time.digitsIn","signatures":["digitsIn(fragment, whole)"],"summary":"Digits, with the failure reported against the WHOLE input rather than the fragment that failed: `Time.parse(\"2:03 pm\")` should complain about \"2:03 pm\", not about \"03 pm\".","types":[],"urlPath":"time"},{"anchor":"function-digitstointeger","kind":"function","line":738,"name":"digitsToInteger","qualifiedName":"Time.digitsToInteger","signatures":["digitsToInteger(text)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"function-parsefraction","kind":"function","line":747,"name":"parseFraction","qualifiedName":"Time.parseFraction","signatures":["parseFraction(text, whole)"],"summary":"\".5\" is 500000000ns: the digits are padded out to nanosecond scale.","types":[],"urlPath":"time"},{"anchor":"function-splitoffset","kind":"function","line":759,"name":"splitOffset","qualifiedName":"Time.splitOffset","signatures":["splitOffset(text)"],"summary":"Splits \"14:03:00+02:00\" into its time and offset halves. A missing offset reads as UTC, matching what a zero offset formats back to.","types":[],"urlPath":"time"},{"anchor":"module-date","kind":"module","line":772,"name":"Date","qualifiedName":"Date","signatures":["of(year, month, day)","fromEpochDay(day)","parse(text)","now()","today()","tomorrow()","yesterday()","utcNow()","utcToday()"],"summary":"Building calendar dates, and asking what today is.","types":[],"urlPath":"time"},{"anchor":"function-of","kind":"function","line":792,"name":"of","qualifiedName":"Date.of","signatures":["of(year, month, day)"],"summary":"Builds a validated calendar date.\n\nThe month and the day are both range-checked, and the day is checked against that month's actual length, so February 30th is an `Error`, and a `Date` you hold is always a real day. The record literal `Date { ... }` bypasses this, so prefer it for anything derived from input.","types":[],"urlPath":"time"},{"anchor":"function-fromepochday","kind":"function","line":808,"name":"fromEpochDay","qualifiedName":"Date.fromEpochDay","signatures":["fromEpochDay(day)"],"summary":"The calendar date a count of days since 1970-01-01 lands on, negative before it.","types":[],"urlPath":"time"},{"anchor":"function-parse","kind":"function","line":822,"name":"parse","qualifiedName":"Date.parse","signatures":["parse(text)"],"summary":"Parses an ISO 8601 calendar date, `2026-07-30`.\n\nThe result is validated as well as parsed, so a well-formed but impossible date is `InvalidDate` rather than `InvalidFormat`.","types":[],"urlPath":"time"},{"anchor":"function-now","kind":"function","line":841,"name":"now","qualifiedName":"Date.now","signatures":["now()"],"summary":"Today's date, in this machine's zone.\n\n`Date.today()` reads better in most code; `now` exists so every type in this file answers the same question the same way.","types":[],"urlPath":"time"},{"anchor":"function-today","kind":"function","line":860,"name":"today","qualifiedName":"Date.today","signatures":["today()"],"summary":"Today's date, in this machine's zone.\n\nPinned by `Time.frozenAt` in a test, like everything else that reads the clock.","types":[],"urlPath":"time"},{"anchor":"function-tomorrow","kind":"function","line":871,"name":"tomorrow","qualifiedName":"Date.tomorrow","signatures":["tomorrow()"],"summary":"The day after today, in this machine's zone.","types":[],"urlPath":"time"},{"anchor":"function-yesterday","kind":"function","line":882,"name":"yesterday","qualifiedName":"Date.yesterday","signatures":["yesterday()"],"summary":"The day before today, in this machine's zone.","types":[],"urlPath":"time"},{"anchor":"function-utcnow","kind":"function","line":893,"name":"utcNow","qualifiedName":"Date.utcNow","signatures":["utcNow()"],"summary":"Today's date in UTC, whatever this machine's zone is.","types":[],"urlPath":"time"},{"anchor":"function-utctoday","kind":"function","line":905,"name":"utcToday","qualifiedName":"Date.utcToday","signatures":["utcToday()"],"summary":"Today's date in UTC. The same as `Date.utcNow()`, under the name that reads better.","types":[],"urlPath":"time"},{"anchor":"module-datetime","kind":"module","line":912,"name":"DateTime","qualifiedName":"DateTime","signatures":["of(date, time, offset)","fromEpochSeconds(count)","parse(text)","now()","utcNow()","epochNanos()"],"summary":"Building instants, and asking what time it is now.","types":[],"urlPath":"time"},{"anchor":"function-of","kind":"function","line":928,"name":"of","qualifiedName":"DateTime.of","signatures":["of(date, time, offset)"],"summary":"Combines a date, a time of day and a UTC offset into an instant.","types":[],"urlPath":"time"},{"anchor":"function-fromepochseconds","kind":"function","line":947,"name":"fromEpochSeconds","qualifiedName":"DateTime.fromEpochSeconds","signatures":["fromEpochSeconds(count)"],"summary":"The instant a count of seconds since the Unix epoch names, rendered at UTC or at the offset you give.\n\nDeclared before the two-argument form: the interpreter resolves an overloaded module function to its LAST definition regardless of arity, so a delegating overload has to come first or it recurses into itself.","types":[],"urlPath":"time"},{"anchor":"function-parse","kind":"function","line":975,"name":"parse","qualifiedName":"DateTime.parse","signatures":["parse(text)"],"summary":"Parses an ISO 8601 instant.\n\nAccepts `2026-07-30T14:03:00`02:00`, the same with `Z`, or a bare civil datetime with no zone at all, which is read as UTC.","types":[],"urlPath":"time"},{"anchor":"function-now","kind":"function","line":1003,"name":"now","qualifiedName":"DateTime.now","signatures":["now()"],"summary":"The current instant, in this machine's zone as it stands right now.\n\nThe offset is the one in effect at this instant, so it is right today. Named IANA zones are not modeled, so it cannot say what the offset WILL be for some future local time.","types":[],"urlPath":"time"},{"anchor":"function-utcnow","kind":"function","line":1019,"name":"utcNow","qualifiedName":"DateTime.utcNow","signatures":["utcNow()"],"summary":"The current instant, at UTC.\n\nThe form to prefer when the value is stored, compared or transmitted: there is no zone to disagree about.","types":[],"urlPath":"time"},{"anchor":"function-epochnanos","kind":"function","line":1036,"name":"epochNanos","qualifiedName":"DateTime.epochNanos","signatures":["epochNanos()"],"summary":"Nanoseconds since the Unix epoch, straight from the clock.\n\nThe rawest reading available, and the right one for measuring a short interval: no calendar work happens on the way.","types":[],"urlPath":"time"},{"anchor":"make-integer","kind":"make","line":1044,"name":"Integer","qualifiedName":"Integer","signatures":[],"summary":"The plural spellings build a Duration; the singular ones from units.kex build a time Measure. `5.seconds` is an elapsed span, `5.sec` a measurement.","types":[],"urlPath":"time"},{"anchor":"make-float","kind":"make","line":1125,"name":"Float","qualifiedName":"Float","signatures":[],"summary":"The same `Duration` constructors on `Float`, for fractional spans: `1.5.hours`, `0.25.seconds`.","types":[],"urlPath":"time"},{"anchor":"make-duration","kind":"make","line":1163,"name":"Duration","qualifiedName":"Duration","signatures":["+(other)","-(other)","*(factor)","/(divisor)","shorterThan?(other)","longerThan?(other)","compareTo(other)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"fn-+","kind":"function","line":1171,"name":"+","qualifiedName":"Duration.+","signatures":["+(other)"],"summary":"Adds two spans.","types":[],"urlPath":"time"},{"anchor":"fn--","kind":"function","line":1180,"name":"-","qualifiedName":"Duration.-","signatures":["-(other)"],"summary":"Subtracts a span. The result may be negative.","types":[],"urlPath":"time"},{"anchor":"fn-*","kind":"function","line":1200,"name":"*","qualifiedName":"Duration.*","signatures":["*(factor)"],"summary":"Multiplies the span by a plain number.\n\n`3 * 1.days` is not the same call because the receiver has to be the Duration, so it is spelled `1.days * 3`.","types":[],"urlPath":"time"},{"anchor":"fn--","kind":"function","line":1209,"name":"/","qualifiedName":"Duration./","signatures":["/(divisor)"],"summary":"Divides the span by a plain number.","types":[],"urlPath":"time"},{"anchor":"fn-shorterthan?","kind":"function","line":1261,"name":"shorterThan?","qualifiedName":"Duration.shorterThan?","signatures":["shorterThan?(other)"],"summary":"Returns `true` when this span is shorter than `other`.\n\nNamed for length rather than for order: `before?`/`after?` are about when something happened, and a Duration is not a point in time.","types":[],"urlPath":"time"},{"anchor":"fn-longerthan?","kind":"function","line":1270,"name":"longerThan?","qualifiedName":"Duration.longerThan?","signatures":["longerThan?(other)"],"summary":"Returns `true` when this span is longer than `other`.","types":[],"urlPath":"time"},{"anchor":"fn-compareto","kind":"function","line":1339,"name":"compareTo","qualifiedName":"Duration.compareTo","signatures":["compareTo(other)"],"summary":"Orders this span against another by length.\n\nDelegates to `Number.compare` (algebra.kex), which orders the two Float second counts.","types":[],"urlPath":"time"},{"anchor":"module-duration","kind":"module","line":1343,"name":"Duration","qualifiedName":"Duration","signatures":["zero()","milliseconds(count)","seconds(count)","minutes(count)","hours(count)","days(count)","weeks(count)","utcOffset(hours, minutes)"],"summary":"Building elapsed spans, and UTC offsets.","types":[],"urlPath":"time"},{"anchor":"function-zero","kind":"function","line":1351,"name":"zero","qualifiedName":"Duration.zero","signatures":["zero()"],"summary":"A span of no time at all. Also the UTC offset.","types":[],"urlPath":"time"},{"anchor":"function-milliseconds","kind":"function","line":1360,"name":"milliseconds","qualifiedName":"Duration.milliseconds","signatures":["milliseconds(count)"],"summary":"A span of `count` milliseconds.","types":[],"urlPath":"time"},{"anchor":"function-seconds","kind":"function","line":1369,"name":"seconds","qualifiedName":"Duration.seconds","signatures":["seconds(count)"],"summary":"A span of `count` seconds.","types":[],"urlPath":"time"},{"anchor":"function-minutes","kind":"function","line":1378,"name":"minutes","qualifiedName":"Duration.minutes","signatures":["minutes(count)"],"summary":"A span of `count` minutes.","types":[],"urlPath":"time"},{"anchor":"function-hours","kind":"function","line":1387,"name":"hours","qualifiedName":"Duration.hours","signatures":["hours(count)"],"summary":"A span of `count` hours.","types":[],"urlPath":"time"},{"anchor":"function-days","kind":"function","line":1396,"name":"days","qualifiedName":"Duration.days","signatures":["days(count)"],"summary":"A span of `count` days, each a fixed 86400 seconds.","types":[],"urlPath":"time"},{"anchor":"function-weeks","kind":"function","line":1405,"name":"weeks","qualifiedName":"Duration.weeks","signatures":["weeks(count)"],"summary":"A span of `count` weeks, each a fixed 604800 seconds.","types":[],"urlPath":"time"},{"anchor":"function-utcoffset","kind":"function","line":1423,"name":"utcOffset","qualifiedName":"Duration.utcOffset","signatures":["utcOffset(hours, minutes)"],"summary":"A whole-minute UTC offset, the only kind ISO 8601 can spell.\n\nA negative hour or minute puts the whole offset west of UTC, so `utcOffset(-5, 30)` is five and a half hours behind UTC, not four and a half.","types":[],"urlPath":"time"},{"anchor":"module-period","kind":"module","line":1432,"name":"Period","qualifiedName":"Period","signatures":["zero()","of(years, months, days)","years(count)","months(count)","days(count)","weeks(count)"],"summary":"Building calendar spans.","types":[],"urlPath":"time"},{"anchor":"function-zero","kind":"function","line":1440,"name":"zero","qualifiedName":"Period.zero","signatures":["zero()"],"summary":"A span of nothing.","types":[],"urlPath":"time"},{"anchor":"function-of","kind":"function","line":1451,"name":"of","qualifiedName":"Period.of","signatures":["of(years, months, days)"],"summary":"A span of the given years, months and days together.","types":[],"urlPath":"time"},{"anchor":"function-years","kind":"function","line":1460,"name":"years","qualifiedName":"Period.years","signatures":["years(count)"],"summary":"A span of `count` calendar years.","types":[],"urlPath":"time"},{"anchor":"function-months","kind":"function","line":1469,"name":"months","qualifiedName":"Period.months","signatures":["months(count)"],"summary":"A span of `count` calendar months.","types":[],"urlPath":"time"},{"anchor":"function-days","kind":"function","line":1478,"name":"days","qualifiedName":"Period.days","signatures":["days(count)"],"summary":"A span of `count` days, as a calendar step.","types":[],"urlPath":"time"},{"anchor":"function-weeks","kind":"function","line":1487,"name":"weeks","qualifiedName":"Period.weeks","signatures":["weeks(count)"],"summary":"A span of `count` weeks, recorded as that many times seven days.","types":[],"urlPath":"time"},{"anchor":"make-period","kind":"make","line":1490,"name":"Period","qualifiedName":"Period","signatures":["inspectValue(colors)","+(other)","-(other)","*(factor)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"fn-inspectvalue","kind":"function","line":1495,"name":"inspectValue","qualifiedName":"Period.inspectValue","signatures":["inspectValue(colors)"],"summary":"Renders the period structurally, for debugging output.","types":[],"urlPath":"time"},{"anchor":"fn-+","kind":"function","line":1513,"name":"+","qualifiedName":"Period.+","signatures":["+(other)"],"summary":"Adds two calendar spans, field by field.","types":[],"urlPath":"time"},{"anchor":"fn--","kind":"function","line":1522,"name":"-","qualifiedName":"Period.-","signatures":["-(other)"],"summary":"Subtracts a calendar span, field by field. Fields may go negative.","types":[],"urlPath":"time"},{"anchor":"fn-*","kind":"function","line":1539,"name":"*","qualifiedName":"Period.*","signatures":["*(factor)"],"summary":"Multiplies every field by `factor`.","types":[],"urlPath":"time"},{"anchor":"make-date","kind":"make","line":1597,"name":"Date","qualifiedName":"Date","signatures":["inspectValue(colors)","+(span)","-(span)","addDays(count)","addWeeks(count)","addMonths(count)","addYears(count)","daysUntil(other)","until(other)","monthsUntil(other)","yearsUntil(other)","before?(other)","after?(other)","compareTo(other)","at(time, offset)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"fn-inspectvalue","kind":"function","line":1602,"name":"inspectValue","qualifiedName":"Date.inspectValue","signatures":["inspectValue(colors)"],"summary":"Renders the date structurally, for debugging output.","types":[],"urlPath":"time"},{"anchor":"fn-+","kind":"function","line":1676,"name":"+","qualifiedName":"Date.+","signatures":["+(span)"],"summary":"Advances the date by a fixed span, whole days only.\n\nA Duration with a sub-day remainder truncates toward zero, so `date ` 36.hours` advances exactly one day. Use a `Period` when the calendar should get a say.","types":[],"urlPath":"time"},{"anchor":"fn--","kind":"function","line":1685,"name":"-","qualifiedName":"Date.-","signatures":["-(span)"],"summary":"Moves the date back by a fixed span, whole days only.","types":[],"urlPath":"time"},{"anchor":"fn-adddays","kind":"function","line":1721,"name":"addDays","qualifiedName":"Date.addDays","signatures":["addDays(count)"],"summary":"The date `count` days later. A negative count moves backwards.","types":[],"urlPath":"time"},{"anchor":"fn-addweeks","kind":"function","line":1730,"name":"addWeeks","qualifiedName":"Date.addWeeks","signatures":["addWeeks(count)"],"summary":"The date `count` weeks later. A negative count moves backwards.","types":[],"urlPath":"time"},{"anchor":"fn-addmonths","kind":"function","line":1750,"name":"addMonths","qualifiedName":"Date.addMonths","signatures":["addMonths(count)"],"summary":"No `date.tomorrow`/`date.yesterday` methods: on BEAM a make-block method flattens onto the same name as the `Date.tomorrow()`/`Date.yesterday()` module functions above and one of the two has to win. The module functions win: `Date.tomorrow()` is the spelling people reach for, and `date.addDays(1)` already says the rest.\n\nThe date `count` calendar months later, with the day clamped into the target month.\n\nOne month after January 31st is the last day of February, not March 3rd. A negative count moves backwards.","types":[],"urlPath":"time"},{"anchor":"fn-addyears","kind":"function","line":1767,"name":"addYears","qualifiedName":"Date.addYears","signatures":["addYears(count)"],"summary":"The date `count` calendar years later, with the day clamped: February 29th plus one year is February 28th.","types":[],"urlPath":"time"},{"anchor":"fn-daysuntil","kind":"function","line":1829,"name":"daysUntil","qualifiedName":"Date.daysUntil","signatures":["daysUntil(other)"],"summary":"Whole days from this date to `other`, negative when `other` is earlier.","types":[],"urlPath":"time"},{"anchor":"fn-until","kind":"function","line":1838,"name":"until","qualifiedName":"Date.until","signatures":["until(other)"],"summary":"The span from this date to `other`, as a `Duration` of whole days.","types":[],"urlPath":"time"},{"anchor":"fn-monthsuntil","kind":"function","line":1858,"name":"monthsUntil","qualifiedName":"Date.monthsUntil","signatures":["monthsUntil(other)"],"summary":"Whole calendar months from this date to `other`, negative when `other` is earlier.\n\nTruncated, not rounded: a partial month does not count, so January 15th to February 14th is 0 months.\n\nThe count is the exact inverse of `addMonths`, which is why the correction below asks `addMonths` rather than comparing day-of-month fields: January 31st plus one month IS February 28th, so January 31st to February 28th is one month, even though 28 < 31. Comparing the day fields answers 0 there and contradicts the addition this same file performs.","types":[],"urlPath":"time"},{"anchor":"fn-yearsuntil","kind":"function","line":1878,"name":"yearsUntil","qualifiedName":"Date.yearsUntil","signatures":["yearsUntil(other)"],"summary":"Whole calendar years from this date to `other`, negative when `other` is earlier. Truncated, like `monthsUntil`.\n\nThis is how to compute an age.","types":[],"urlPath":"time"},{"anchor":"fn-before?","kind":"function","line":1891,"name":"before?","qualifiedName":"Date.before?","signatures":["before?(other)"],"summary":"Returns `true` when this date is earlier than `other`.","types":[],"urlPath":"time"},{"anchor":"fn-after?","kind":"function","line":1900,"name":"after?","qualifiedName":"Date.after?","signatures":["after?(other)"],"summary":"Returns `true` when this date is later than `other`.","types":[],"urlPath":"time"},{"anchor":"fn-compareto","kind":"function","line":1912,"name":"compareTo","qualifiedName":"Date.compareTo","signatures":["compareTo(other)"],"summary":"Orders this date against another.","types":[],"urlPath":"time"},{"anchor":"fn-at","kind":"function","line":1935,"name":"at","qualifiedName":"Date.at","signatures":["at(time, offset)"],"summary":"This date at a given time of day and offset, as a `DateTime`.","types":[],"urlPath":"time"},{"anchor":"make-time","kind":"make","line":1939,"name":"Time","qualifiedName":"Time","signatures":["inspectValue(colors)","before?(other)","after?(other)","+(span)","-(span)","addSeconds(count)","addMinutes(count)","addHours(count)","until(other)","compareTo(other)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"fn-inspectvalue","kind":"function","line":1944,"name":"inspectValue","qualifiedName":"Time.inspectValue","signatures":["inspectValue(colors)"],"summary":"Renders the time structurally, for debugging output.","types":[],"urlPath":"time"},{"anchor":"fn-before?","kind":"function","line":1970,"name":"before?","qualifiedName":"Time.before?","signatures":["before?(other)"],"summary":"Returns `true` when this time of day is earlier than `other`.","types":[],"urlPath":"time"},{"anchor":"fn-after?","kind":"function","line":1979,"name":"after?","qualifiedName":"Time.after?","signatures":["after?(other)"],"summary":"Returns `true` when this time of day is later than `other`.","types":[],"urlPath":"time"},{"anchor":"fn-+","kind":"function","line":1995,"name":"+","qualifiedName":"Time.+","signatures":["+(span)"],"summary":"Advances the time of day by a span, wrapping within the day.\n\nA Time has no date to carry into, so 23:00 ` 2.hours is 01:00. Reach for `DateTime` when the day rolling over is something you need to see.\n\nThe nanosecond field rides along untouched: `wholeSeconds` truncates the span, so a sub-second Duration shifts nothing.","types":[],"urlPath":"time"},{"anchor":"fn--","kind":"function","line":2004,"name":"-","qualifiedName":"Time.-","signatures":["-(span)"],"summary":"Moves the time of day back by a span, wrapping within the day.","types":[],"urlPath":"time"},{"anchor":"fn-addseconds","kind":"function","line":2013,"name":"addSeconds","qualifiedName":"Time.addSeconds","signatures":["addSeconds(count)"],"summary":"The time of day `count` seconds later, wrapping within the day.","types":[],"urlPath":"time"},{"anchor":"fn-addminutes","kind":"function","line":2022,"name":"addMinutes","qualifiedName":"Time.addMinutes","signatures":["addMinutes(count)"],"summary":"The time of day `count` minutes later, wrapping within the day.","types":[],"urlPath":"time"},{"anchor":"fn-addhours","kind":"function","line":2031,"name":"addHours","qualifiedName":"Time.addHours","signatures":["addHours(count)"],"summary":"The time of day `count` hours later, wrapping within the day.","types":[],"urlPath":"time"},{"anchor":"fn-until","kind":"function","line":2042,"name":"until","qualifiedName":"Time.until","signatures":["until(other)"],"summary":"Elapsed time from this time of day to `other`, within the same day.\n\nNegative when `other` is earlier. Sub-second precision is kept.","types":[],"urlPath":"time"},{"anchor":"fn-compareto","kind":"function","line":2065,"name":"compareTo","qualifiedName":"Time.compareTo","signatures":["compareTo(other)"],"summary":"Orders this time of day against another, nanoseconds included.","types":[],"urlPath":"time"},{"anchor":"make-datetime","kind":"make","line":2076,"name":"DateTime","qualifiedName":"DateTime","signatures":["inspectValue(colors)","at(offset)","+(span)","-(span)","addDays(count)","addWeeks(count)","addMonths(count)","addYears(count)","until(other)","before?(other)","after?(other)","compareTo(other)"],"summary":"","types":[],"urlPath":"time"},{"anchor":"fn-inspectvalue","kind":"function","line":2081,"name":"inspectValue","qualifiedName":"DateTime.inspectValue","signatures":["inspectValue(colors)"],"summary":"Renders the instant structurally, for debugging output.","types":[],"urlPath":"time"},{"anchor":"fn-at","kind":"function","line":2131,"name":"at","qualifiedName":"DateTime.at","signatures":["at(offset)"],"summary":"The same instant, rendered at another offset.\n\nNothing moves: the wall clock changes because the offset does, and `epochSeconds` is unchanged.","types":[],"urlPath":"time"},{"anchor":"fn-+","kind":"function","line":2153,"name":"+","qualifiedName":"DateTime.+","signatures":["+(span)"],"summary":"Advances the instant by a fixed span, keeping its offset.","types":[],"urlPath":"time"},{"anchor":"fn--","kind":"function","line":2162,"name":"-","qualifiedName":"DateTime.-","signatures":["-(span)"],"summary":"Moves the instant back by a fixed span, keeping its offset.","types":[],"urlPath":"time"},{"anchor":"fn-adddays","kind":"function","line":2197,"name":"addDays","qualifiedName":"DateTime.addDays","signatures":["addDays(count)"],"summary":"The instant `count` days later, keeping the wall clock and the offset.","types":[],"urlPath":"time"},{"anchor":"fn-addweeks","kind":"function","line":2207,"name":"addWeeks","qualifiedName":"DateTime.addWeeks","signatures":["addWeeks(count)"],"summary":"The instant `count` weeks later, keeping the wall clock and the offset.","types":[],"urlPath":"time"},{"anchor":"fn-addmonths","kind":"function","line":2218,"name":"addMonths","qualifiedName":"DateTime.addMonths","signatures":["addMonths(count)"],"summary":"The instant `count` calendar months later, with the day clamped into the target month.","types":[],"urlPath":"time"},{"anchor":"fn-addyears","kind":"function","line":2228,"name":"addYears","qualifiedName":"DateTime.addYears","signatures":["addYears(count)"],"summary":"The instant `count` calendar years later, with the day clamped.","types":[],"urlPath":"time"},{"anchor":"fn-until","kind":"function","line":2243,"name":"until","qualifiedName":"DateTime.until","signatures":["until(other)"],"summary":"Elapsed time from this instant to `other`, negative when `other` is earlier. Sub-second precision is kept.","types":[],"urlPath":"time"},{"anchor":"fn-before?","kind":"function","line":2259,"name":"before?","qualifiedName":"DateTime.before?","signatures":["before?(other)"],"summary":"Returns `true` when this instant is earlier than `other`, whatever offsets they are written at.","types":[],"urlPath":"time"},{"anchor":"fn-after?","kind":"function","line":2268,"name":"after?","qualifiedName":"DateTime.after?","signatures":["after?(other)"],"summary":"Returns `true` when this instant is later than `other`.","types":[],"urlPath":"time"},{"anchor":"fn-compareto","kind":"function","line":2296,"name":"compareTo","qualifiedName":"DateTime.compareTo","signatures":["compareTo(other)"],"summary":"Orders this instant against another, by instant rather than by wall clock, so 12:00Z and 14:00`02:00 compare `Equal`.\n\nNamed `compareTo` rather than `compare`: a make-block `compare` is shadowed by the builtin comparison dispatch and fails at runtime on both backends.","types":[],"urlPath":"time"},{"anchor":"trait-truthyable","kind":"trait","line":18,"name":"Truthyable","qualifiedName":"Truthyable","signatures":["truthy? : Bool"],"summary":"`Truthyable`: what counts as true when a value is used as a condition.\n\nThe rule is Crystal's, and it is short: only `false`, `None` and `()` are falsy. Everything else is truthy, including `0`, `\"\"` and `[]`, which some languages treat as false and Kex deliberately does not.\n\n```kex\n0.truthy?       # => true\n\"\".truthy?      # => true\n[].truthy?      # => true\nNone.truthy?    # => false\nfalse.truthy?   # => false\n```\n\nWhen you want the \"is there anything here\" question instead, that is `Blankable`'s `blank?` / `present?`.\n\nThere is no NaN to consider: a float operation that would produce one raises instead, matching BEAM (see nonFiniteFloatError in src/interpreter/value.cxx).","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":26,"name":"truthy?","qualifiedName":"Truthyable.truthy?","signatures":["truthy? : Bool"],"summary":"Returns `true` when the value counts as true in a condition.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"make-bool","kind":"make","line":44,"name":"Bool","qualifiedName":"Bool","signatures":[],"summary":"","types":[],"urlPath":"truthyable"},{"anchor":"make-integer","kind":"make","line":57,"name":"Integer","qualifiedName":"Integer","signatures":["truthy? : Bool"],"summary":"","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":68,"name":"truthy?","qualifiedName":"Integer.truthy?","signatures":["truthy? : Bool"],"summary":"Always `true`, including for zero.\n\nZero is a number, not an absence. Compare it explicitly when zero means something: `count == 0`.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"make-float","kind":"make","line":72,"name":"Float","qualifiedName":"Float","signatures":["truthy? : Bool"],"summary":"","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":79,"name":"truthy?","qualifiedName":"Float.truthy?","signatures":["truthy? : Bool"],"summary":"Always `true`, including for zero.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"make-string","kind":"make","line":83,"name":"String","qualifiedName":"String","signatures":["truthy? : Bool"],"summary":"","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":94,"name":"truthy?","qualifiedName":"String.truthy?","signatures":["truthy? : Bool"],"summary":"Always `true`, including for the empty string.\n\nUse `blank?` from `Blankable` when an empty or whitespace-only string should count as nothing.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"make-optional<x>","kind":"make","line":98,"name":"Optional<X>","qualifiedName":"Optional<X>","signatures":["truthy? : Bool"],"summary":"","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":109,"name":"truthy?","qualifiedName":"Optional<X>.truthy?","signatures":["truthy? : Bool"],"summary":"Returns `true` for a `Just` and `false` for `None`.\n\nThe one type where truthiness is genuinely about presence, which is what makes an optional usable directly as a condition.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"make-[x]","kind":"make","line":114,"name":"[X]","qualifiedName":"[X]","signatures":["truthy? : Bool"],"summary":"","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":124,"name":"truthy?","qualifiedName":"[X].truthy?","signatures":["truthy? : Bool"],"summary":"Always `true`, including for the empty list.\n\nUse `empty?` or `blank?` when an empty list should count as nothing.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"make-map<k,-v>","kind":"make","line":128,"name":"Map<K, V>","qualifiedName":"Map<K, V>","signatures":["truthy? : Bool"],"summary":"","types":["Bool"],"urlPath":"truthyable"},{"anchor":"fn-truthy?","kind":"function","line":136,"name":"truthy?","qualifiedName":"Map<K, V>.truthy?","signatures":["truthy? : Bool"],"summary":"Always `true`, including for the empty map.","types":["Bool"],"urlPath":"truthyable"},{"anchor":"record-type","kind":"record","line":17,"name":"Type","qualifiedName":"Type","signatures":[],"summary":"Types as values.\n\n`Type.of(x)` answers what a value IS, as something you can hold, print, compare, and take apart:\n\n```kex\nType.of(42)                     # Type { name: \"Integer\", args: [] }\nType.of([1, 2]).to(String)        # \"[Integer]\"\nType.of(x) == Type.of(y)        # structural equality, like any record\nType.of(due).fields             # [\"year\", \"month\", \"day\"]\n```\n\nThe answer comes from the compiler where it can: a checked expression knows things a value cannot carry, such as the unused half of a `Result` or the element type of an empty list. Where the checker has no concrete answer: gradual code, `--no-check`, a value arriving from another process: the value itself is asked instead. That fallback is honest but lossy: an empty list has no element to inspect, and a `Result` only ever holds one side.","types":["String","[Type]","Bool"],"urlPath":"type"},{"anchor":"module-type","kind":"module","line":33,"name":"Type","qualifiedName":"Type","signatures":["of(value)","named(name)","generic(name, args)","function(params, result)","returnedBy(function)"],"summary":"Building and obtaining `Type` values.","types":[],"urlPath":"type"},{"anchor":"function-of","kind":"function","line":49,"name":"of","qualifiedName":"Type.of","signatures":["of(value)"],"summary":"Returns the type of `value`.\n\nThe entry point to everything else here. Prefer it over matching on the `Type` record directly.","types":[],"urlPath":"type"},{"anchor":"function-named","kind":"function","line":62,"name":"named","qualifiedName":"Type.named","signatures":["named(name)"],"summary":"Builds a type from its name, for comparing against something you already know.","types":[],"urlPath":"type"},{"anchor":"function-generic","kind":"function","line":74,"name":"generic","qualifiedName":"Type.generic","signatures":["generic(name, args)"],"summary":"Builds a type that takes arguments, from its name and those arguments.\n\nRenamed from `Type.with` when `with` became the capability-substitution keyword (kexhq/kex#143).","types":[],"urlPath":"type"},{"anchor":"function-function","kind":"function","line":86,"name":"function","qualifiedName":"Type.function","signatures":["function(params, result)"],"summary":"Builds a function type from its parameter types and its result type: in the order a signature is written.","types":[],"urlPath":"type"},{"anchor":"function-returnedby","kind":"function","line":99,"name":"returnedBy","qualifiedName":"Type.returnedBy","signatures":["returnedBy(function)"],"summary":"Returns the type a named function returns, as answered by the compiler.\n\nNamed functions only. A lambda or a function VALUE carries no signature at runtime, and an overloaded name has no single answer: both are compile errors rather than a guess.","types":[],"urlPath":"type"},{"anchor":"make-type","kind":"make","line":109,"name":"Type","qualifiedName":"Type","signatures":["to(String)"],"summary":"Everything else is a METHOD, not a module function: a module function is only reachable through UFCS in the interpreter, so `Type.of(x).fields` worked there and raised on BEAM.","types":[],"urlPath":"type"},{"anchor":"fn-to","kind":"function","line":175,"name":"to","qualifiedName":"Type.to","signatures":["to(String)"],"summary":"Renders the type the way it is written in SOURCE, not the way it is stored.\n\nA list reads as `[Integer]`, a tuple as `(Integer, String)`, an optional as `String?`, a map as `{String: Integer}`, and a function as its signature.","types":[],"urlPath":"type"},{"anchor":"trait-unit","kind":"trait","line":24,"name":"Unit","qualifiedName":"Unit","signatures":["factor : Float","kind : Atom","symbol : String"],"summary":"Numbers that carry a unit.\n\nWriting `5.sec` or `90.minute` gives you a `Measure`: a number, the unit it was written in, and the same quantity in that dimension's base unit. Adding, subtracting and converting all go through the canonical value, so the arithmetic is right regardless of which units the operands were written in, and mixing dimensions is an `Error` rather than a silently wrong number.\n\n```kex\n5.sec.to(String)                     # => \"5.0 s\"\n1.5.hour.to(String)                  # => \"1.5 h\"\n(1.hour ` 30.minute).map(~to(String))  # => Ok(\"1.5 h\")\n90.minute.convert(Hour)              # => Ok(1.5 h)\n```\n\nThe time units (`nanosecond` through `week`) are in the prelude. Other dimensions live in opt-in modules under `Units`, and every one of them measures against the same machinery here.\n\nA `Measure` is a measurement, not an elapsed span: `5.sec` describes a quantity, while `Duration` is what `Time` and `Date+ use for a span between two moments.\n\nThe trait a unit implements: how it converts to its dimension's base unit, which dimension that is, and how it is written.","types":["Float","Atom","String"],"urlPath":"units"},{"anchor":"fn-factor","kind":"function","line":32,"name":"factor","qualifiedName":"Unit.factor","signatures":["factor : Float"],"summary":"How many base units one of this unit is: `60.0` for a minute, whose base unit is the second.","types":["Float"],"urlPath":"units"},{"anchor":"fn-kind","kind":"function","line":41,"name":"kind","qualifiedName":"Unit.kind","signatures":["kind : Atom"],"summary":"Which dimension this unit measures, as an atom. Only measures of the same kind can be added or converted between.","types":["Atom"],"urlPath":"units"},{"anchor":"fn-symbol","kind":"function","line":49,"name":"symbol","qualifiedName":"Unit.symbol","signatures":["symbol : String"],"summary":"How this unit is written when a measure is displayed.","types":["String"],"urlPath":"units"},{"anchor":"function-measurekind","kind":"function","line":59,"name":"measureKind","qualifiedName":"measureKind","signatures":["measureKind(measure)"],"summary":"The dimension a measure belongs to.","types":[],"urlPath":"units"},{"anchor":"function-measuresymbol","kind":"function","line":68,"name":"measureSymbol","qualifiedName":"measureSymbol","signatures":["measureSymbol(measure)"],"summary":"The symbol a measure displays with.","types":[],"urlPath":"units"},{"anchor":"function-measurefactor","kind":"function","line":77,"name":"measureFactor","qualifiedName":"measureFactor","signatures":["measureFactor(measure)"],"summary":"The conversion factor of a measure's unit.","types":[],"urlPath":"units"},{"anchor":"record-unitdefinition","kind":"record","line":83,"name":"UnitDefinition","qualifiedName":"UnitDefinition","signatures":[],"summary":"A unit described at run time rather than by a constructor.\n\nRuntime-defined units are used for prefixes and units derived by arithmetic: `s^2` from squaring a duration, a kilo- prefix applied to a base unit.","types":["Float","Atom","String"],"urlPath":"units"},{"anchor":"make-unitdefinition","kind":"make","line":94,"name":"UnitDefinition","qualifiedName":"UnitDefinition","signatures":[],"summary":"","types":[],"urlPath":"units"},{"anchor":"record-measure","kind":"record","line":116,"name":"Measure","qualifiedName":"Measure","signatures":[],"summary":"A quantity with a unit.\n\nA Measure is shared by every unit module. `canonical` stores the value in that dimension's base unit; its Unit controls its display. That split is what makes `1.hour ` 30.minute+ correct and still print in hours.","types":["Float","UnitDefinition"],"urlPath":"units"},{"anchor":"record-duration","kind":"record","line":132,"name":"Duration","qualifiedName":"Duration","signatures":[],"summary":"An elapsed span of time, in seconds.\n\nDuration is an elapsed span used by Time, Date, and DateTime. A time Measure is deliberately not a Duration: `5.sec` describes a measurement.","types":["Float"],"urlPath":"units"},{"anchor":"type-timeunit","kind":"type","line":138,"name":"TimeUnit","qualifiedName":"TimeUnit","signatures":[],"summary":"The time units, from nanoseconds to weeks. The base unit is the second.","types":[],"urlPath":"units"},{"anchor":"make-timeunit","kind":"make","line":141,"name":"TimeUnit","qualifiedName":"TimeUnit","signatures":["factor(@Nanosecond)","symbol(@Nanosecond)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"fn-factor","kind":"function","line":142,"name":"factor","qualifiedName":"TimeUnit.factor","signatures":["factor(@Nanosecond)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"fn-symbol","kind":"function","line":153,"name":"symbol","qualifiedName":"TimeUnit.symbol","signatures":["symbol(@Nanosecond)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"make-integer","kind":"make","line":167,"name":"Integer","qualifiedName":"Integer","signatures":["timeMeasure(unit)"],"summary":"Time-unit constructors on `Integer`: `5.sec`, `90.minute`, `2.week`.\n\nEach answers a `Measure` whose display unit is the one you named, so `90.minute` prints as minutes even though it is stored as 5400 seconds.","types":[],"urlPath":"units"},{"anchor":"fn-timemeasure","kind":"function","line":234,"name":"timeMeasure","qualifiedName":"Integer.timeMeasure","signatures":["timeMeasure(unit)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"make-float","kind":"make","line":251,"name":"Float","qualifiedName":"Float","signatures":["timeMeasure(unit)"],"summary":"The same time-unit constructors on `Float`, for fractional quantities: `1.5.hour`, `0.25.sec`.","types":[],"urlPath":"units"},{"anchor":"fn-timemeasure","kind":"function","line":302,"name":"timeMeasure","qualifiedName":"Float.timeMeasure","signatures":["timeMeasure(unit)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"make-measure","kind":"make","line":314,"name":"Measure","qualifiedName":"Measure","signatures":["to(String)","scale(multiplier)","^(exponent)","convertTo(unit)","convert(unit)","+(other)","-(other)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"fn-to","kind":"function","line":318,"name":"to","qualifiedName":"Measure.to","signatures":["to(String)"],"summary":"","types":[],"urlPath":"units"},{"anchor":"fn-scale","kind":"function","line":336,"name":"scale","qualifiedName":"Measure.scale","signatures":["scale(multiplier)"],"summary":"Formatting into a target display unit deliberately has NO prelude clause. A unit module (Units.SI, Units.Data, ...) supplies `to(String, in:)` for the units it owns, and reaching one requires importing it. A catch-all here would answer `None` for an un-imported module instead: a silently empty Optional in place of \"you need `using Units.SI`\".\n\nMultiplies the measure by a plain number, keeping its unit.\n\nThe unit is unchanged, so this scales a quantity rather than converting it: three of a two-second interval is six seconds.","types":[],"urlPath":"units"},{"anchor":"fn-^","kind":"function","line":357,"name":"^","qualifiedName":"Measure.^","signatures":["^(exponent)"],"summary":"Raises the measure to a power, recording the derived unit in its notation.\n\nRaising a measure preserves its display unit while applying the power to its canonical value, display value, and conversion factor. The current Unit trait represents a dimension as one Atom, so the dimension remains the base kind; the notation records the derived unit (for example `s^2`).","types":[],"urlPath":"units"},{"anchor":"fn-convertto","kind":"function","line":383,"name":"convertTo","qualifiedName":"Measure.convertTo","signatures":["convertTo(unit)"],"summary":"Converts the measure to another unit of the same dimension.\n\nAnswers `Error` when the dimensions differ: you cannot express seconds in bytes, and this says so rather than producing a wrong number.\n\nConvert through the Unit trait so units from opt-in modules (SI, Data, and future domains) remain interchangeable with prelude time units.","types":[],"urlPath":"units"},{"anchor":"fn-convert","kind":"function","line":407,"name":"convert","qualifiedName":"Measure.convert","signatures":["convert(unit)"],"summary":"Converts the measure to another time unit. The short spelling of `convertTo`, kept for prelude time measures.","types":[],"urlPath":"units"},{"anchor":"fn-+","kind":"function","line":423,"name":"+","qualifiedName":"Measure.+","signatures":["+(other)"],"summary":"Adds two measures of the same dimension.\n\nThe units need not match: the sum goes through the canonical values, and comes back displayed in the LEFT operand's unit. Adding measures of different dimensions is an `Error`.","types":[],"urlPath":"units"},{"anchor":"fn--","kind":"function","line":447,"name":"-","qualifiedName":"Measure.-","signatures":["-(other)"],"summary":"Subtracts a measure of the same dimension.\n\nLike ```, the result is displayed in the left operand's unit, and mixing dimensions is an `Error+.","types":[],"urlPath":"units"},{"anchor":"module-units","kind":"module","line":469,"name":"Units","qualifiedName":"Units","signatures":[],"summary":"The time units are prelude-global, because every unit module measures against the same dimensions: `Units.SI` defines `Watt * Hour` with the `Hour` declared above, and future domains do the same. That makes `Hour` correct but not obviously located, so the same constructors are reachable under `Units` too, for call sites that would rather name where a unit comes from. These are aliases, not copies: each binds the identical constructor, so `Units.Hour` and `Hour` match the same patterns and compare equal.","types":["?"],"urlPath":"units"},{"anchor":"constant-nanosecond","kind":"constant","line":470,"name":"Nanosecond","qualifiedName":"Units.Nanosecond","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-microsecond","kind":"constant","line":471,"name":"Microsecond","qualifiedName":"Units.Microsecond","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-millisecond","kind":"constant","line":472,"name":"Millisecond","qualifiedName":"Units.Millisecond","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-second","kind":"constant","line":473,"name":"Second","qualifiedName":"Units.Second","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-minute","kind":"constant","line":474,"name":"Minute","qualifiedName":"Units.Minute","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-hour","kind":"constant","line":475,"name":"Hour","qualifiedName":"Units.Hour","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-day","kind":"constant","line":476,"name":"Day","qualifiedName":"Units.Day","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"constant-week","kind":"constant","line":477,"name":"Week","qualifiedName":"Units.Week","signatures":[],"summary":"","types":["?"],"urlPath":"units"},{"anchor":"module-uri","kind":"module","line":1,"name":"URI","qualifiedName":"URI","signatures":["parse(text) : String -> Result<URI, URIError>","fromIRI(text) : String -> Result<URI, URIError>","parse(text) : String -> Result<URL, URIError>","build : String -> String -> [String] -> Query -> Result<URL, URIError>","from(entries) : [(String, String?)] -> Query","parse(text) : String -> Result<Query, URIError>","from(entries) : [(String, String)] -> Form","parse(text) : String -> Result<Form, URIError>","equivalent?(other)","resolve(reference)","inspectValue(colors)","equivalent?(other)","resolve(reference)","inspectValue(colors)"],"summary":"","types":["String","[(String, String?)]","URIErrorKind","Integer?","Result<URI, URIError>","Result<URL, URIError>","(String) -> (String) -> ([String]) -> (Query) -> Result<URL, URIError>","Query","Result<Query, URIError>","[(String, String)]","Form","Result<Form, URIError>"],"urlPath":"uri"},{"anchor":"record-uri","kind":"record","line":15,"name":"URI","qualifiedName":"URI.URI","signatures":[],"summary":"Strict RFC 3986 URI values, hierarchical URLs, query strings, and HTML form encoding. Parsing preserves caller spelling; normalization is always explicit.\n\n```kex\nusing URI\n\nlet base = URL.parse(\"https://example.test/a/\").try\nlet reference = URI.parse(\"../items?limit=10\").try\nbase.resolve(reference).try.string\n```\n\nA parsed RFC 3986 URI reference. Construction is strict; use `parse` rather than building this representation directly. `string` preserves the spelling supplied by the caller, while `normalize` is explicit.","types":["String"],"urlPath":"uri"},{"anchor":"record-url","kind":"record","line":22,"name":"URL","qualifiedName":"URI.URL","signatures":[],"summary":"An absolute hierarchical URI with an authority component, such as an HTTP URL. Unlike a general `URI`, a `URL` always has a scheme and host, which makes accessors such as `scheme` and `host` total.","types":["String"],"urlPath":"uri"},{"anchor":"record-host","kind":"record","line":27,"name":"Host","qualifiedName":"URI.Host","signatures":[],"summary":"A host's display spelling and normalized ASCII/IDNA spelling.","types":["String"],"urlPath":"uri"},{"anchor":"record-query","kind":"record","line":36,"name":"Query","qualifiedName":"URI.Query","signatures":[],"summary":"Ordered URI query entries. `None` distinguishes a bare key from `key=`.\n\nOrder and duplicates matter in real APIs: `tag=kex&tag=beam` must not become a map with one value silently discarded.","types":["[(String, String?)]"],"urlPath":"uri"},{"anchor":"record-form","kind":"record","line":44,"name":"Form","qualifiedName":"URI.Form","signatures":[],"summary":"Ordered `application/x-www-form-urlencoded` entries.\n\nThis is deliberately separate from `Query`: HTML forms encode spaces as plus signs, while a generic URI query treats a plus as an ordinary ``+.","types":["[(String, String?)]"],"urlPath":"uri"},{"anchor":"record-urierror","kind":"record","line":49,"name":"URIError","qualifiedName":"URI.URIError","signatures":[],"summary":"A typed URI parsing, conversion, or resolution failure.","types":["URIErrorKind","String","Integer?"],"urlPath":"uri"},{"anchor":"type-urierrorkind","kind":"type","line":56,"name":"URIErrorKind","qualifiedName":"URI.URIErrorKind","signatures":[],"summary":"Stable URI failure categories.","types":[],"urlPath":"uri"},{"anchor":"function-parse","kind":"function","line":69,"name":"parse","qualifiedName":"URI.parse","signatures":["parse(text) : String -> Result<URI, URIError>"],"summary":"Strictly parses an ASCII RFC 3986 URI reference.\n\nRelative references are valid here. Use `URL.parse` when the input must be a complete hierarchical URL with a scheme and authority.","types":["String","Result<URI, URIError>"],"urlPath":"uri"},{"anchor":"function-fromiri","kind":"function","line":83,"name":"fromIRI","qualifiedName":"URI.fromIRI","signatures":["fromIRI(text) : String -> Result<URI, URIError>"],"summary":"Converts a Unicode IRI to an ASCII URI using IDNA and UTF-8 percent encoding.\n\nUse this for human-entered international addresses. `parse` is intentionally stricter and accepts only an already encoded ASCII URI.","types":["String","Result<URI, URIError>"],"urlPath":"uri"},{"anchor":"module-uri-url","kind":"module","line":86,"name":"URI.URL","qualifiedName":"URI.URL","signatures":["parse(text) : String -> Result<URL, URIError>","build : String -> String -> [String] -> Query -> Result<URL, URIError>"],"summary":"","types":["String","Result<URL, URIError>","(String) -> (String) -> ([String]) -> (Query) -> Result<URL, URIError>"],"urlPath":"uri"},{"anchor":"function-parse","kind":"function","line":97,"name":"parse","qualifiedName":"URI.URL.parse","signatures":["parse(text) : String -> Result<URL, URIError>"],"summary":"Parses an absolute hierarchical URL with an authority.\n\nRejects relative references, opaque URIs, and values without a host, so a caller can use `scheme` and `host` without handling absence.","types":["String","Result<URL, URIError>"],"urlPath":"uri"},{"anchor":"function-build","kind":"function","line":119,"name":"build","qualifiedName":"URI.URL.build","signatures":["build : String -> String -> [String] -> Query -> Result<URL, URIError>"],"summary":"Builds and validates a URL from decoded path segments and a query value.\n\nPass decoded values, not pre-escaped text. The builder escapes each path segment independently, so a slash inside one value cannot accidentally become another level of the path.","types":["(String) -> (String) -> ([String]) -> (Query) -> Result<URL, URIError>"],"urlPath":"uri"},{"anchor":"module-uri-query","kind":"module","line":123,"name":"URI.Query","qualifiedName":"URI.Query","signatures":["from(entries) : [(String, String?)] -> Query","parse(text) : String -> Result<Query, URIError>"],"summary":"","types":["[(String, String?)]","Query","String","Result<Query, URIError>"],"urlPath":"uri"},{"anchor":"function-from","kind":"function","line":138,"name":"from","qualifiedName":"URI.Query.from","signatures":["from(entries) : [(String, String?)] -> Query"],"summary":"Builds a query while preserving order, duplicates, and bare keys.\n\nA `None` value encodes as a bare key; `Just(\"\")` encodes with an equals sign. This preserves the difference between `?debug` and `?debug=`.","types":["[(String, String?)]","Query"],"urlPath":"uri"},{"anchor":"function-parse","kind":"function","line":148,"name":"parse","qualifiedName":"URI.Query.parse","signatures":["parse(text) : String -> Result<Query, URIError>"],"summary":"Parses generic URI query encoding; `` remains a literal plus.","types":["String","Result<Query, URIError>"],"urlPath":"uri"},{"anchor":"module-uri-form","kind":"module","line":152,"name":"URI.Form","qualifiedName":"URI.Form","signatures":["from(entries) : [(String, String)] -> Form","parse(text) : String -> Result<Form, URIError>"],"summary":"","types":["[(String, String)]","Form","String","Result<Form, URIError>"],"urlPath":"uri"},{"anchor":"function-from","kind":"function","line":160,"name":"from","qualifiedName":"URI.Form.from","signatures":["from(entries) : [(String, String)] -> Form"],"summary":"Builds a form value while preserving order and duplicates.","types":["[(String, String)]","Form"],"urlPath":"uri"},{"anchor":"function-parse","kind":"function","line":170,"name":"parse","qualifiedName":"URI.Form.parse","signatures":["parse(text) : String -> Result<Form, URIError>"],"summary":"Parses form encoding where `` represents a space.","types":["String","Result<Form, URIError>"],"urlPath":"uri"},{"anchor":"make-uri","kind":"make","line":174,"name":"URI","qualifiedName":"URI","signatures":["equivalent?(other)","resolve(reference)","inspectValue(colors)"],"summary":"","types":[],"urlPath":"uri"},{"anchor":"fn-equivalent?","kind":"function","line":195,"name":"equivalent?","qualifiedName":"URI.equivalent?","signatures":["equivalent?(other)"],"summary":"Compares normalized representations rather than original spellings.","types":[],"urlPath":"uri"},{"anchor":"fn-resolve","kind":"function","line":206,"name":"resolve","qualifiedName":"URI.resolve","signatures":["resolve(reference)"],"summary":"Resolves a URI reference against this absolute base.","types":[],"urlPath":"uri"},{"anchor":"fn-inspectvalue","kind":"function","line":225,"name":"inspectValue","qualifiedName":"URI.inspectValue","signatures":["inspectValue(colors)"],"summary":"Structural inspection is also credential-safe.","types":[],"urlPath":"uri"},{"anchor":"make-url","kind":"make","line":228,"name":"URL","qualifiedName":"URL","signatures":["equivalent?(other)","resolve(reference)","inspectValue(colors)"],"summary":"","types":[],"urlPath":"uri"},{"anchor":"fn-equivalent?","kind":"function","line":241,"name":"equivalent?","qualifiedName":"URL.equivalent?","signatures":["equivalent?(other)"],"summary":"Compares normalized representations rather than original spellings.","types":[],"urlPath":"uri"},{"anchor":"fn-resolve","kind":"function","line":251,"name":"resolve","qualifiedName":"URL.resolve","signatures":["resolve(reference)"],"summary":"Resolves a URI reference while preserving the URL invariant.","types":[],"urlPath":"uri"},{"anchor":"fn-inspectvalue","kind":"function","line":267,"name":"inspectValue","qualifiedName":"URL.inspectValue","signatures":["inspectValue(colors)"],"summary":"Structural inspection is also credential-safe.","types":[],"urlPath":"uri"},{"anchor":"make-query","kind":"make","line":270,"name":"Query","qualifiedName":"Query","signatures":[],"summary":"","types":[],"urlPath":"uri"},{"anchor":"make-form","kind":"make","line":280,"name":"Form","qualifiedName":"Form","signatures":[],"summary":"","types":[],"urlPath":"uri"},{"anchor":"module-control-retry","kind":"module","line":16,"name":"Control.Retry","qualifiedName":"Control.Retry","signatures":["fixed(maximumAttempts, delay) : Integer -> Duration -> Policy","exponential : Integer -> Duration -> Duration -> Policy","withMaximumElapsed(maximumElapsed)","withJitter(fraction)","run(policy, operation) : Policy -> Block<Result<X, E>> -> Result<X, E>","run(policy, operation) : Policy -> Predicate<E> -> Block<Result<X, E>> -> Result<X, E>","runWith : Policy -> Predicate<E> -> Sleeper -> Block<Result<X, E>> -> Result<X, E>","runWithRandom : Policy -> Predicate<E> -> Sleeper -> Block<Float> -> Block<Result<X, E>> -> Result<X, E>","attempt(policy, predicate, sleeper, random, operation, number, delay, elapsed)","fixed(maximumAttempts, delay) : Integer -> Duration -> Policy","exponential : Integer -> Duration -> Duration -> Policy","run(policy, operation) : Policy -> Block<Result<X, E>> -> Result<X, E>","run(policy, operation) : Policy -> Predicate<E> -> Block<Result<X, E>> -> Result<X, E>","runWith : Policy -> Predicate<E> -> Sleeper -> Block<Result<X, E>> -> Result<X, E>","runWithRandom : Policy -> Predicate<E> -> Sleeper -> Block<Float> -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Bounded retries for operations whose failures can be classified by the application.\n\nA retry is never automatically safe just because an error was temporary. The caller owns the operation and, where necessary, a predicate that excludes permanent failures and non-idempotent work. Policies bound attempts, delay, and optionally total sleep so a dependency cannot stall the program forever.\n\n```kex\nusing Control.Retry\n\nlet policy = Retry.exponential(4, 100.milliseconds, 2.seconds)\n  .withJitter(0.2)\nRetry.run(policy, ~retryable?) do\n  client.get(\"https://api.example.com/inventory\")\nend\n```","types":["Integer","Duration","Float","Duration?","Policy","(Integer) -> (Duration) -> (Duration) -> Policy","Block<Result<X, E>>","Result<X, E>","Predicate<E>","(Block<Result<X, E>>) -> Result<X, E>","(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Result<X, E>>) -> Result<X, E>","(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Float>) -> (Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"record-policy","kind":"record","line":20,"name":"Policy","qualifiedName":"Control.Retry.Policy","signatures":[],"summary":"A bounded retry schedule. Attempts includes the initial call. Delays are immutable `Duration` values and never exceed `maximumDelay`.","types":["Integer","Duration","Float","Duration?"],"urlPath":"control/retry"},{"anchor":"type-predicate","kind":"type","line":30,"name":"Predicate","qualifiedName":"Control.Retry.Predicate","signatures":[],"summary":"Decides whether an application error is eligible for another attempt.","types":[],"urlPath":"control/retry"},{"anchor":"type-sleeper","kind":"type","line":34,"name":"Sleeper","qualifiedName":"Control.Retry.Sleeper","signatures":[],"summary":"Performs one scheduled delay. Supplying this callback makes retry tests deterministic without sleeping.","types":[],"urlPath":"control/retry"},{"anchor":"function-fixed","kind":"function","line":48,"name":"fixed","qualifiedName":"Control.Retry.fixed","signatures":["fixed(maximumAttempts, delay) : Integer -> Duration -> Policy"],"summary":"Builds a constant-delay retry policy.\n\nFixed delays are predictable and useful for a local resource expected to become ready shortly. For many clients sharing a remote dependency, prefer exponential backoff with jitter to avoid synchronized retry bursts.","types":["Integer","Duration","Policy"],"urlPath":"control/retry"},{"anchor":"function-exponential","kind":"function","line":70,"name":"exponential","qualifiedName":"Control.Retry.exponential","signatures":["exponential : Integer -> Duration -> Duration -> Policy"],"summary":"Builds a doubling backoff capped at `maximumDelay`.\n\nThe first retry waits `initialDelay`; later delays double until they reach the cap. Add jitter for production network traffic.","types":["(Integer) -> (Duration) -> (Duration) -> Policy"],"urlPath":"control/retry"},{"anchor":"make-policy","kind":"make","line":80,"name":"Policy","qualifiedName":"Policy","signatures":["withMaximumElapsed(maximumElapsed)","withJitter(fraction)"],"summary":"","types":[],"urlPath":"control/retry"},{"anchor":"fn-withmaximumelapsed","kind":"function","line":90,"name":"withMaximumElapsed","qualifiedName":"Policy.withMaximumElapsed","signatures":["withMaximumElapsed(maximumElapsed)"],"summary":"Returns the same schedule with a bound on total scheduled sleep time.\n\nThe operation's own execution time is not counted; use operation-specific deadlines for that. A retry whose next delay would exceed this bound is not started.","types":[],"urlPath":"control/retry"},{"anchor":"fn-withjitter","kind":"function","line":111,"name":"withJitter","qualifiedName":"Policy.withJitter","signatures":["withJitter(fraction)"],"summary":"Returns the same schedule with symmetric bounded jitter. A fraction of `0.25` selects each actual delay from 75% through 125% of its scheduled value. Fractions are clamped to `0.0..1.0`.\n\nJitter prevents many workers that failed together from retrying together. It changes delay timing, never the number of attempts or the backoff cap.","types":[],"urlPath":"control/retry"},{"anchor":"function-run","kind":"function","line":134,"name":"run","qualifiedName":"Control.Retry.run","signatures":["run(policy, operation) : Policy -> Block<Result<X, E>> -> Result<X, E>","run(policy, operation) : Policy -> Predicate<E> -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Runs `operation` until it succeeds or exhausts the policy.\n\nThe last application error is returned unchanged. This helper performs no network-specific classification: callers decide what operation to wrap.","types":["Policy","Block<Result<X, E>>","Result<X, E>","Predicate<E>","(Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"function-runwith","kind":"function","line":164,"name":"runWith","qualifiedName":"Control.Retry.runWith","signatures":["runWith : Policy -> Predicate<E> -> Sleeper -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Runs with injected error classification and sleeping.\n\nThis is the deterministic testing seam: a fake sleeper can record durations or advance a virtual clock. Production callers normally use `Retry.run`.","types":["(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"function-runwithrandom","kind":"function","line":171,"name":"runWithRandom","qualifiedName":"Control.Retry.runWithRandom","signatures":["runWithRandom : Policy -> Predicate<E> -> Sleeper -> Block<Float> -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Runs with injected sleeping and a random source returning a value in `0.0..1.0`. Out-of-range test values are clamped. Production `run` uses a cryptographically secure backend source; this overload makes jitter specs deterministic.","types":["(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Float>) -> (Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"function-attempt","kind":"function","line":174,"name":"attempt","qualifiedName":"Control.Retry.attempt","signatures":["attempt(policy, predicate, sleeper, random, operation, number, delay, elapsed)"],"summary":"","types":[],"urlPath":"control/retry"},{"anchor":"module-control-retry-retry","kind":"module","line":202,"name":"Control.Retry.Retry","qualifiedName":"Control.Retry.Retry","signatures":["fixed(maximumAttempts, delay) : Integer -> Duration -> Policy","exponential : Integer -> Duration -> Duration -> Policy","run(policy, operation) : Policy -> Block<Result<X, E>> -> Result<X, E>","run(policy, operation) : Policy -> Predicate<E> -> Block<Result<X, E>> -> Result<X, E>","runWith : Policy -> Predicate<E> -> Sleeper -> Block<Result<X, E>> -> Result<X, E>","runWithRandom : Policy -> Predicate<E> -> Sleeper -> Block<Float> -> Block<Result<X, E>> -> Result<X, E>"],"summary":"The imported public namespace: `using Control.Retry` then `Retry.run(...)`.","types":["Integer","Duration","Policy","(Integer) -> (Duration) -> (Duration) -> Policy","Block<Result<X, E>>","Result<X, E>","Predicate<E>","(Block<Result<X, E>>) -> Result<X, E>","(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Result<X, E>>) -> Result<X, E>","(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Float>) -> (Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"function-fixed","kind":"function","line":204,"name":"fixed","qualifiedName":"Control.Retry.Retry.fixed","signatures":["fixed(maximumAttempts, delay) : Integer -> Duration -> Policy"],"summary":"Public imported alias of `Control.Retry.fixed`.","types":["Integer","Duration","Policy"],"urlPath":"control/retry"},{"anchor":"function-exponential","kind":"function","line":208,"name":"exponential","qualifiedName":"Control.Retry.Retry.exponential","signatures":["exponential : Integer -> Duration -> Duration -> Policy"],"summary":"Public imported alias of `Control.Retry.exponential`.","types":["(Integer) -> (Duration) -> (Duration) -> Policy"],"urlPath":"control/retry"},{"anchor":"function-run","kind":"function","line":212,"name":"run","qualifiedName":"Control.Retry.Retry.run","signatures":["run(policy, operation) : Policy -> Block<Result<X, E>> -> Result<X, E>","run(policy, operation) : Policy -> Predicate<E> -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Public imported alias of `Control.Retry.run`.","types":["Policy","Block<Result<X, E>>","Result<X, E>","Predicate<E>","(Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"function-runwith","kind":"function","line":220,"name":"runWith","qualifiedName":"Control.Retry.Retry.runWith","signatures":["runWith : Policy -> Predicate<E> -> Sleeper -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Deterministic seam with an injected sleeper, primarily for specifications.","types":["(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"function-runwithrandom","kind":"function","line":224,"name":"runWithRandom","qualifiedName":"Control.Retry.Retry.runWithRandom","signatures":["runWithRandom : Policy -> Predicate<E> -> Sleeper -> Block<Float> -> Block<Result<X, E>> -> Result<X, E>"],"summary":"Deterministic seam with injected sleeping and random sampling.","types":["(Policy) -> (Predicate<E>) -> (Sleeper) -> (Block<Float>) -> (Block<Result<X, E>>) -> Result<X, E>"],"urlPath":"control/retry"},{"anchor":"module-data","kind":"module","line":33,"name":"Data","qualifiedName":"Data","signatures":["from(items)","reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : Queue<A> -> Queue<A>","enqueue(value) : A -> Queue<A>","dequeue : (A, Queue<A>)?","peek : A?","==(other) : Queue<A> -> Bool","+(other) : Queue<A> -> Queue<A>","+(other) : [A] -> Queue<A>"],"summary":"A first-in-first-out queue.\n\nOpt-in: nothing here is in scope until `using Data.Queue`.\n\n```kex\nusing Data.Queue\n```\n\nA banker's queue: `front` holds the elements ready to leave, `back` holds what was most recently added, reversed. `enqueue` conses onto `back`; `dequeue` takes `front`'s head, and only rotates `back` (reversing it into `front`) when `front` runs out. Both operations are amortized O(1), versus the O(n) `enqueue` a single-list queue would pay for.\n\n```kex\nlet q = Queue.from([1, 2, 3])\nq.enqueue(4).items    # => [1, 2, 3, 4]\nq.dequeue             # => Just((1, Queue(2, 3)))\nq.peek                # => Just(1)\n```\n\n`items` is `@front ` @back.reverse`, so the queue is not opaque: it is always a real list you can hand to anything, the same promise `Data.Set` makes.\n\nUnlike a set, a queue's representation is NOT canonical: `Queue.from([1,2])` and `Queue.from([1]).enqueue(2)` hold the same elements in different `front`/`back` splits, so they are structurally unequal even though they answer the same to every method. `==` is therefore overloaded to compare `items` rather than the record fields directly, but that overload only reaches ordinary `==` calls. Two such queues used as map keys, or matched against each other as record patterns, still compare structurally on both backends, and can disagree with `==`.\n\nEvery method answers with a new queue rather than changing the receiver. `enqueue!` and `dequeue!` come free from the `!+ rebinding form.","types":["[A]","Queue<A>","B","(B) -> (A) -> B","A","(A, Queue<A>)?","A?","Bool"],"urlPath":"data/queue"},{"anchor":"record-queue","kind":"record","line":39,"name":"Queue","qualifiedName":"Data.Queue","signatures":[],"summary":"A queue of elements, split into a ready-to-leave `front` and a most-recently-added, reversed `back`.\n\nBuild one with `Queue.from` rather than by hand.","types":["[A]"],"urlPath":"data/queue"},{"anchor":"module-data-queue","kind":"module","line":45,"name":"Data.Queue","qualifiedName":"Data.Queue","signatures":["from(items)"],"summary":"Constructors for `Queue`.","types":["Queue<A>"],"urlPath":"data/queue"},{"anchor":"function-from","kind":"function","line":53,"name":"from","qualifiedName":"Data.Queue.from","signatures":["from(items)"],"summary":"Builds a queue from a list, front to back.","types":[],"urlPath":"data/queue"},{"anchor":"constant-empty","kind":"constant","line":61,"name":"empty","qualifiedName":"Data.Queue.empty","signatures":[],"summary":"The queue with no elements. Also the `Monoid` identity.","types":["Queue<A>"],"urlPath":"data/queue"},{"anchor":"make-queue<a>","kind":"make","line":64,"name":"Queue<A>","qualifiedName":"Queue<A>","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : Queue<A> -> Queue<A>","enqueue(value) : A -> Queue<A>","dequeue : (A, Queue<A>)?","peek : A?","==(other) : Queue<A> -> Bool","+(other) : Queue<A> -> Queue<A>","+(other) : [A] -> Queue<A>"],"summary":"","types":["B","(B) -> (A) -> B","Queue<A>","A","(A, Queue<A>)?","A?","Bool","[A]"],"urlPath":"data/queue"},{"anchor":"fn-reduce","kind":"function","line":75,"name":"reduce","qualifiedName":"Queue<A>.reduce","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B"],"summary":"Folds from front to back.\n\nThis is `Queue`'s `Enumerable`/`Foldable` primitive.","types":["B","(B) -> (A) -> B"],"urlPath":"data/queue"},{"anchor":"fn-combine","kind":"function","line":91,"name":"combine","qualifiedName":"Queue<A>.combine","signatures":["combine(other) : Queue<A> -> Queue<A>"],"summary":"Combines two queues, this one's elements followed by the argument's.","types":["Queue<A>"],"urlPath":"data/queue"},{"anchor":"fn-enqueue","kind":"function","line":112,"name":"enqueue","qualifiedName":"Queue<A>.enqueue","signatures":["enqueue(value) : A -> Queue<A>"],"summary":"Returns a new queue with `value` added at the back.\n\nUse `enqueue!` to rebind the receiver variable.","types":["A","Queue<A>"],"urlPath":"data/queue"},{"anchor":"fn-dequeue","kind":"function","line":129,"name":"dequeue","qualifiedName":"Queue<A>.dequeue","signatures":["dequeue : (A, Queue<A>)?"],"summary":"Returns the front element and the queue without it, wrapped in `Just`, or `None` for an empty queue.\n\nRotates `back` into `front` (reversing it) when `front` has run out: the one case that is not O(1), and only amortized so because each element is reversed at most once over the queue's lifetime.\n\nUse `dequeue!` to rebind the receiver variable.","types":["(A, Queue<A>)?"],"urlPath":"data/queue"},{"anchor":"fn-peek","kind":"function","line":146,"name":"peek","qualifiedName":"Queue<A>.peek","signatures":["peek : A?"],"summary":"Returns the front element wrapped in `Just`, or `None` for an empty queue.","types":["A?"],"urlPath":"data/queue"},{"anchor":"fn-==","kind":"function","line":178,"name":"==","qualifiedName":"Queue<A>.==","signatures":["==(other) : Queue<A> -> Bool"],"summary":"Compares two queues by their elements, front to back: NOT by their `front`/`back` split, which is not canonical. See the file header.","types":["Queue<A>","Bool"],"urlPath":"data/queue"},{"anchor":"fn-+","kind":"function","line":189,"name":"+","qualifiedName":"Queue<A>.+","signatures":["+(other) : Queue<A> -> Queue<A>","+(other) : [A] -> Queue<A>"],"summary":"Appends another queue's elements, or a plain list's.","types":["Queue<A>","[A]"],"urlPath":"data/queue"},{"anchor":"make-queue<a>","kind":"make","line":204,"name":"Queue<A>","qualifiedName":"Queue<A>","signatures":[],"summary":"","types":[],"urlPath":"data/queue"},{"anchor":"module-data","kind":"module","line":44,"name":"Data","qualifiedName":"Data","signatures":["from(items)","from(items)","reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : Set<A> -> Set<A>","contains?(value) : A -> Bool","add(value) : A -> Set<A>","delete(value) : A -> Set<A>","union(other) : Set<A> -> Set<A>","intersect(other) : Set<A> -> Set<A>","difference(other) : Set<A> -> Set<A>","symmetricDifference(other) : Set<A> -> Set<A>","subset?(other) : Set<A> -> Bool","superset?(other) : Set<A> -> Bool","disjoint?(other) : Set<A> -> Bool","+(other) : Set<A> -> Set<A>","+(other) : [A] -> Set<A>","-(other) : Set<A> -> Set<A>","-(other) : [A] -> Set<A>","map(f) : (A -> B) -> Set<B>","filter(pred) : (A -> Bool) -> Set<A>","reject(pred) : (A -> Bool) -> Set<A>","reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : UnorderedSet<A> -> UnorderedSet<A>","contains?(value) : A -> Bool","add(value) : A -> UnorderedSet<A>","delete(value) : A -> UnorderedSet<A>","union(other) : UnorderedSet<A> -> UnorderedSet<A>","intersect(other) : UnorderedSet<A> -> UnorderedSet<A>","difference(other) : UnorderedSet<A> -> UnorderedSet<A>","symmetricDifference(other) : UnorderedSet<A> -> UnorderedSet<A>","subset?(other) : UnorderedSet<A> -> Bool","superset?(other) : UnorderedSet<A> -> Bool","disjoint?(other) : UnorderedSet<A> -> Bool","+(other) : UnorderedSet<A> -> UnorderedSet<A>","+(other) : [A] -> UnorderedSet<A>","-(other) : UnorderedSet<A> -> UnorderedSet<A>","-(other) : [A] -> UnorderedSet<A>","map(f) : (A -> B) -> UnorderedSet<B>","filter(pred) : (A -> Bool) -> UnorderedSet<A>","reject(pred) : (A -> Bool) -> UnorderedSet<A>"],"summary":"Immutable collections of distinct elements.\n\nOpt-in: nothing here is in scope until `using Data.Set`, which brings both flavours below into scope at once.\n\n```kex\nusing Data.Set\n```\n\nMembership is decided by structural equality: the same equality `==` and map keys use, so records and tuples are compared by value, not identity. Every method answers with a new set; the `!` forms (`add!`, `delete!`) build a new set and rebind the receiver variable rather than modifying anything in place.\n\n```kex\nlet tags = Set.from([\"kex\", \"beam\", \"kex\"])\ntags.count                     # => 2\ntags.contains?(\"beam\")         # => true\ntags.add(\"erlang\").items       # => [\"beam\", \"erlang\", \"kex\"]\n```\n\nThere are two flavours, differing only in how they store their elements:\n\n```kex\nSet           sorted, so iteration is in ascending element order and the\n              elements must be Orderable.\nUnorderedSet  hash-backed, so membership does not pay for ordering and\n              the elements need only be comparable. Iteration order is\n              unspecified: never write a test against it.\n```\n\nReach for `Set` when you will read the elements back out, and for `UnorderedSet` when the set exists to answer `contains?` quickly.\n\n`==` between two sets needs no overload of its own: each flavour keeps its backing canonical (sorted and duplicate free, or a map) so comparing the records structurally already IS set equality.\n\nBoth wrap structures the runtime already has (a list and a map), so no set is opaque: `.items` is always a real list you can hand to anything.\n\nThose two backings are also the ones the BEAM's own set libraries use: a `Set` is laid out exactly like an `ordsets` term and an `UnorderedSet` exactly like a `sets` v2 term, so the operations here can be routed to the native BIFs later without changing what a set IS. What rules out adopting `gb_sets` instead is the other backend: a tree-walk interpreter cannot produce an opaque BEAM term, and a set that only one backend can build is not a set the prelude can offer.","types":["[A]","{A: Bool}","Set<A>","UnorderedSet<A>","B","(B) -> (A) -> B","A","Bool","(A) -> B","Set<B>","(A) -> Bool","UnorderedSet<B>"],"urlPath":"data/set"},{"anchor":"record-set","kind":"record","line":54,"name":"Set","qualifiedName":"Data.Set","signatures":[],"summary":"A set whose elements are kept sorted and duplicate free.\n\nBuild one with `Set.from` rather than by hand: the record literal does no deduplication and no sorting, and every method here relies on both. Reading `items` back is the field itself, so handing a set's elements to list code costs nothing.\n\n```kex\nSet.from([3, 1, 2]).items   # => [1, 2, 3]\n```","types":["[A]"],"urlPath":"data/set"},{"anchor":"record-unorderedset","kind":"record","line":64,"name":"UnorderedSet","qualifiedName":"Data.UnorderedSet","signatures":[],"summary":"A set backed by a map from each member to `true`; its keys ARE the elements.\n\nBuild one with `UnorderedSet.from`. Iteration order is whatever the map hands back, so use `items.sort` when you need a stable order.\n\n```kex\nUnorderedSet.from([3, 1, 2]).contains?(2)   # => true\n```","types":["{A: Bool}"],"urlPath":"data/set"},{"anchor":"module-data-set","kind":"module","line":69,"name":"Data.Set","qualifiedName":"Data.Set","signatures":["from(items)"],"summary":"Constructors for the sorted `Set`.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"function-from","kind":"function","line":86,"name":"from","qualifiedName":"Data.Set.from","signatures":["from(items)"],"summary":"Builds a sorted set from a list, discarding duplicates.\n\nThis is the normal way to make a `Set`. Deduplication goes through a map rather than `List.uniq`: map keys are unique under exactly the structural equality a set wants, and each element costs one insertion instead of a scan of everything kept so far.","types":[],"urlPath":"data/set"},{"anchor":"constant-empty","kind":"constant","line":99,"name":"empty","qualifiedName":"Data.Set.empty","signatures":[],"summary":"The set with no elements. Also the `Monoid` identity, so `s.combine(Set.empty)` is `s`.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"module-data-unorderedset","kind":"module","line":103,"name":"Data.UnorderedSet","qualifiedName":"Data.UnorderedSet","signatures":["from(items)"],"summary":"Constructors for the hash-backed `UnorderedSet`.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"function-from","kind":"function","line":118,"name":"from","qualifiedName":"Data.UnorderedSet.from","signatures":["from(items)"],"summary":"Builds an unordered set from a list, discarding duplicates.\n\nNothing is sorted, so unlike `Set.from` this does not require the elements to be `Orderable`.","types":[],"urlPath":"data/set"},{"anchor":"constant-empty","kind":"constant","line":128,"name":"empty","qualifiedName":"Data.UnorderedSet.empty","signatures":[],"summary":"The unordered set with no elements. Also the `Monoid` identity.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"make-set<a>","kind":"make","line":131,"name":"Set<A>","qualifiedName":"Set<A>","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : Set<A> -> Set<A>","contains?(value) : A -> Bool","add(value) : A -> Set<A>","delete(value) : A -> Set<A>","union(other) : Set<A> -> Set<A>","intersect(other) : Set<A> -> Set<A>","difference(other) : Set<A> -> Set<A>","symmetricDifference(other) : Set<A> -> Set<A>","subset?(other) : Set<A> -> Bool","superset?(other) : Set<A> -> Bool","disjoint?(other) : Set<A> -> Bool","+(other) : Set<A> -> Set<A>","+(other) : [A] -> Set<A>","-(other) : Set<A> -> Set<A>","-(other) : [A] -> Set<A>","map(f) : (A -> B) -> Set<B>","filter(pred) : (A -> Bool) -> Set<A>","reject(pred) : (A -> Bool) -> Set<A>"],"summary":"","types":["B","(B) -> (A) -> B","Set<A>","A","Bool","[A]","(A) -> B","Set<B>","(A) -> Bool"],"urlPath":"data/set"},{"anchor":"fn-reduce","kind":"function","line":144,"name":"reduce","qualifiedName":"Set<A>.reduce","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B"],"summary":"Folds over the elements in ascending order.\n\nThis is `Set`'s `Enumerable` primitive; `each`, `find`, `any?` and the rest are built on it. The collection-returning operations are overridden below, because `Enumerable`'s defaults answer with a list.","types":["B","(B) -> (A) -> B"],"urlPath":"data/set"},{"anchor":"fn-combine","kind":"function","line":163,"name":"combine","qualifiedName":"Set<A>.combine","signatures":["combine(other) : Set<A> -> Set<A>"],"summary":"Combines two sets by union. The `Monoid` operation.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"fn-contains?","kind":"function","line":178,"name":"contains?","qualifiedName":"Set<A>.contains?","signatures":["contains?(value) : A -> Bool"],"summary":"Returns `true` when `value` is a member.","types":["A","Bool"],"urlPath":"data/set"},{"anchor":"fn-add","kind":"function","line":220,"name":"add","qualifiedName":"Set<A>.add","signatures":["add(value) : A -> Set<A>"],"summary":"Returns a new set with `value` added. Adding an element that is already a member changes nothing: that is what makes a set a set.\n\nUse `add!` to rebind the receiver variable.","types":["A","Set<A>"],"urlPath":"data/set"},{"anchor":"fn-delete","kind":"function","line":234,"name":"delete","qualifiedName":"Set<A>.delete","signatures":["delete(value) : A -> Set<A>"],"summary":"Returns a new set without `value`. Removing something that is not a member changes nothing.\n\nUse `delete!` to rebind the receiver variable.","types":["A","Set<A>"],"urlPath":"data/set"},{"anchor":"fn-union","kind":"function","line":247,"name":"union","qualifiedName":"Set<A>.union","signatures":["union(other) : Set<A> -> Set<A>"],"summary":"Returns every element of either set.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"fn-intersect","kind":"function","line":261,"name":"intersect","qualifiedName":"Set<A>.intersect","signatures":["intersect(other) : Set<A> -> Set<A>"],"summary":"Returns the elements both sets have.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"fn-difference","kind":"function","line":280,"name":"difference","qualifiedName":"Set<A>.difference","signatures":["difference(other) : Set<A> -> Set<A>"],"summary":"Returns the elements of this set that `other` does not have.\n\nOrder matters: `a.difference(b)` and `b.difference(a)` are different questions. Use `symmetricDifference` when you want both answers.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"fn-symmetricdifference","kind":"function","line":297,"name":"symmetricDifference","qualifiedName":"Set<A>.symmetricDifference","signatures":["symmetricDifference(other) : Set<A> -> Set<A>"],"summary":"Returns the elements in exactly one of the two sets: everything they do not agree on.","types":["Set<A>"],"urlPath":"data/set"},{"anchor":"fn-subset?","kind":"function","line":312,"name":"subset?","qualifiedName":"Set<A>.subset?","signatures":["subset?(other) : Set<A> -> Bool"],"summary":"Returns `true` when every element of this set is also in `other`. The empty set is a subset of everything.","types":["Set<A>","Bool"],"urlPath":"data/set"},{"anchor":"fn-superset?","kind":"function","line":327,"name":"superset?","qualifiedName":"Set<A>.superset?","signatures":["superset?(other) : Set<A> -> Bool"],"summary":"Returns `true` when this set has every element of `other`. The mirror image of `subset?`.","types":["Set<A>","Bool"],"urlPath":"data/set"},{"anchor":"fn-disjoint?","kind":"function","line":341,"name":"disjoint?","qualifiedName":"Set<A>.disjoint?","signatures":["disjoint?(other) : Set<A> -> Bool"],"summary":"Returns `true` when the two sets share no element.","types":["Set<A>","Bool"],"urlPath":"data/set"},{"anchor":"fn-+","kind":"function","line":358,"name":"+","qualifiedName":"Set<A>.+","signatures":["+(other) : Set<A> -> Set<A>","+(other) : [A] -> Set<A>"],"summary":"Unions with another set, or with a plain list.\n\nThe list form is the everyday way to add one element without naming a method: `s ` [x]`.","types":["Set<A>","[A]"],"urlPath":"data/set"},{"anchor":"fn--","kind":"function","line":371,"name":"-","qualifiedName":"Set<A>.-","signatures":["-(other) : Set<A> -> Set<A>","-(other) : [A] -> Set<A>"],"summary":"Removes another set's elements, or a plain list's.","types":["Set<A>","[A]"],"urlPath":"data/set"},{"anchor":"fn-map","kind":"function","line":391,"name":"map","qualifiedName":"Set<A>.map","signatures":["map(f) : (A -> B) -> Set<B>"],"summary":"Applies `f` to every element and returns a set of the results.\n\nMapping a set may collapse elements: if `f` sends two members to the same value, the result has one. That is not a loss of information so much as the point of a set: `Set.from([1, -1]).map(~abs)` has one member.","types":["(A) -> B","Set<B>"],"urlPath":"data/set"},{"anchor":"fn-filter","kind":"function","line":401,"name":"filter","qualifiedName":"Set<A>.filter","signatures":["filter(pred) : (A -> Bool) -> Set<A>"],"summary":"Returns a new set with only the elements `pred` accepts.","types":["(A) -> Bool","Set<A>"],"urlPath":"data/set"},{"anchor":"fn-reject","kind":"function","line":412,"name":"reject","qualifiedName":"Set<A>.reject","signatures":["reject(pred) : (A -> Bool) -> Set<A>"],"summary":"Returns a new set without the elements `pred` accepts. The complement of `filter`.","types":["(A) -> Bool","Set<A>"],"urlPath":"data/set"},{"anchor":"make-set<a>","kind":"make","line":426,"name":"Set<A>","qualifiedName":"Set<A>","signatures":[],"summary":"","types":[],"urlPath":"data/set"},{"anchor":"make-unorderedset<a>","kind":"make","line":439,"name":"UnorderedSet<A>","qualifiedName":"UnorderedSet<A>","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : UnorderedSet<A> -> UnorderedSet<A>","contains?(value) : A -> Bool","add(value) : A -> UnorderedSet<A>","delete(value) : A -> UnorderedSet<A>","union(other) : UnorderedSet<A> -> UnorderedSet<A>","intersect(other) : UnorderedSet<A> -> UnorderedSet<A>","difference(other) : UnorderedSet<A> -> UnorderedSet<A>","symmetricDifference(other) : UnorderedSet<A> -> UnorderedSet<A>","subset?(other) : UnorderedSet<A> -> Bool","superset?(other) : UnorderedSet<A> -> Bool","disjoint?(other) : UnorderedSet<A> -> Bool","+(other) : UnorderedSet<A> -> UnorderedSet<A>","+(other) : [A] -> UnorderedSet<A>","-(other) : UnorderedSet<A> -> UnorderedSet<A>","-(other) : [A] -> UnorderedSet<A>","map(f) : (A -> B) -> UnorderedSet<B>","filter(pred) : (A -> Bool) -> UnorderedSet<A>","reject(pred) : (A -> Bool) -> UnorderedSet<A>"],"summary":"","types":["B","(B) -> (A) -> B","UnorderedSet<A>","A","Bool","[A]","(A) -> B","UnorderedSet<B>","(A) -> Bool"],"urlPath":"data/set"},{"anchor":"fn-reduce","kind":"function","line":451,"name":"reduce","qualifiedName":"UnorderedSet<A>.reduce","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B"],"summary":"Folds over the elements.\n\nThe order is whatever the underlying map hands back: unspecified, and not to be relied on. Use a `Set` when the order of the fold matters.","types":["B","(B) -> (A) -> B"],"urlPath":"data/set"},{"anchor":"fn-combine","kind":"function","line":464,"name":"combine","qualifiedName":"UnorderedSet<A>.combine","signatures":["combine(other) : UnorderedSet<A> -> UnorderedSet<A>"],"summary":"Combines two sets by union. The `Monoid` operation.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-contains?","kind":"function","line":478,"name":"contains?","qualifiedName":"UnorderedSet<A>.contains?","signatures":["contains?(value) : A -> Bool"],"summary":"Returns `true` when `value` is a member.\n\nThis is the operation the flavour exists for: a map lookup, with no ordering to maintain.","types":["A","Bool"],"urlPath":"data/set"},{"anchor":"fn-add","kind":"function","line":522,"name":"add","qualifiedName":"UnorderedSet<A>.add","signatures":["add(value) : A -> UnorderedSet<A>"],"summary":"Returns a new set with `value` added. Adding an existing member changes nothing.\n\nUse `add!` to rebind the receiver variable.","types":["A","UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-delete","kind":"function","line":534,"name":"delete","qualifiedName":"UnorderedSet<A>.delete","signatures":["delete(value) : A -> UnorderedSet<A>"],"summary":"Returns a new set without `value`. Removing a non-member changes nothing.\n\nUse `delete!` to rebind the receiver variable.","types":["A","UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-union","kind":"function","line":544,"name":"union","qualifiedName":"UnorderedSet<A>.union","signatures":["union(other) : UnorderedSet<A> -> UnorderedSet<A>"],"summary":"Returns every element of either set.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-intersect","kind":"function","line":555,"name":"intersect","qualifiedName":"UnorderedSet<A>.intersect","signatures":["intersect(other) : UnorderedSet<A> -> UnorderedSet<A>"],"summary":"Returns the elements both sets have.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-difference","kind":"function","line":566,"name":"difference","qualifiedName":"UnorderedSet<A>.difference","signatures":["difference(other) : UnorderedSet<A> -> UnorderedSet<A>"],"summary":"Returns the elements of this set that `other` does not have.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-symmetricdifference","kind":"function","line":577,"name":"symmetricDifference","qualifiedName":"UnorderedSet<A>.symmetricDifference","signatures":["symmetricDifference(other) : UnorderedSet<A> -> UnorderedSet<A>"],"summary":"Returns the elements in exactly one of the two sets.","types":["UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-subset?","kind":"function","line":587,"name":"subset?","qualifiedName":"UnorderedSet<A>.subset?","signatures":["subset?(other) : UnorderedSet<A> -> Bool"],"summary":"Returns `true` when every element of this set is also in `other`.","types":["UnorderedSet<A>","Bool"],"urlPath":"data/set"},{"anchor":"fn-superset?","kind":"function","line":597,"name":"superset?","qualifiedName":"UnorderedSet<A>.superset?","signatures":["superset?(other) : UnorderedSet<A> -> Bool"],"summary":"Returns `true` when this set has every element of `other`.","types":["UnorderedSet<A>","Bool"],"urlPath":"data/set"},{"anchor":"fn-disjoint?","kind":"function","line":607,"name":"disjoint?","qualifiedName":"UnorderedSet<A>.disjoint?","signatures":["disjoint?(other) : UnorderedSet<A> -> Bool"],"summary":"Returns `true` when the two sets share no element.","types":["UnorderedSet<A>","Bool"],"urlPath":"data/set"},{"anchor":"fn-+","kind":"function","line":617,"name":"+","qualifiedName":"UnorderedSet<A>.+","signatures":["+(other) : UnorderedSet<A> -> UnorderedSet<A>","+(other) : [A] -> UnorderedSet<A>"],"summary":"Unions with another unordered set, or with a plain list.","types":["UnorderedSet<A>","[A]"],"urlPath":"data/set"},{"anchor":"fn--","kind":"function","line":629,"name":"-","qualifiedName":"UnorderedSet<A>.-","signatures":["-(other) : UnorderedSet<A> -> UnorderedSet<A>","-(other) : [A] -> UnorderedSet<A>"],"summary":"Removes another unordered set's elements, or a plain list's.","types":["UnorderedSet<A>","[A]"],"urlPath":"data/set"},{"anchor":"fn-map","kind":"function","line":642,"name":"map","qualifiedName":"UnorderedSet<A>.map","signatures":["map(f) : (A -> B) -> UnorderedSet<B>"],"summary":"Applies `f` to every element and returns an unordered set of the results. Elements that map to the same value collapse into one.","types":["(A) -> B","UnorderedSet<B>"],"urlPath":"data/set"},{"anchor":"fn-filter","kind":"function","line":652,"name":"filter","qualifiedName":"UnorderedSet<A>.filter","signatures":["filter(pred) : (A -> Bool) -> UnorderedSet<A>"],"summary":"Returns a new unordered set with only the elements `pred` accepts.","types":["(A) -> Bool","UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"fn-reject","kind":"function","line":662,"name":"reject","qualifiedName":"UnorderedSet<A>.reject","signatures":["reject(pred) : (A -> Bool) -> UnorderedSet<A>"],"summary":"Returns a new unordered set without the elements `pred` accepts.","types":["(A) -> Bool","UnorderedSet<A>"],"urlPath":"data/set"},{"anchor":"make-unorderedset<a>","kind":"make","line":678,"name":"UnorderedSet<A>","qualifiedName":"UnorderedSet<A>","signatures":[],"summary":"","types":[],"urlPath":"data/set"},{"anchor":"module-data","kind":"module","line":22,"name":"Data","qualifiedName":"Data","signatures":["from(items)","reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : Stack<A> -> Stack<A>","push(value) : A -> Stack<A>","pop : (A, Stack<A>)?","peek : A?","+(other) : Stack<A> -> Stack<A>","+(other) : [A] -> Stack<A>"],"summary":"A last-in-first-out stack.\n\nOpt-in: nothing here is in scope until `using Data.Stack`.\n\n```kex\nusing Data.Stack\n```\n\nElements are stored top first, so `push`, `pop` and `peek` are all list-head operations: none of them pay for the size of the stack. `items` reverses that internal order, so it reads bottom-to-top, the order you would have pushed them in:\n\n```kex\nlet s = Stack.from([1, 2, 3])\ns.peek                # => Just(3)\ns.push(4).items       # => [1, 2, 3, 4]\ns.pop                 # => Just((3, Stack(1, 2)))\nStack.empty.pop       # => None\n```\n\nEvery method answers with a new stack rather than changing the receiver. `push!` and `pop!` come free from the `!` rebinding form, the same as `add!`/`delete!` do for `Data.Set`: they build a new stack and rebind the receiver variable rather than modifying anything in place.","types":["[A]","Stack<A>","B","(B) -> (A) -> B","A","(A, Stack<A>)?","A?"],"urlPath":"data/stack"},{"anchor":"record-stack","kind":"record","line":31,"name":"Stack","qualifiedName":"Data.Stack","signatures":[],"summary":"A stack of elements, held top first.\n\nBuild one with `Stack.from` rather than by hand: the record literal takes elements in storage order (top first), which reads backwards from the `items` a caller normally thinks in.\n\n```kex\nStack.from([1, 2, 3]).items   # => [1, 2, 3]\n```","types":["[A]"],"urlPath":"data/stack"},{"anchor":"module-data-stack","kind":"module","line":36,"name":"Data.Stack","qualifiedName":"Data.Stack","signatures":["from(items)"],"summary":"Constructors for `Stack`.","types":["Stack<A>"],"urlPath":"data/stack"},{"anchor":"function-from","kind":"function","line":45,"name":"from","qualifiedName":"Data.Stack.from","signatures":["from(items)"],"summary":"Builds a stack from a list, read bottom-to-top: the last element is on top.","types":[],"urlPath":"data/stack"},{"anchor":"constant-empty","kind":"constant","line":53,"name":"empty","qualifiedName":"Data.Stack.empty","signatures":[],"summary":"The stack with no elements. Also the `Monoid` identity.","types":["Stack<A>"],"urlPath":"data/stack"},{"anchor":"make-stack<a>","kind":"make","line":56,"name":"Stack<A>","qualifiedName":"Stack<A>","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B","combine(other) : Stack<A> -> Stack<A>","push(value) : A -> Stack<A>","pop : (A, Stack<A>)?","peek : A?","+(other) : Stack<A> -> Stack<A>","+(other) : [A] -> Stack<A>"],"summary":"","types":["B","(B) -> (A) -> B","Stack<A>","A","(A, Stack<A>)?","A?","[A]"],"urlPath":"data/stack"},{"anchor":"fn-reduce","kind":"function","line":67,"name":"reduce","qualifiedName":"Stack<A>.reduce","signatures":["reduce(acc, f) : B -> (B -> A -> B) -> B"],"summary":"Folds from the top down to the bottom.\n\nThis is `Stack`'s `Enumerable`/`Foldable` primitive.","types":["B","(B) -> (A) -> B"],"urlPath":"data/stack"},{"anchor":"fn-combine","kind":"function","line":84,"name":"combine","qualifiedName":"Stack<A>.combine","signatures":["combine(other) : Stack<A> -> Stack<A>"],"summary":"Combines two stacks by pushing the argument's elements on top of this one, top element last.","types":["Stack<A>"],"urlPath":"data/stack"},{"anchor":"fn-push","kind":"function","line":106,"name":"push","qualifiedName":"Stack<A>.push","signatures":["push(value) : A -> Stack<A>"],"summary":"Returns a new stack with `value` pushed on top.\n\nUse `push!` to rebind the receiver variable.","types":["A","Stack<A>"],"urlPath":"data/stack"},{"anchor":"fn-pop","kind":"function","line":119,"name":"pop","qualifiedName":"Stack<A>.pop","signatures":["pop : (A, Stack<A>)?"],"summary":"Returns the top element and the stack without it, wrapped in `Just`, or `None` for an empty stack.\n\nUse `pop!` to rebind the receiver variable.","types":["(A, Stack<A>)?"],"urlPath":"data/stack"},{"anchor":"fn-peek","kind":"function","line":130,"name":"peek","qualifiedName":"Stack<A>.peek","signatures":["peek : A?"],"summary":"Returns the top element wrapped in `Just`, or `None` for an empty stack.","types":["A?"],"urlPath":"data/stack"},{"anchor":"fn-+","kind":"function","line":164,"name":"+","qualifiedName":"Stack<A>.+","signatures":["+(other) : Stack<A> -> Stack<A>","+(other) : [A] -> Stack<A>"],"summary":"Pushes another stack's elements, or a plain list's, on top.\n\nThe list form reads bottom-to-top, the same as `Stack.from`: the last element of the list ends up on top.","types":["Stack<A>","[A]"],"urlPath":"data/stack"},{"anchor":"make-stack<a>","kind":"make","line":179,"name":"Stack<A>","qualifiedName":"Stack<A>","signatures":[],"summary":"","types":[],"urlPath":"data/stack"},{"anchor":"module-kex-ast","kind":"module","line":22,"name":"Kex.AST","qualifiedName":"Kex.AST","signatures":["parse(source) : String -> Result<Program, ParseError>","parse(source) : String -> String -> Result<Program, ParseError>","parseFile(path) : FS.FilePath -> Result<Program, ParseError>","parseType(source) : String -> Result<TypeRef, ParseError>","parseExpression(source) : String -> Result<Expression, ParseError>","typeRefText(NamedType(name, []))","patternRefText(BindPattern(name))","patternFieldText(PatternField { name, pattern, stringKey })","referenceText(NamedType(name, args))","to(String)"],"summary":"Parses Kex source code into a structured AST, at run time.\n\nOpt-in: nothing here is in scope until `using Kex.AST`.\n\nThis is the entry point for tools that read Kex source: linters, formatters, documentation generators, code search. The AST includes module definitions, function signatures, type/record definitions, traits, make blocks, and the doc-comments extracted from `#` lines, which is how the standard library's own documentation is generated.\n\n```kex\nusing Kex.AST\n\nmain do\n  match Kex.AST.parseFile(\"src/main.kex\") do\n    Ok(program) => IO.printLine(\"${program.items.count} top-level items\")\n    Error(e)    => IO.printError(e.message)\n  end\nend\n```\n\nEverything answers a `Result`, so a source file that does not parse is a value you handle rather than an exception.","types":["String","Integer","[Node]","Location?","Result<Program, ParseError>","(String) -> Result<Program, ParseError>","FS.FilePath","Result<TypeRef, ParseError>","Result<Expression, ParseError>","[TypeRef]","TypeRef","[(String, TypeRef)]","Expression","[PatternRef]","PatternRef?","String?","[PatternField]","PatternRef","Bool","Int","Float","[String]","[Expression]","[NamedArgument]","Expression?","TypeRef?","[ElseIf]","[Expression]?","[MatchArm]","[MapItem]","[RecordField]","[LambdaParam]","RescueInfo?","[[Expression]]","GeneratedTemplate","RescueInfo","Node","GeneratedMakeInfo","[CompiledItem]","Location","[ParamInfo]","[ClauseInfo]","[VariantInfo]?","[FieldInfo]","ModuleInfo","FunctionInfo","AnnotationInfo","TypeInfo","RecordInfo","TraitInfo","MakeInfo","PragmaInfo","ConstantInfo","MainInfo","VisibilityInfo","UsingInfo","ExportInfo","CompiledInfo"],"urlPath":"kex/ast"},{"anchor":"record-location","kind":"record","line":27,"name":"Location","qualifiedName":"Kex.AST.Location","signatures":[],"summary":"Where in a source file something appeared.\n\nLine and column are 1-based, for reporting to a person; the offsets are 0-based byte positions, for slicing the source.","types":["String","Integer"],"urlPath":"kex/ast"},{"anchor":"record-program","kind":"record","line":45,"name":"Program","qualifiedName":"Kex.AST.Program","signatures":[],"summary":"A parsed source file: its schema version, and its top-level items.","types":["Integer","[Node]"],"urlPath":"kex/ast"},{"anchor":"record-parseerror","kind":"record","line":56,"name":"ParseError","qualifiedName":"Kex.AST.ParseError","signatures":[],"summary":"Why a source file could not be parsed.","types":["String","Location?"],"urlPath":"kex/ast"},{"anchor":"function-parse","kind":"function","line":80,"name":"parse","qualifiedName":"Kex.AST.parse","signatures":["parse(source) : String -> Result<Program, ParseError>","parse(source) : String -> String -> Result<Program, ParseError>"],"summary":"Parses Kex source text into a structured AST.\n\nPass `filename` when you have one: it is what appears in every `Location` and in the error message, so diagnostics can point at a real file.","types":["String","Result<Program, ParseError>","(String) -> Result<Program, ParseError>"],"urlPath":"kex/ast"},{"anchor":"function-parsefile","kind":"function","line":103,"name":"parseFile","qualifiedName":"Kex.AST.parseFile","signatures":["parseFile(path) : FS.FilePath -> Result<Program, ParseError>"],"summary":"Reads a file and parses it, reporting locations against its path.\n\nA file that cannot be read is an `Error` like one that cannot be parsed.","types":["FS.FilePath","Result<Program, ParseError>"],"urlPath":"kex/ast"},{"anchor":"function-parsetype","kind":"function","line":119,"name":"parseType","qualifiedName":"Kex.AST.parseType","signatures":["parseType(source) : String -> Result<TypeRef, ParseError>"],"summary":"Parses a type expression on its own, without a surrounding program.\n\nUse it to read a type written in data: a signature in a config file, a type named on a command line. `typeRefText` renders the result back.","types":["String","Result<TypeRef, ParseError>"],"urlPath":"kex/ast"},{"anchor":"function-parseexpression","kind":"function","line":133,"name":"parseExpression","qualifiedName":"Kex.AST.parseExpression","signatures":["parseExpression(source) : String -> Result<Expression, ParseError>"],"summary":"Parses a single expression on its own, without a surrounding program.\n\nThis gives you the expression's SHAPE. To evaluate one instead, use `Evaluator.runExpression`.","types":["String","Result<Expression, ParseError>"],"urlPath":"kex/ast"},{"anchor":"type-typeref","kind":"type","line":139,"name":"TypeRef","qualifiedName":"Kex.AST.TypeRef","signatures":[],"summary":"A type as it was written in source.\n\n`typeRefText` renders one back to the source spelling.","types":["String","[TypeRef]","[TypeRef]","TypeRef","[TypeRef]","TypeRef","TypeRef","TypeRef","[TypeRef]","[TypeRef]","[(String, TypeRef)]","TypeRef","TypeRef","String","String","Expression","String"],"urlPath":"kex/ast"},{"anchor":"function-typereftext","kind":"function","line":170,"name":"typeRefText","qualifiedName":"Kex.AST.typeRefText","signatures":["typeRefText(NamedType(name, []))"],"summary":"Renders a `TypeRef` back to the way it is written in source.\n\nA list reads as `[Integer]`, a map as `{String: Integer}`, an optional as `String?`: the spelling a reader would recognise, not the constructor tree behind it.","types":[],"urlPath":"kex/ast"},{"anchor":"type-patternref","kind":"type","line":195,"name":"PatternRef","qualifiedName":"Kex.AST.PatternRef","signatures":[],"summary":"Structured representation of patterns.\n\nPattern nodes describe what a declaration or match arm accepts; they do not contain runtime values. A linter can distinguish a wildcard from a binding, for example, without reparsing source text.","types":["String","String","String","[PatternRef]","[PatternRef]","[PatternRef]","PatternRef?","String?","[PatternField]","PatternRef","PatternRef","PatternRef"],"urlPath":"kex/ast"},{"anchor":"record-patternfield","kind":"record","line":210,"name":"PatternField","qualifiedName":"Kex.AST.PatternField","signatures":[],"summary":"One field inside a record or map-shaped pattern.\n\n`pattern` is `None` for shorthand such as `{ name }`. `stringKey` keeps `{\"name\": value}` distinct from the atom-key spelling `{ name: value }`.","types":["String","PatternRef?","Bool"],"urlPath":"kex/ast"},{"anchor":"function-patternreftext","kind":"function","line":224,"name":"patternRefText","qualifiedName":"Kex.AST.patternRefText","signatures":["patternRefText(BindPattern(name))"],"summary":"Renders a `PatternRef` back to the way it is written in source.","types":[],"urlPath":"kex/ast"},{"anchor":"function-patternfieldtext","kind":"function","line":237,"name":"patternFieldText","qualifiedName":"Kex.AST.patternFieldText","signatures":["patternFieldText(PatternField { name, pattern, stringKey })"],"summary":"","types":[],"urlPath":"kex/ast"},{"anchor":"function-referencetext","kind":"function","line":254,"name":"referenceText","qualifiedName":"Kex.AST.referenceText","signatures":["referenceText(NamedType(name, args))"],"summary":"Renders either a type or a pattern back to source.\n\nThe one call to reach for when a node may carry either: it dispatches to `typeRefText` or `patternRefText` as appropriate.","types":[],"urlPath":"kex/ast"},{"anchor":"type-expression","kind":"type","line":284,"name":"Expression","qualifiedName":"Kex.AST.Expression","signatures":[],"summary":"Structured representation of expression AST nodes.\n\nExpressions retain syntax-level distinctions that matter to tools: a method call is not flattened into a generic call, `var` is distinct from `let`, and a trailing `if` remains recognizable. Walk these constructors when writing a linter or code search; use `Evaluator` when the goal is to execute an expression rather than inspect it.","types":["Int","Float","Int","String","[String]","[Expression]","Bool","String","String","Expression","String","Expression","String","Expression","Expression","[Expression]","[NamedArgument]","Expression?","String","[String]","[Expression]","Expression","String","[Expression]","[NamedArgument]","Expression?","Bool","Bool","TypeRef?","Expression","PatternRef?","[Expression]","[ElseIf]","[Expression]?","Expression","String?","[MatchArm]","String?","[MatchArm]","Expression?","Expression?","[Expression]","Expression?","[MapItem]","String","[RecordField]","[Expression]","[Expression]","[LambdaParam]","[Expression]","TypeRef?","RescueInfo?","PatternRef","TypeRef?","Expression","String","TypeRef?","Expression","String","Expression","Expression","[Expression]","Expression","Expression","Expression","Expression","Expression","Expression","Expression","String","[Expression]","Bool","String","String?","Bool","[[Expression]]","String","String?","[String]","[String]","[Expression]","String","Expression","[Expression]","Expression","GeneratedTemplate","String","[Expression]","RescueInfo","Expression","[Expression]","[String]","[Expression]","Expression","Expression"],"urlPath":"kex/ast"},{"anchor":"record-namedargument","kind":"record","line":333,"name":"NamedArgument","qualifiedName":"Kex.AST.NamedArgument","signatures":[],"summary":"One `name: value` argument at a call site.","types":["String","Expression"],"urlPath":"kex/ast"},{"anchor":"record-matcharm","kind":"record","line":342,"name":"MatchArm","qualifiedName":"Kex.AST.MatchArm","signatures":[],"summary":"One arm of `match`, `receive`, or `rescue`.\n\nMultiple `patterns` are the comma-separated alternatives on the left of the arrow. `guard` is absent when the arm has no `when` condition.","types":["[PatternRef]","Expression?","Expression"],"urlPath":"kex/ast"},{"anchor":"record-lambdaparam","kind":"record","line":349,"name":"LambdaParam","qualifiedName":"Kex.AST.LambdaParam","signatures":[],"summary":"One lambda parameter and its optional source annotation.","types":["String","TypeRef?"],"urlPath":"kex/ast"},{"anchor":"record-rescueinfo","kind":"record","line":359,"name":"RescueInfo","qualifiedName":"Kex.AST.RescueInfo","signatures":[],"summary":"The structured recovery clauses attached to a function or expression.\n\nNamed rescue arms live in `arms`; a catch-all rescue keeps its optional binding and body separately. `inlineReturn` represents the compact rescue form rather than inventing a synthetic block.","types":["[MatchArm]","String?","[Expression]","Expression?"],"urlPath":"kex/ast"},{"anchor":"record-elseif","kind":"record","line":367,"name":"ElseIf","qualifiedName":"Kex.AST.ElseIf","signatures":[],"summary":"One `elif` branch, in source order.","types":["Expression","[Expression]"],"urlPath":"kex/ast"},{"anchor":"type-mapitem","kind":"type","line":373,"name":"MapItem","qualifiedName":"Kex.AST.MapItem","signatures":[],"summary":"One entry in a map literal: either a key/value pair or `...spread`.","types":["Expression","Expression","Expression"],"urlPath":"kex/ast"},{"anchor":"record-recordfield","kind":"record","line":378,"name":"RecordField","qualifiedName":"Kex.AST.RecordField","signatures":[],"summary":"One explicitly initialized field in a record literal.","types":["String","Expression"],"urlPath":"kex/ast"},{"anchor":"type-generatedtemplate","kind":"type","line":388,"name":"GeneratedTemplate","qualifiedName":"Kex.AST.GeneratedTemplate","signatures":[],"summary":"A declaration template whose name (and, for a make block, target) is computed by a `compiled do` expression.\n\nTools normally encounter this only while inspecting metaprogramming code. After expansion, generated declarations appear as ordinary `Node`s.","types":["Node","GeneratedMakeInfo"],"urlPath":"kex/ast"},{"anchor":"record-generatedmakeinfo","kind":"record","line":393,"name":"GeneratedMakeInfo","qualifiedName":"Kex.AST.GeneratedMakeInfo","signatures":[],"summary":"The fixed portion of a generated `make` declaration.","types":["Bool","[TypeRef]","[CompiledItem]","Location"],"urlPath":"kex/ast"},{"anchor":"record-maininfo","kind":"record","line":401,"name":"MainInfo","qualifiedName":"Kex.AST.MainInfo","signatures":[],"summary":"The program entry point, including documentation and recovery clauses.","types":["String?","[ParamInfo]","[Expression]","RescueInfo?","Location"],"urlPath":"kex/ast"},{"anchor":"record-paraminfo","kind":"record","line":413,"name":"ParamInfo","qualifiedName":"Kex.AST.ParamInfo","signatures":[],"summary":"One declared function parameter.\n\n`name` is absent for a destructuring parameter; `pattern` preserves that destructuring shape. `hasDefault` records whether an initializer appeared.","types":["String?","PatternRef?","TypeRef?","Bool"],"urlPath":"kex/ast"},{"anchor":"record-clauseinfo","kind":"record","line":424,"name":"ClauseInfo","qualifiedName":"Kex.AST.ClauseInfo","signatures":[],"summary":"One clause of a function, including its patterns and body.\n\nMulti-clause functions place all clauses in one `FunctionInfo`, preserving source order so tooling can reason about which pattern is tried first.","types":["[ParamInfo]","[Expression]","TypeRef?","RescueInfo?","Bool"],"urlPath":"kex/ast"},{"anchor":"record-functioninfo","kind":"record","line":437,"name":"FunctionInfo","qualifiedName":"Kex.AST.FunctionInfo","signatures":[],"summary":"A named function and all of its pattern-matching clauses.\n\n`doc` contains the normalized `#` comment immediately attached to the declaration. Documentation generators can therefore share the same parsed structure as linters instead of scanning comments independently.","types":["String","String?","Bool","[ClauseInfo]","Location"],"urlPath":"kex/ast"},{"anchor":"record-annotationinfo","kind":"record","line":450,"name":"AnnotationInfo","qualifiedName":"Kex.AST.AnnotationInfo","signatures":[],"summary":"A standalone function or method type signature.\n\n`implicitThis` distinguishes `:>` methods from module-level `:` functions without making a tool inspect punctuation in the original source.","types":["String","TypeRef","String?","Bool","Location"],"urlPath":"kex/ast"},{"anchor":"record-variantinfo","kind":"record","line":459,"name":"VariantInfo","qualifiedName":"Kex.AST.VariantInfo","signatures":[],"summary":"One constructor of an algebraic data type.","types":["String","[TypeRef]"],"urlPath":"kex/ast"},{"anchor":"record-typeinfo","kind":"record","line":468,"name":"TypeInfo","qualifiedName":"Kex.AST.TypeInfo","signatures":[],"summary":"A type alias or algebraic data type declaration.\n\n`variants` is present for an ADT and absent for an alias or abstract type. `parents` preserves declared bounds and inherited type relationships.","types":["String","String?","[String]","[TypeRef]","[VariantInfo]?","Location"],"urlPath":"kex/ast"},{"anchor":"record-fieldinfo","kind":"record","line":478,"name":"FieldInfo","qualifiedName":"Kex.AST.FieldInfo","signatures":[],"summary":"One field declared by a record type.","types":["String","TypeRef","Bool"],"urlPath":"kex/ast"},{"anchor":"record-recordinfo","kind":"record","line":485,"name":"RecordInfo","qualifiedName":"Kex.AST.RecordInfo","signatures":[],"summary":"A record declaration with fields in source order.","types":["String","String?","[String]","[FieldInfo]","Location"],"urlPath":"kex/ast"},{"anchor":"record-traitinfo","kind":"record","line":494,"name":"TraitInfo","qualifiedName":"Kex.AST.TraitInfo","signatures":[],"summary":"A trait declaration and the signatures or default methods in its body.","types":["String","String?","[String]","[Node]","Location"],"urlPath":"kex/ast"},{"anchor":"record-makeinfo","kind":"record","line":506,"name":"MakeInfo","qualifiedName":"Kex.AST.MakeInfo","signatures":[],"summary":"A `make` implementation block.\n\n`target` is the receiver type, `implements` lists explicit traits, and `body` retains methods and visibility sections in declaration order.","types":["TypeRef","String?","Bool","[TypeRef]","[Node]","Location"],"urlPath":"kex/ast"},{"anchor":"record-pragmainfo","kind":"record","line":516,"name":"PragmaInfo","qualifiedName":"Kex.AST.PragmaInfo","signatures":[],"summary":"A compiler pragma and its optional value.","types":["String","String?","Location"],"urlPath":"kex/ast"},{"anchor":"record-moduleinfo","kind":"record","line":523,"name":"ModuleInfo","qualifiedName":"Kex.AST.ModuleInfo","signatures":[],"summary":"A module and its declarations in source order.","types":["String","String?","[Node]","Location"],"urlPath":"kex/ast"},{"anchor":"record-constantinfo","kind":"record","line":531,"name":"ConstantInfo","qualifiedName":"Kex.AST.ConstantInfo","signatures":[],"summary":"A named constant declaration. The AST reader never evaluates its value.","types":["String","String?","TypeRef?","Location"],"urlPath":"kex/ast"},{"anchor":"record-visibilityinfo","kind":"record","line":539,"name":"VisibilityInfo","qualifiedName":"Kex.AST.VisibilityInfo","signatures":[],"summary":"A `public` or `private` section and the declarations it contains.","types":["Bool","[Node]","Location"],"urlPath":"kex/ast"},{"anchor":"record-usinginfo","kind":"record","line":546,"name":"UsingInfo","qualifiedName":"Kex.AST.UsingInfo","signatures":[],"summary":"A `using` import, including aliasing, filters, and an optional scoped body.","types":["String","String?","[String]","[Expression]","Location"],"urlPath":"kex/ast"},{"anchor":"record-exportinfo","kind":"record","line":556,"name":"ExportInfo","qualifiedName":"Kex.AST.ExportInfo","signatures":[],"summary":"An `export` declaration and its public-name filters.","types":["String","String?","[String]","Location"],"urlPath":"kex/ast"},{"anchor":"type-compileditem","kind":"type","line":565,"name":"CompiledItem","qualifiedName":"Kex.AST.CompiledItem","signatures":[],"summary":"One item inside a `compiled do` block before expansion.","types":["Node","Expression"],"urlPath":"kex/ast"},{"anchor":"record-compiledinfo","kind":"record","line":570,"name":"CompiledInfo","qualifiedName":"Kex.AST.CompiledInfo","signatures":[],"summary":"A compile-time block and its declarations or expressions in source order.","types":["[CompiledItem]","Location"],"urlPath":"kex/ast"},{"anchor":"type-node","kind":"type","line":588,"name":"Node","qualifiedName":"Kex.AST.Node","signatures":[],"summary":"Any top-level or declaration-level AST node.\n\nA source tool can match only the declarations it understands and leave the rest alone. The program's `schemaVersion` lets persisted consumers reject a tree whose possible node shapes have changed.","types":["ModuleInfo","FunctionInfo","AnnotationInfo","TypeInfo","RecordInfo","TraitInfo","MakeInfo","PragmaInfo","ConstantInfo","MainInfo","VisibilityInfo","UsingInfo","ExportInfo","CompiledInfo"],"urlPath":"kex/ast"},{"anchor":"make-typeref-|-patternref","kind":"make","line":612,"name":"TypeRef | PatternRef","qualifiedName":"TypeRef | PatternRef","signatures":["to(String)"],"summary":"Source-like conversion through the standard `to(String)` spelling.\n\nUseful in diagnostics: a tool can interpolate the type or pattern it found without manually dispatching between the two reference families.","types":[],"urlPath":"kex/ast"},{"anchor":"fn-to","kind":"function","line":613,"name":"to","qualifiedName":"TypeRef | PatternRef.to","signatures":["to(String)"],"summary":"","types":[],"urlPath":"kex/ast"},{"anchor":"module-net-dns","kind":"module","line":14,"name":"Net.DNS","qualifiedName":"Net.DNS","signatures":["parse(text) : String -> Result<Name, NetError>","system()","custom(options)","addresses(name)","lookup(kind, name)","clear()","statistics()","close()","addresses(name) : Name -> Result<[Net.IP.Address], NetError>"],"summary":"Typed DNS lookup with explicit resolver ownership and bounded caching.\n\nUse `DNS.addresses` for an occasional hostname lookup. Own a `Resolver` when a service performs repeated lookups, needs cache statistics, or must query a particular nameserver. Resolver ownership makes both caching and cleanup visible instead of hiding process-wide state behind every lookup.\n\n```kex\nusing Net.DNS\n\nlet resolver = Resolver.system.try\nlet name = Name.parse(\"example.test\").try\nlet addresses = resolver.addresses(name).try\nresolver.close\n```","types":["String","Net.IP.Address","Name","Integer","[String]","Net.Port","[DNSRecord]","DNSSECStatus","Duration","CacheOptions","[Nameserver]","[Name]","Result<Name, NetError>","Result<[Net.IP.Address], NetError>"],"urlPath":"net/dns"},{"anchor":"record-name","kind":"record","line":21,"name":"Name","qualifiedName":"Net.DNS.Name","signatures":[],"summary":"A validated DNS name with caller-facing and IDNA ASCII spellings.\n\n`display` keeps the readable form supplied by the caller; `ascii` is the wire-safe IDNA form used in DNS queries. Keeping both lets an error message say what the user typed without sending noncanonical labels to a resolver.","types":["String"],"urlPath":"net/dns"},{"anchor":"type-recordtype","kind":"type","line":27,"name":"RecordType","qualifiedName":"Net.DNS.RecordType","signatures":[],"summary":"Record families supported by `Resolver.lookup`.","types":[],"urlPath":"net/dns"},{"anchor":"type-dnssecstatus","kind":"type","line":31,"name":"DNSSECStatus","qualifiedName":"Net.DNS.DNSSECStatus","signatures":[],"summary":"DNSSEC state reported by the underlying resolver. Kex does not independently validate DNSSEC and therefore commonly reports `Indeterminate`.","types":[],"urlPath":"net/dns"},{"anchor":"type-dnsrecord","kind":"type","line":34,"name":"DNSRecord","qualifiedName":"Net.DNS.DNSRecord","signatures":[],"summary":"Typed DNS resource records, preserving MX/SRV priorities and TXT chunks.","types":["Net.IP.Address","Name","Integer","Name","[String]","Integer","Integer","Net.Port","Name","Name"],"urlPath":"net/dns"},{"anchor":"record-lookupresponse","kind":"record","line":37,"name":"LookupResponse","qualifiedName":"Net.DNS.LookupResponse","signatures":[],"summary":"Records and the reported DNSSEC state from one lookup.","types":["[DNSRecord]","DNSSECStatus"],"urlPath":"net/dns"},{"anchor":"record-cacheoptions","kind":"record","line":47,"name":"CacheOptions","qualifiedName":"Net.DNS.CacheOptions","signatures":[],"summary":"Bounds for a resolver-owned positive and negative cache.\n\nPositive answers honor their DNS TTL up to `maximumTtl`. Failed lookups are cached for `negativeTtl`, preventing a missing hostname from hammering the configured nameserver on every request.","types":["Integer","Duration"],"urlPath":"net/dns"},{"anchor":"record-nameserver","kind":"record","line":54,"name":"Nameserver","qualifiedName":"Net.DNS.Nameserver","signatures":[],"summary":"One DNS server used by a custom resolver.","types":["Net.IP.Address","Net.Port"],"urlPath":"net/dns"},{"anchor":"record-resolveroptions","kind":"record","line":61,"name":"ResolverOptions","qualifiedName":"Net.DNS.ResolverOptions","signatures":[],"summary":"Isolated resolver configuration. An empty search list only queries the name as written; search domains are tried in order for single-label names.","types":["CacheOptions","[Nameserver]","[Name]","Integer","Duration"],"urlPath":"net/dns"},{"anchor":"record-cachestatistics","kind":"record","line":74,"name":"CacheStatistics","qualifiedName":"Net.DNS.CacheStatistics","signatures":[],"summary":"Lifetime cache counters. `clear` empties entries but keeps these counters.\n\nCompare `hits` with `misses` when tuning `entries` or TTL bounds. A rising `evictions` count means the resolver is seeing more distinct names than its cache can retain.","types":["Integer"],"urlPath":"net/dns"},{"anchor":"type-resolver","kind":"type","line":83,"name":"Resolver","qualifiedName":"Net.DNS.Resolver","signatures":[],"summary":"An opaque, process-safe resolver that owns its cache.","types":[],"urlPath":"net/dns"},{"anchor":"module-net-dns-name","kind":"module","line":86,"name":"Net.DNS.Name","qualifiedName":"Net.DNS.Name","signatures":["parse(text) : String -> Result<Name, NetError>"],"summary":"Validation and IDNA conversion for DNS names.","types":["String","Result<Name, NetError>"],"urlPath":"net/dns"},{"anchor":"function-parse","kind":"function","line":96,"name":"parse","qualifiedName":"Net.DNS.Name.parse","signatures":["parse(text) : String -> Result<Name, NetError>"],"summary":"Validates a name and converts Unicode labels to IDNA ASCII.","types":["String","Result<Name, NetError>"],"urlPath":"net/dns"},{"anchor":"module-net-dns-resolver","kind":"module","line":101,"name":"Net.DNS.Resolver","qualifiedName":"Net.DNS.Resolver","signatures":["system()","custom(options)"],"summary":"Constructors for long-lived, cache-owning resolvers.","types":[],"urlPath":"net/dns"},{"anchor":"function-system","kind":"function","line":113,"name":"system","qualifiedName":"Net.DNS.Resolver.system","signatures":["system()"],"summary":"Opens a resolver using system configuration and default cache bounds.\n\nReuse the returned resolver for the lifetime of a service so repeated names benefit from its bounded cache, then close it during shutdown.","types":[],"urlPath":"net/dns"},{"anchor":"function-custom","kind":"function","line":142,"name":"custom","qualifiedName":"Net.DNS.Resolver.custom","signatures":["custom(options)"],"summary":"Opens an isolated resolver with typed nameservers and query bounds.\n\nThis does not inherit the machine's search domains or nameservers. It is useful for tests, service discovery, and applications with their own DNS policy.","types":[],"urlPath":"net/dns"},{"anchor":"make-resolver","kind":"make","line":145,"name":"Resolver","qualifiedName":"Resolver","signatures":["addresses(name)","lookup(kind, name)","clear()","statistics()","close()"],"summary":"","types":[],"urlPath":"net/dns"},{"anchor":"fn-addresses","kind":"function","line":156,"name":"addresses","qualifiedName":"Resolver.addresses","signatures":["addresses(name)"],"summary":"Resolves AAAA and A records, returning IPv6 addresses first.\n\nThis is the convenient operation for connecting to a host. Use `lookup` when record type, TTL-related behavior, or DNSSEC status matters.","types":[],"urlPath":"net/dns"},{"anchor":"fn-lookup","kind":"function","line":166,"name":"lookup","qualifiedName":"Resolver.lookup","signatures":["lookup(kind, name)"],"summary":"Looks up one supported resource-record family.","types":[],"urlPath":"net/dns"},{"anchor":"fn-clear","kind":"function","line":176,"name":"clear","qualifiedName":"Resolver.clear","signatures":["clear()"],"summary":"Empties cached entries without resetting lifetime counters.\n\nExisting statistics remain meaningful across a manual refresh, while the next lookup is forced back to DNS.","types":[],"urlPath":"net/dns"},{"anchor":"fn-statistics","kind":"function","line":184,"name":"statistics","qualifiedName":"Resolver.statistics","signatures":["statistics()"],"summary":"Reports current occupancy and lifetime cache counters.","types":[],"urlPath":"net/dns"},{"anchor":"fn-close","kind":"function","line":190,"name":"close","qualifiedName":"Resolver.close","signatures":["close()"],"summary":"Idempotently closes the resolver.\n\nCalls after closing fail with `Closed`; closing again is harmless.","types":[],"urlPath":"net/dns"},{"anchor":"module-net-dns-dns","kind":"module","line":195,"name":"Net.DNS.DNS","qualifiedName":"Net.DNS.DNS","signatures":["addresses(name) : Name -> Result<[Net.IP.Address], NetError>"],"summary":"Convenience lookup operations for callers that do not need resolver ownership or cache reuse.","types":["Name","Result<[Net.IP.Address], NetError>"],"urlPath":"net/dns"},{"anchor":"function-addresses","kind":"function","line":206,"name":"addresses","qualifiedName":"Net.DNS.DNS.addresses","signatures":["addresses(name) : Name -> Result<[Net.IP.Address], NetError>"],"summary":"Resolves a name once with a short-lived system resolver.\n\nPrefer this for command-line tools and one-off checks. A server that looks up names repeatedly should own a `Resolver` so it can reuse cached answers.","types":["Name","Result<[Net.IP.Address], NetError>"],"urlPath":"net/dns"},{"anchor":"module-net-http","kind":"module","line":14,"name":"Net.HTTP","qualifiedName":"Net.HTTP","signatures":["from(entries) : [(String, String)] -> Result<Headers, NetError>","parse(text) : String -> Result<Headers, NetError>","from(code) : Integer -> Result<Status, NetError>","binary(status, body, headers)","text(status, body)","empty(status)","route(method, path, handler)","get(path, handler)","head(path, handler)","options(path, handler)","post(path, handler)","put(path, handler)","patch(path, handler)","delete(path, handler)","start(endpoint, router)","serve(endpoint, router)","stop(server)","join(server)","running?(server)","localAddress(server)","open() : Result<Client, NetError>","open(options) : ClientOptions -> Result<Client, NetError>","request(method, url, headers, body)","get(url)","post(url, body)","put(url, body)","patch(url, body)","delete(url)","head(url)","options(url)","statistics()","close()","add(name, value)","set(name, value)","remove(name)","get(name)","getAll(name)","inspectValue(colors)","parameter(name)","request : String -> String -> Headers -> Binary -> Result<Response<Binary>, NetError>","get(url) : String -> Result<Response<Binary>, NetError>","delete(url) : String -> Result<Response<Binary>, NetError>","head(url) : String -> Result<Response<Binary>, NetError>","options(url) : String -> Result<Response<Binary>, NetError>","post(url, body) : String -> Binary -> Result<Response<Binary>, NetError>","put(url, body) : String -> Binary -> Result<Response<Binary>, NetError>","patch(url, body) : String -> Binary -> Result<Response<Binary>, NetError>"],"summary":"Buffered HTTP clients, responses, and a small declaration-ordered server router. Requests never follow redirects or perform generic retries implicitly.\n\n```kex\nusing Net.HTTP\n\nlet response = HTTP.get(\"https://example.test/\").try\nresponse.status.success?   # => true\n```\n\nFor connection reuse and statistics, own a client explicitly:\n\n```kex\nlet client = Client.open.try\nlet response = client.get(\"https://example.test/\").try\nclient.close.try\n```","types":["[(String, String)]","Integer","Status","Headers","B","String","URI","Map<String, String>","RouteContext","Handler","[Route]","Duration","PoolOptions","Result<Headers, NetError>","Result<Status, NetError>","?","ClientOptions","Result<Client, NetError>","(String) -> (String) -> (Headers) -> (Binary) -> Result<Response<Binary>, NetError>","Result<Response<Binary>, NetError>","Binary"],"urlPath":"net/http"},{"anchor":"record-headers","kind":"record","line":18,"name":"Headers","qualifiedName":"Net.HTTP.Headers","signatures":[],"summary":"An insertion-ordered HTTP field collection. Names compare case-insensitively and duplicate fields are preserved.","types":["[(String, String)]"],"urlPath":"net/http"},{"anchor":"record-status","kind":"record","line":23,"name":"Status","qualifiedName":"Net.HTTP.Status","signatures":[],"summary":"A validated HTTP status code in `100..599`.","types":["Integer"],"urlPath":"net/http"},{"anchor":"record-response","kind":"record","line":28,"name":"Response","qualifiedName":"Net.HTTP.Response","signatures":[],"summary":"A typed HTTP response envelope whose body representation is explicit.","types":["Status","Headers","B"],"urlPath":"net/http"},{"anchor":"record-request","kind":"record","line":35,"name":"Request","qualifiedName":"Net.HTTP.Request","signatures":[],"summary":"A typed HTTP request envelope whose body representation is explicit.","types":["String","URI","Headers","B"],"urlPath":"net/http"},{"anchor":"record-routecontext","kind":"record","line":43,"name":"RouteContext","qualifiedName":"Net.HTTP.RouteContext","signatures":[],"summary":"Route captures decoded after path segmentation.","types":["Map<String, String>"],"urlPath":"net/http"},{"anchor":"record-context","kind":"record","line":48,"name":"Context","qualifiedName":"Net.HTTP.Context","signatures":[],"summary":"Per-request server context.","types":["RouteContext"],"urlPath":"net/http"},{"anchor":"type-handler","kind":"type","line":53,"name":"Handler","qualifiedName":"Net.HTTP.Handler","signatures":[],"summary":"A buffered HTTP route handler.","types":[],"urlPath":"net/http"},{"anchor":"record-route","kind":"record","line":56,"name":"Route","qualifiedName":"Net.HTTP.Route","signatures":[],"summary":"One declared route; routers preserve declaration order.","types":["String","Handler"],"urlPath":"net/http"},{"anchor":"record-router","kind":"record","line":63,"name":"Router","qualifiedName":"Net.HTTP.Router","signatures":[],"summary":"An immutable, declaration-ordered HTTP router.","types":["[Route]"],"urlPath":"net/http"},{"anchor":"record-shutdownreport","kind":"record","line":68,"name":"ShutdownReport","qualifiedName":"Net.HTTP.ShutdownReport","signatures":[],"summary":"Counts and elapsed time from graceful server shutdown.","types":["Integer"],"urlPath":"net/http"},{"anchor":"record-serveroptions","kind":"record","line":76,"name":"ServerOptions","qualifiedName":"Net.HTTP.ServerOptions","signatures":[],"summary":"Bounded HTTP server resources and default graceful-shutdown duration.","types":["Integer","Duration"],"urlPath":"net/http"},{"anchor":"record-pooloptions","kind":"record","line":83,"name":"PoolOptions","qualifiedName":"Net.HTTP.PoolOptions","signatures":[],"summary":"HTTP connection-pool bounds and idle lifetime.","types":["Integer"],"urlPath":"net/http"},{"anchor":"record-clientoptions","kind":"record","line":91,"name":"ClientOptions","qualifiedName":"Net.HTTP.ClientOptions","signatures":[],"summary":"Options owned by an explicit HTTP client.","types":["PoolOptions"],"urlPath":"net/http"},{"anchor":"record-clientstatistics","kind":"record","line":96,"name":"ClientStatistics","qualifiedName":"Net.HTTP.ClientStatistics","signatures":[],"summary":"Lifetime request/reuse counters plus current pooled connections.","types":["Integer"],"urlPath":"net/http"},{"anchor":"record-clientclosereport","kind":"record","line":103,"name":"ClientCloseReport","qualifiedName":"Net.HTTP.ClientCloseReport","signatures":[],"summary":"Resources released by `Client.close`.","types":["Integer"],"urlPath":"net/http"},{"anchor":"type-client","kind":"type","line":109,"name":"Client","qualifiedName":"Net.HTTP.Client","signatures":[],"summary":"The pooled HTTP client. An opaque handle over the connection pool that owns it; `Client.open` makes one and `client.close` releases it.","types":[],"urlPath":"net/http"},{"anchor":"module-net-http-headers","kind":"module","line":112,"name":"Net.HTTP.Headers","qualifiedName":"Net.HTTP.Headers","signatures":["from(entries) : [(String, String)] -> Result<Headers, NetError>","parse(text) : String -> Result<Headers, NetError>"],"summary":"Construction and parsing of validated HTTP header collections.","types":["Headers","[(String, String)]","Result<Headers, NetError>","String"],"urlPath":"net/http"},{"anchor":"constant-empty","kind":"constant","line":121,"name":"empty","qualifiedName":"Net.HTTP.Headers.empty","signatures":[],"summary":"Returns a field collection with no entries.","types":["Headers"],"urlPath":"net/http"},{"anchor":"function-from","kind":"function","line":137,"name":"from","qualifiedName":"Net.HTTP.Headers.from","signatures":["from(entries) : [(String, String)] -> Result<Headers, NetError>"],"summary":"Validates header names and values without folding duplicates.\n\nDuplicate fields stay in their original order. Invalid names and values containing line breaks are rejected instead of creating a malformed or injectable HTTP message.","types":["[(String, String)]","Result<Headers, NetError>"],"urlPath":"net/http"},{"anchor":"function-parse","kind":"function","line":149,"name":"parse","qualifiedName":"Net.HTTP.Headers.parse","signatures":["parse(text) : String -> Result<Headers, NetError>"],"summary":"Parses CRLF- or LF-separated header fields.\n\nUse this at a protocol boundary when headers arrive as text. Application code normally builds them with `from`, `add`, and `set`.","types":["String","Result<Headers, NetError>"],"urlPath":"net/http"},{"anchor":"module-net-http-status","kind":"module","line":154,"name":"Net.HTTP.Status","qualifiedName":"Net.HTTP.Status","signatures":["from(code) : Integer -> Result<Status, NetError>"],"summary":"Validation for numeric HTTP status codes.","types":["Integer","Result<Status, NetError>"],"urlPath":"net/http"},{"anchor":"function-from","kind":"function","line":163,"name":"from","qualifiedName":"Net.HTTP.Status.from","signatures":["from(code) : Integer -> Result<Status, NetError>"],"summary":"Validates an HTTP status code.","types":["Integer","Result<Status, NetError>"],"urlPath":"net/http"},{"anchor":"module-net-http-response","kind":"module","line":168,"name":"Net.HTTP.Response","qualifiedName":"Net.HTTP.Response","signatures":["binary(status, body, headers)","text(status, body)","empty(status)"],"summary":"Buffered response constructors for route handlers.","types":[],"urlPath":"net/http"},{"anchor":"function-binary","kind":"function","line":173,"name":"binary","qualifiedName":"Net.HTTP.Response.binary","signatures":["binary(status, body, headers)"],"summary":"Builds a buffered binary response with validated headers.","types":[],"urlPath":"net/http"},{"anchor":"function-text","kind":"function","line":178,"name":"text","qualifiedName":"Net.HTTP.Response.text","signatures":["text(status, body)"],"summary":"Builds a UTF-8 text response with an explicit text/plain content type.","types":[],"urlPath":"net/http"},{"anchor":"function-empty","kind":"function","line":183,"name":"empty","qualifiedName":"Net.HTTP.Response.empty","signatures":["empty(status)"],"summary":"Builds a response with an empty body.","types":[],"urlPath":"net/http"},{"anchor":"module-net-http-router","kind":"module","line":187,"name":"Net.HTTP.Router","qualifiedName":"Net.HTTP.Router","signatures":[],"summary":"The empty starting point for an immutable route declaration chain.","types":["?"],"urlPath":"net/http"},{"anchor":"constant-build","kind":"constant","line":190,"name":"build","qualifiedName":"Net.HTTP.Router.build","signatures":[],"summary":"","types":["?"],"urlPath":"net/http"},{"anchor":"make-router","kind":"make","line":193,"name":"Router","qualifiedName":"Router","signatures":["route(method, path, handler)","get(path, handler)","head(path, handler)","options(path, handler)","post(path, handler)","put(path, handler)","patch(path, handler)","delete(path, handler)"],"summary":"","types":[],"urlPath":"net/http"},{"anchor":"fn-route","kind":"function","line":201,"name":"route","qualifiedName":"Router.route","signatures":["route(method, path, handler)"],"summary":"Appends a route; earlier matching declarations win.\n\nUse this for a method without a convenience function. Paths may include named or wildcard captures, which the handler reads from `RouteContext`.","types":[],"urlPath":"net/http"},{"anchor":"fn-get","kind":"function","line":205,"name":"get","qualifiedName":"Router.get","signatures":["get(path, handler)"],"summary":"Appends a GET route. GET also supplies automatic HEAD fallback.","types":[],"urlPath":"net/http"},{"anchor":"fn-head","kind":"function","line":207,"name":"head","qualifiedName":"Router.head","signatures":["head(path, handler)"],"summary":"Appends an explicit HEAD route, overriding automatic GET fallback.","types":[],"urlPath":"net/http"},{"anchor":"fn-options","kind":"function","line":209,"name":"options","qualifiedName":"Router.options","signatures":["options(path, handler)"],"summary":"Appends an explicit OPTIONS route, overriding generated OPTIONS.","types":[],"urlPath":"net/http"},{"anchor":"fn-post","kind":"function","line":211,"name":"post","qualifiedName":"Router.post","signatures":["post(path, handler)"],"summary":"Appends a POST route.","types":[],"urlPath":"net/http"},{"anchor":"fn-put","kind":"function","line":213,"name":"put","qualifiedName":"Router.put","signatures":["put(path, handler)"],"summary":"Appends a PUT route.","types":[],"urlPath":"net/http"},{"anchor":"fn-patch","kind":"function","line":215,"name":"patch","qualifiedName":"Router.patch","signatures":["patch(path, handler)"],"summary":"Appends a PATCH route.","types":[],"urlPath":"net/http"},{"anchor":"fn-delete","kind":"function","line":217,"name":"delete","qualifiedName":"Router.delete","signatures":["delete(path, handler)"],"summary":"Appends a DELETE route.","types":[],"urlPath":"net/http"},{"anchor":"module-net-http-server","kind":"module","line":221,"name":"Net.HTTP.Server","qualifiedName":"Net.HTTP.Server","signatures":["start(endpoint, router)","serve(endpoint, router)","stop(server)","join(server)","running?(server)","localAddress(server)"],"summary":"Starting, observing, and gracefully stopping HTTP servers.","types":[],"urlPath":"net/http"},{"anchor":"type-running","kind":"type","line":223,"name":"Running","qualifiedName":"Net.HTTP.Server.Running","signatures":[],"summary":"An opaque asynchronous HTTP server handle.","types":[],"urlPath":"net/http"},{"anchor":"function-start","kind":"function","line":237,"name":"start","qualifiedName":"Net.HTTP.Server.start","signatures":["start(endpoint, router)"],"summary":"Starts a server with conservative defaults and returns immediately.\n\nThe returned handle owns the listener and active handlers. Use `start` when the process has other work to do; use `serve` for a foreground server whose main job is handling HTTP.","types":[],"urlPath":"net/http"},{"anchor":"function-serve","kind":"function","line":241,"name":"serve","qualifiedName":"Net.HTTP.Server.serve","signatures":["serve(endpoint, router)"],"summary":"Starts with defaults and blocks until the server stops.","types":[],"urlPath":"net/http"},{"anchor":"function-stop","kind":"function","line":254,"name":"stop","qualifiedName":"Net.HTTP.Server.stop","signatures":["stop(server)"],"summary":"Gracefully stops using the duration captured at start.\n\nNew requests stop being accepted while in-flight handlers get their grace period to finish. The report says how much work completed or was forced down during shutdown.","types":[],"urlPath":"net/http"},{"anchor":"function-join","kind":"function","line":258,"name":"join","qualifiedName":"Net.HTTP.Server.join","signatures":["join(server)"],"summary":"Waits until the server owner exits.","types":[],"urlPath":"net/http"},{"anchor":"function-running?","kind":"function","line":260,"name":"running?","qualifiedName":"Net.HTTP.Server.running?","signatures":["running?(server)"],"summary":"","types":[],"urlPath":"net/http"},{"anchor":"function-localaddress","kind":"function","line":268,"name":"localAddress","qualifiedName":"Net.HTTP.Server.localAddress","signatures":["localAddress(server)"],"summary":"Returns the bound address, including an operating-system-assigned port.","types":[],"urlPath":"net/http"},{"anchor":"module-net-http-client","kind":"module","line":272,"name":"Net.HTTP.Client","qualifiedName":"Net.HTTP.Client","signatures":["open() : Result<Client, NetError>","open(options) : ClientOptions -> Result<Client, NetError>"],"summary":"Constructors for explicitly owned, connection-pooling HTTP clients.","types":["ClientOptions","Result<Client, NetError>"],"urlPath":"net/http"},{"anchor":"function-open","kind":"function","line":283,"name":"open","qualifiedName":"Net.HTTP.Client.open","signatures":["open() : Result<Client, NetError>","open(options) : ClientOptions -> Result<Client, NetError>"],"summary":"Opens an explicit pooled client with conservative defaults.\n\nReuse one client for related requests so keep-alive connections and DNS work can be reused. Close it when the owning service shuts down.","types":["ClientOptions","Result<Client, NetError>"],"urlPath":"net/http"},{"anchor":"make-client","kind":"make","line":290,"name":"Client","qualifiedName":"Client","signatures":["request(method, url, headers, body)","get(url)","post(url, body)","put(url, body)","patch(url, body)","delete(url)","head(url)","options(url)","statistics()","close()"],"summary":"","types":[],"urlPath":"net/http"},{"anchor":"fn-request","kind":"function","line":301,"name":"request","qualifiedName":"Client.request","signatures":["request(method, url, headers, body)"],"summary":"Sends a buffered request. Redirects and generic retries are not implicit.\n\nThis keeps policy with the caller: inspect a redirect before following it, and retry only methods and failures your application knows are safe.","types":[],"urlPath":"net/http"},{"anchor":"fn-get","kind":"function","line":303,"name":"get","qualifiedName":"Client.get","signatures":["get(url)"],"summary":"Sends a buffered GET request.","types":[],"urlPath":"net/http"},{"anchor":"fn-post","kind":"function","line":305,"name":"post","qualifiedName":"Client.post","signatures":["post(url, body)"],"summary":"Sends a buffered binary POST request.","types":[],"urlPath":"net/http"},{"anchor":"fn-put","kind":"function","line":307,"name":"put","qualifiedName":"Client.put","signatures":["put(url, body)"],"summary":"Sends a buffered binary PUT request.","types":[],"urlPath":"net/http"},{"anchor":"fn-patch","kind":"function","line":309,"name":"patch","qualifiedName":"Client.patch","signatures":["patch(url, body)"],"summary":"Sends a buffered binary PATCH request.","types":[],"urlPath":"net/http"},{"anchor":"fn-delete","kind":"function","line":311,"name":"delete","qualifiedName":"Client.delete","signatures":["delete(url)"],"summary":"Sends a DELETE request with an empty body.","types":[],"urlPath":"net/http"},{"anchor":"fn-head","kind":"function","line":313,"name":"head","qualifiedName":"Client.head","signatures":["head(url)"],"summary":"Sends a HEAD request; the returned body is empty.","types":[],"urlPath":"net/http"},{"anchor":"fn-options","kind":"function","line":315,"name":"options","qualifiedName":"Client.options","signatures":["options(url)"],"summary":"Sends an OPTIONS request.","types":[],"urlPath":"net/http"},{"anchor":"fn-statistics","kind":"function","line":323,"name":"statistics","qualifiedName":"Client.statistics","signatures":["statistics()"],"summary":"Reports current pool occupancy and lifetime request counters.","types":[],"urlPath":"net/http"},{"anchor":"fn-close","kind":"function","line":327,"name":"close","qualifiedName":"Client.close","signatures":["close()"],"summary":"Idempotently closes the client and every idle pooled connection.\n\nFurther requests fail with `Closed`; a second close is harmless.","types":[],"urlPath":"net/http"},{"anchor":"make-headers","kind":"make","line":330,"name":"Headers","qualifiedName":"Headers","signatures":["add(name, value)","set(name, value)","remove(name)","get(name)","getAll(name)","inspectValue(colors)"],"summary":"","types":[],"urlPath":"net/http"},{"anchor":"fn-add","kind":"function","line":333,"name":"add","qualifiedName":"Headers.add","signatures":["add(name, value)"],"summary":"Appends a field without replacing existing fields of the same name.","types":[],"urlPath":"net/http"},{"anchor":"fn-set","kind":"function","line":336,"name":"set","qualifiedName":"Headers.set","signatures":["set(name, value)"],"summary":"Replaces all fields of `name` with one value.","types":[],"urlPath":"net/http"},{"anchor":"fn-remove","kind":"function","line":341,"name":"remove","qualifiedName":"Headers.remove","signatures":["remove(name)"],"summary":"Removes every field matching `name` case-insensitively.","types":[],"urlPath":"net/http"},{"anchor":"fn-get","kind":"function","line":346,"name":"get","qualifiedName":"Headers.get","signatures":["get(name)"],"summary":"Returns the first matching field value.","types":[],"urlPath":"net/http"},{"anchor":"fn-getall","kind":"function","line":351,"name":"getAll","qualifiedName":"Headers.getAll","signatures":["getAll(name)"],"summary":"Returns every matching value in insertion order.","types":[],"urlPath":"net/http"},{"anchor":"fn-inspectvalue","kind":"function","line":355,"name":"inspectValue","qualifiedName":"Headers.inspectValue","signatures":["inspectValue(colors)"],"summary":"Structural inspection uses the same credential-safe rendering.","types":[],"urlPath":"net/http"},{"anchor":"make-status","kind":"make","line":358,"name":"Status","qualifiedName":"Status","signatures":[],"summary":"","types":[],"urlPath":"net/http"},{"anchor":"make-routecontext","kind":"make","line":371,"name":"RouteContext","qualifiedName":"RouteContext","signatures":["parameter(name)"],"summary":"","types":[],"urlPath":"net/http"},{"anchor":"fn-parameter","kind":"function","line":379,"name":"parameter","qualifiedName":"RouteContext.parameter","signatures":["parameter(name)"],"summary":"Returns one decoded named or wildcard route capture.","types":[],"urlPath":"net/http"},{"anchor":"module-net-http-http","kind":"module","line":386,"name":"Net.HTTP.HTTP","qualifiedName":"Net.HTTP.HTTP","signatures":["request : String -> String -> Headers -> Binary -> Result<Response<Binary>, NetError>","get(url) : String -> Result<Response<Binary>, NetError>","delete(url) : String -> Result<Response<Binary>, NetError>","head(url) : String -> Result<Response<Binary>, NetError>","options(url) : String -> Result<Response<Binary>, NetError>","post(url, body) : String -> Binary -> Result<Response<Binary>, NetError>","put(url, body) : String -> Binary -> Result<Response<Binary>, NetError>","patch(url, body) : String -> Binary -> Result<Response<Binary>, NetError>"],"summary":"Stateless HTTP convenience calls for scripts and occasional requests.","types":["(String) -> (String) -> (Headers) -> (Binary) -> Result<Response<Binary>, NetError>","String","Result<Response<Binary>, NetError>","Binary"],"urlPath":"net/http"},{"anchor":"function-request","kind":"function","line":395,"name":"request","qualifiedName":"Net.HTTP.HTTP.request","signatures":["request : String -> String -> Headers -> Binary -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless buffered request with no redirect or hidden retry.\n\nEach call owns a short-lived client. This is convenient for scripts and occasional requests; use `Client` for a service making repeated calls.","types":["(String) -> (String) -> (Headers) -> (Binary) -> Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-get","kind":"function","line":399,"name":"get","qualifiedName":"Net.HTTP.HTTP.get","signatures":["get(url) : String -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless buffered GET.","types":["String","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-delete","kind":"function","line":402,"name":"delete","qualifiedName":"Net.HTTP.HTTP.delete","signatures":["delete(url) : String -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless DELETE with an empty body.","types":["String","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-head","kind":"function","line":405,"name":"head","qualifiedName":"Net.HTTP.HTTP.head","signatures":["head(url) : String -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless HEAD and returns an empty response body.","types":["String","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-options","kind":"function","line":408,"name":"options","qualifiedName":"Net.HTTP.HTTP.options","signatures":["options(url) : String -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless OPTIONS request.","types":["String","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-post","kind":"function","line":411,"name":"post","qualifiedName":"Net.HTTP.HTTP.post","signatures":["post(url, body) : String -> Binary -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless buffered binary POST.","types":["String","Binary","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-put","kind":"function","line":414,"name":"put","qualifiedName":"Net.HTTP.HTTP.put","signatures":["put(url, body) : String -> Binary -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless buffered binary PUT.","types":["String","Binary","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"function-patch","kind":"function","line":417,"name":"patch","qualifiedName":"Net.HTTP.HTTP.patch","signatures":["patch(url, body) : String -> Binary -> Result<Response<Binary>, NetError>"],"summary":"Sends one stateless buffered binary PATCH.","types":["String","Binary","Result<Response<Binary>, NetError>"],"urlPath":"net/http"},{"anchor":"module-net-ip","kind":"module","line":8,"name":"Net.IP","qualifiedName":"Net.IP","signatures":["parse(text) : String -> Result<Address, NetError>","parse(text) : String -> Result<Network, NetError>","contains(address) : Address -> Bool"],"summary":"Validated, canonical IP addresses and CIDR networks.\n\n```kex\nusing Net.IP\n\nlet address = Address.parse(\"192.0.2.42\").try\nlet network = Network.parse(\"192.0.2.0/24\").try\nnetwork.contains(address)   # => true\n```","types":["String","Result<Address, NetError>","Result<Network, NetError>","Address","Bool"],"urlPath":"net/ip"},{"anchor":"record-address","kind":"record","line":15,"name":"Address","qualifiedName":"Net.IP.Address","signatures":[],"summary":"A canonical IPv4 or IPv6 address.\n\nThe stored spelling is normalized, so addresses that arrived in different forms compare and print consistently. Zone identifiers such as `%en0` are properties of socket endpoints and do not belong here.","types":["String"],"urlPath":"net/ip"},{"anchor":"record-network","kind":"record","line":24,"name":"Network","qualifiedName":"Net.IP.Network","signatures":[],"summary":"A canonical CIDR network with host bits cleared.\n\nParsing `192.0.2.9/24` therefore produces `192.0.2.0/24`. This makes a `Network` suitable for access-control rules and routing tables: its identity is the range, not whichever host address happened to describe it.","types":["String"],"urlPath":"net/ip"},{"anchor":"module-net-ip-address","kind":"module","line":29,"name":"Net.IP.Address","qualifiedName":"Net.IP.Address","signatures":["parse(text) : String -> Result<Address, NetError>"],"summary":"Strict parsing and canonicalization of individual IP addresses.","types":["String","Result<Address, NetError>"],"urlPath":"net/ip"},{"anchor":"function-parse","kind":"function","line":34,"name":"parse","qualifiedName":"Net.IP.Address.parse","signatures":["parse(text) : String -> Result<Address, NetError>"],"summary":"Parses IPv4 or IPv6 text and canonicalizes its spelling.","types":["String","Result<Address, NetError>"],"urlPath":"net/ip"},{"anchor":"module-net-ip-network","kind":"module","line":39,"name":"Net.IP.Network","qualifiedName":"Net.IP.Network","signatures":["parse(text) : String -> Result<Network, NetError>"],"summary":"Strict parsing and canonicalization of CIDR networks.","types":["String","Result<Network, NetError>"],"urlPath":"net/ip"},{"anchor":"function-parse","kind":"function","line":44,"name":"parse","qualifiedName":"Net.IP.Network.parse","signatures":["parse(text) : String -> Result<Network, NetError>"],"summary":"Parses a CIDR and clears host bits.","types":["String","Result<Network, NetError>"],"urlPath":"net/ip"},{"anchor":"make-address","kind":"make","line":48,"name":"Address","qualifiedName":"Address","signatures":[],"summary":"","types":[],"urlPath":"net/ip"},{"anchor":"make-network","kind":"make","line":113,"name":"Network","qualifiedName":"Network","signatures":["contains(address) : Address -> Bool"],"summary":"","types":["Address","Bool"],"urlPath":"net/ip"},{"anchor":"fn-contains","kind":"function","line":134,"name":"contains","qualifiedName":"Network.contains","signatures":["contains(address) : Address -> Bool"],"summary":"Returns `true` when `address` falls within this network.\n\nAn address from the other family is simply outside the network; callers do not need to compare `version` first.","types":["Address","Bool"],"urlPath":"net/ip"},{"anchor":"module-net-socket","kind":"module","line":12,"name":"Net.Socket","qualifiedName":"Net.Socket","signatures":["host(name, port) : String -> Net.Port -> Endpoint","any(port) : Net.Port -> Endpoint","loopback(port) : Net.Port -> Endpoint","connect(endpoint) : Endpoint -> Result<TCPConnection, NetError>","connect(endpoint) : Endpoint -> ConnectOptions -> Result<TCPConnection, NetError>","listen(endpoint) : Endpoint -> Result<TCPListener, NetError>","listen(endpoint) : Endpoint -> ListenOptions -> Result<TCPListener, NetError>","sendAll(connection, data)","receiveChunk(connection, limit)","receiveExactly(connection, count)","receiveUntil(connection, delimiter, limit)","receiveLine(connection, limit)","shutdownWrite(connection)","accept(listener)","close(connection)","closed?(connection)","localAddress(connection)","peerAddress(connection)","host(name, port) : String -> Net.Port -> Endpoint","any(port) : Net.Port -> Endpoint","loopback(port) : Net.Port -> Endpoint","bind(endpoint) : Endpoint -> Result<Socket, NetError>","bind(endpoint) : Endpoint -> BindOptions -> Result<Socket, NetError>","sendTo(socket, endpoint, data)","receiveFrom(socket, limit)","close(socket)","closed?(socket)","localAddress(socket)","joinMulticast(socket, group, interface)","leaveMulticast(socket, group, interface)","path(value) : String -> Result<Address, NetError>","connect(address) : Address -> Result<UnixConnection, NetError>","connect(address) : Address -> ConnectOptions -> Result<UnixConnection, NetError>","listen(address) : Address -> Result<UnixListener, NetError>","listen(address) : Address -> ListenOptions -> Result<UnixListener, NetError>","sendAll(connection, data)","receiveChunk(connection, limit)","receiveExactly(connection, count)","receiveUntil(connection, delimiter, limit)","receiveLine(connection, limit)","shutdownWrite(connection)","accept(listener)","close(connection)","closed?(connection)","connect(endpoint, config) : Net.Socket.TCP.Endpoint -> ClientConfig -> Result<TLSConnection, NetError>","sendAll(connection, data)","receiveChunk(connection, limit)","close(connection)","closed?(connection)"],"summary":"Process-owned TCP byte streams and listeners. All blocking operations return typed `NetError` values; close operations are idempotent.\n\n```kex\nusing Net\nusing Net.Socket\n\nlet listener = TCP.listen(TCP.Endpoint.loopback(Port.from(0).try)).try\nlet address = listener.localAddress.try\nlet client = TCP.connect(address).try\nclient.sendAll(\"ping\".to(Binary).try).try\nlistener.close\n```","types":["String","Net.Port","Duration","Bool","Integer","Endpoint","Result<TCPConnection, NetError>","(ConnectOptions) -> Result<TCPConnection, NetError>","Result<TCPListener, NetError>","(ListenOptions) -> Result<TCPListener, NetError>","Binary","Result<Socket, NetError>","(BindOptions) -> Result<Socket, NetError>","Result<Address, NetError>","Address","Result<UnixConnection, NetError>","(ConnectOptions) -> Result<UnixConnection, NetError>","Result<UnixListener, NetError>","(ListenOptions) -> Result<UnixListener, NetError>","[String]","Net.Socket.TCP.Endpoint","ClientConfig","Result<TLSConnection, NetError>"],"urlPath":"net/socket"},{"anchor":"module-net-socket-tcp","kind":"module","line":13,"name":"Net.Socket.TCP","qualifiedName":"Net.Socket.TCP","signatures":["host(name, port) : String -> Net.Port -> Endpoint","any(port) : Net.Port -> Endpoint","loopback(port) : Net.Port -> Endpoint","connect(endpoint) : Endpoint -> Result<TCPConnection, NetError>","connect(endpoint) : Endpoint -> ConnectOptions -> Result<TCPConnection, NetError>","listen(endpoint) : Endpoint -> Result<TCPListener, NetError>","listen(endpoint) : Endpoint -> ListenOptions -> Result<TCPListener, NetError>","sendAll(connection, data)","receiveChunk(connection, limit)","receiveExactly(connection, count)","receiveUntil(connection, delimiter, limit)","receiveLine(connection, limit)","shutdownWrite(connection)","accept(listener)","close(connection)","closed?(connection)","localAddress(connection)","peerAddress(connection)"],"summary":"","types":["String","Net.Port","Duration","Bool","Integer","Endpoint","Result<TCPConnection, NetError>","(ConnectOptions) -> Result<TCPConnection, NetError>","Result<TCPListener, NetError>","(ListenOptions) -> Result<TCPListener, NetError>"],"urlPath":"net/socket"},{"anchor":"type-plain","kind":"type","line":15,"name":"Plain","qualifiedName":"Net.Socket.TCP.Plain","signatures":[],"summary":"Marker for an unencrypted stream.","types":[],"urlPath":"net/socket"},{"anchor":"type-tcpconnection","kind":"type","line":17,"name":"TCPConnection","qualifiedName":"Net.Socket.TCP.TCPConnection","signatures":[],"summary":"An opaque connected TCP stream.","types":[],"urlPath":"net/socket"},{"anchor":"type-tcplistener","kind":"type","line":19,"name":"TCPListener","qualifiedName":"Net.Socket.TCP.TCPListener","signatures":[],"summary":"An opaque TCP listening socket.","types":[],"urlPath":"net/socket"},{"anchor":"record-endpoint","kind":"record","line":22,"name":"Endpoint","qualifiedName":"Net.Socket.TCP.Endpoint","signatures":[],"summary":"A host name or numeric address paired with a validated port.","types":["String","Net.Port"],"urlPath":"net/socket"},{"anchor":"record-connectoptions","kind":"record","line":32,"name":"ConnectOptions","qualifiedName":"Net.Socket.TCP.ConnectOptions","signatures":[],"summary":"Connection deadlines and operating-system socket policy.\n\nBuffer values of zero leave sizing to the operating system. `noDelay?` disables Nagle's algorithm, which is usually right for request/response traffic; bulk-transfer protocols may prefer fewer, larger packets.","types":["Duration","Bool","Integer"],"urlPath":"net/socket"},{"anchor":"record-listenoptions","kind":"record","line":44,"name":"ListenOptions","qualifiedName":"Net.Socket.TCP.ListenOptions","signatures":[],"summary":"Listener queue and policy inherited by accepted connections.\n\n`backlog` bounds connections waiting for `accept`. Buffer values of zero keep the platform defaults rather than requesting a particular byte size.","types":["Integer","Bool"],"urlPath":"net/socket"},{"anchor":"module-net-socket-tcp-endpoint","kind":"module","line":53,"name":"Net.Socket.TCP.Endpoint","qualifiedName":"Net.Socket.TCP.Endpoint","signatures":["host(name, port) : String -> Net.Port -> Endpoint","any(port) : Net.Port -> Endpoint","loopback(port) : Net.Port -> Endpoint"],"summary":"","types":["String","Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-host","kind":"function","line":63,"name":"host","qualifiedName":"Net.Socket.TCP.Endpoint.host","signatures":["host(name, port) : String -> Net.Port -> Endpoint"],"summary":"Pairs a hostname or numeric address with a port.\n\nResolution happens when connecting, so this preserves `name` exactly as supplied rather than validating it as an `IP.Address`.","types":["String","Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-any","kind":"function","line":74,"name":"any","qualifiedName":"Net.Socket.TCP.Endpoint.any","signatures":["any(port) : Net.Port -> Endpoint"],"summary":"Builds an IPv4 wildcard endpoint for listening on every local interface.\n\nBe deliberate with this in development: unlike `loopback`, it may expose the service to other machines on the network.","types":["Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-loopback","kind":"function","line":85,"name":"loopback","qualifiedName":"Net.Socket.TCP.Endpoint.loopback","signatures":["loopback(port) : Net.Port -> Endpoint"],"summary":"Builds an IPv4 loopback endpoint reachable only from this machine.\n\nPort zero lets the operating system choose a free port, which is useful for tests; ask `localAddress` which port was assigned after listening.","types":["Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-connect","kind":"function","line":96,"name":"connect","qualifiedName":"Net.Socket.TCP.connect","signatures":["connect(endpoint) : Endpoint -> Result<TCPConnection, NetError>","connect(endpoint) : Endpoint -> ConnectOptions -> Result<TCPConnection, NetError>"],"summary":"Connects to a TCP endpoint with the backend's bounded connect deadline.","types":["Endpoint","Result<TCPConnection, NetError>","(ConnectOptions) -> Result<TCPConnection, NetError>"],"urlPath":"net/socket"},{"anchor":"function-listen","kind":"function","line":107,"name":"listen","qualifiedName":"Net.Socket.TCP.listen","signatures":["listen(endpoint) : Endpoint -> Result<TCPListener, NetError>","listen(endpoint) : Endpoint -> ListenOptions -> Result<TCPListener, NetError>"],"summary":"Binds and starts listening. Port zero selects an ephemeral local port.","types":["Endpoint","Result<TCPListener, NetError>","(ListenOptions) -> Result<TCPListener, NetError>"],"urlPath":"net/socket"},{"anchor":"function-sendall","kind":"function","line":119,"name":"sendAll","qualifiedName":"Net.Socket.TCP.sendAll","signatures":["sendAll(connection, data)"],"summary":"Sends every byte, retrying partial operating-system writes internally.\n\nOn failure, `NetError.progress` records how many bytes were accepted before the error. Do not blindly retry the whole payload when progress is present.","types":[],"urlPath":"net/socket"},{"anchor":"function-receivechunk","kind":"function","line":129,"name":"receiveChunk","qualifiedName":"Net.Socket.TCP.receiveChunk","signatures":["receiveChunk(connection, limit)"],"summary":"Receives up to `limit` bytes; EOF is reported as `Closed`.\n\nA successful result is one available chunk, not necessarily a complete application message. Use `receiveExactly`, `receiveUntil`, or `receiveLine` when the protocol supplies a boundary.","types":[],"urlPath":"net/socket"},{"anchor":"function-receiveexactly","kind":"function","line":137,"name":"receiveExactly","qualifiedName":"Net.Socket.TCP.receiveExactly","signatures":["receiveExactly(connection, count)"],"summary":"Receives exactly `count` bytes or returns a typed EOF/timeout failure.\n\nUseful after a protocol header has declared the payload length.","types":[],"urlPath":"net/socket"},{"anchor":"function-receiveuntil","kind":"function","line":145,"name":"receiveUntil","qualifiedName":"Net.Socket.TCP.receiveUntil","signatures":["receiveUntil(connection, delimiter, limit)"],"summary":"Receives through the first `delimiter` without exceeding `limit` bytes.\n\nThe delimiter is included in the returned bytes. An empty delimiter is a `Parse` error; reaching the bound first is a `Limit` error.","types":[],"urlPath":"net/socket"},{"anchor":"function-receiveline","kind":"function","line":154,"name":"receiveLine","qualifiedName":"Net.Socket.TCP.receiveLine","signatures":["receiveLine(connection, limit)"],"summary":"Receives through a newline without exceeding `limit` bytes.\n\nThe newline remains in the returned binary. Decode and trim only after a complete bounded line has been received.","types":[],"urlPath":"net/socket"},{"anchor":"function-shutdownwrite","kind":"function","line":157,"name":"shutdownWrite","qualifiedName":"Net.Socket.TCP.shutdownWrite","signatures":["shutdownWrite(connection)"],"summary":"Half-closes the write side while leaving reads available.","types":[],"urlPath":"net/socket"},{"anchor":"function-accept","kind":"function","line":165,"name":"accept","qualifiedName":"Net.Socket.TCP.accept","signatures":["accept(listener)"],"summary":"Waits for and returns the next connection accepted by the listener.\n\nA timeout applies to this wait only; it does not become a read timeout on the returned connection.","types":[],"urlPath":"net/socket"},{"anchor":"function-close","kind":"function","line":168,"name":"close","qualifiedName":"Net.Socket.TCP.close","signatures":["close(connection)"],"summary":"Idempotently closes a connected stream.","types":[],"urlPath":"net/socket"},{"anchor":"function-closed?","kind":"function","line":172,"name":"closed?","qualifiedName":"Net.Socket.TCP.closed?","signatures":["closed?(connection)"],"summary":"","types":[],"urlPath":"net/socket"},{"anchor":"function-localaddress","kind":"function","line":176,"name":"localAddress","qualifiedName":"Net.Socket.TCP.localAddress","signatures":["localAddress(connection)"],"summary":"Returns the bound local endpoint, including an ephemeral assigned port.","types":[],"urlPath":"net/socket"},{"anchor":"function-peeraddress","kind":"function","line":184,"name":"peerAddress","qualifiedName":"Net.Socket.TCP.peerAddress","signatures":["peerAddress(connection)"],"summary":"Returns the remote endpoint of a connected stream.","types":[],"urlPath":"net/socket"},{"anchor":"module-net-socket-udp","kind":"module","line":188,"name":"Net.Socket.UDP","qualifiedName":"Net.Socket.UDP","signatures":["host(name, port) : String -> Net.Port -> Endpoint","any(port) : Net.Port -> Endpoint","loopback(port) : Net.Port -> Endpoint","bind(endpoint) : Endpoint -> Result<Socket, NetError>","bind(endpoint) : Endpoint -> BindOptions -> Result<Socket, NetError>","sendTo(socket, endpoint, data)","receiveFrom(socket, limit)","close(socket)","closed?(socket)","localAddress(socket)","joinMulticast(socket, group, interface)","leaveMulticast(socket, group, interface)"],"summary":"","types":["String","Net.Port","Endpoint","Binary","Bool","Integer","Duration","Result<Socket, NetError>","(BindOptions) -> Result<Socket, NetError>"],"urlPath":"net/socket"},{"anchor":"type-socket","kind":"type","line":199,"name":"Socket","qualifiedName":"Net.Socket.UDP.Socket","signatures":[],"summary":"Connectionless datagrams. A receive limit rejects an oversized datagram instead of returning a silently truncated payload.\n\n```kex\nlet socket = UDP.bind(UDP.Endpoint.loopback(Port.from(0).try)).try\nlet address = socket.localAddress.try\nsocket.sendTo(address, \"hello\".to(Binary).try).try\nlet packet = socket.receiveFrom(1024).try\nsocket.close\n```\n\nAn opaque bound datagram socket.","types":[],"urlPath":"net/socket"},{"anchor":"record-endpoint","kind":"record","line":201,"name":"Endpoint","qualifiedName":"Net.Socket.UDP.Endpoint","signatures":[],"summary":"A datagram address paired with a validated port.","types":["String","Net.Port"],"urlPath":"net/socket"},{"anchor":"record-datagram","kind":"record","line":209,"name":"Datagram","qualifiedName":"Net.Socket.UDP.Datagram","signatures":[],"summary":"A received datagram and its source endpoint.\n\nReply to `source` rather than the socket's local address: UDP has no connection that remembers which peer sent the packet.","types":["Endpoint","Binary"],"urlPath":"net/socket"},{"anchor":"record-bindoptions","kind":"record","line":215,"name":"BindOptions","qualifiedName":"Net.Socket.UDP.BindOptions","signatures":[],"summary":"Curated socket policy. Broadcast is opt-in. The receive timeout applies to each `receiveFrom` call; multicast TTL is bounded to the IP hop-limit range.","types":["Bool","Integer","Duration"],"urlPath":"net/socket"},{"anchor":"module-net-socket-udp-endpoint","kind":"module","line":221,"name":"Net.Socket.UDP.Endpoint","qualifiedName":"Net.Socket.UDP.Endpoint","signatures":["host(name, port) : String -> Net.Port -> Endpoint","any(port) : Net.Port -> Endpoint","loopback(port) : Net.Port -> Endpoint"],"summary":"","types":["String","Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-host","kind":"function","line":228,"name":"host","qualifiedName":"Net.Socket.UDP.Endpoint.host","signatures":["host(name, port) : String -> Net.Port -> Endpoint"],"summary":"Pairs a hostname or numeric address with a UDP port.","types":["String","Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-any","kind":"function","line":236,"name":"any","qualifiedName":"Net.Socket.UDP.Endpoint.any","signatures":["any(port) : Net.Port -> Endpoint"],"summary":"Builds an IPv4 wildcard endpoint for receiving on every local interface.","types":["Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-loopback","kind":"function","line":244,"name":"loopback","qualifiedName":"Net.Socket.UDP.Endpoint.loopback","signatures":["loopback(port) : Net.Port -> Endpoint"],"summary":"Builds an IPv4 loopback endpoint reachable only from this machine.","types":["Net.Port","Endpoint"],"urlPath":"net/socket"},{"anchor":"function-bind","kind":"function","line":252,"name":"bind","qualifiedName":"Net.Socket.UDP.bind","signatures":["bind(endpoint) : Endpoint -> Result<Socket, NetError>","bind(endpoint) : Endpoint -> BindOptions -> Result<Socket, NetError>"],"summary":"Binds a datagram socket. Port zero selects an ephemeral local port.","types":["Endpoint","Result<Socket, NetError>","(BindOptions) -> Result<Socket, NetError>"],"urlPath":"net/socket"},{"anchor":"function-sendto","kind":"function","line":265,"name":"sendTo","qualifiedName":"Net.Socket.UDP.sendTo","signatures":["sendTo(socket, endpoint, data)"],"summary":"Sends one complete datagram and returns its byte count.\n\nDatagram boundaries are preserved: one `sendTo` corresponds to one `receiveFrom`, unless the packet is lost by the network.","types":[],"urlPath":"net/socket"},{"anchor":"function-receivefrom","kind":"function","line":274,"name":"receiveFrom","qualifiedName":"Net.Socket.UDP.receiveFrom","signatures":["receiveFrom(socket, limit)"],"summary":"Receives one datagram no larger than `limit` bytes.\n\nOversized packets fail with `Limit` instead of being silently truncated, so a caller never mistakes a prefix for a complete message.","types":[],"urlPath":"net/socket"},{"anchor":"function-close","kind":"function","line":276,"name":"close","qualifiedName":"Net.Socket.UDP.close","signatures":["close(socket)"],"summary":"Idempotently closes the socket.","types":[],"urlPath":"net/socket"},{"anchor":"function-closed?","kind":"function","line":278,"name":"closed?","qualifiedName":"Net.Socket.UDP.closed?","signatures":["closed?(socket)"],"summary":"","types":[],"urlPath":"net/socket"},{"anchor":"function-localaddress","kind":"function","line":280,"name":"localAddress","qualifiedName":"Net.Socket.UDP.localAddress","signatures":["localAddress(socket)"],"summary":"Returns the bound endpoint, including an ephemeral assigned port.","types":[],"urlPath":"net/socket"},{"anchor":"function-joinmulticast","kind":"function","line":283,"name":"joinMulticast","qualifiedName":"Net.Socket.UDP.joinMulticast","signatures":["joinMulticast(socket, group, interface)"],"summary":"Joins an IPv4 multicast group on the selected local interface. The group must be multicast and the interface must be an IPv4 address.","types":[],"urlPath":"net/socket"},{"anchor":"function-leavemulticast","kind":"function","line":285,"name":"leaveMulticast","qualifiedName":"Net.Socket.UDP.leaveMulticast","signatures":["leaveMulticast(socket, group, interface)"],"summary":"Leaves a membership previously joined with the same group and interface.","types":[],"urlPath":"net/socket"},{"anchor":"module-net-socket-unix","kind":"module","line":289,"name":"Net.Socket.Unix","qualifiedName":"Net.Socket.Unix","signatures":["path(value) : String -> Result<Address, NetError>","connect(address) : Address -> Result<UnixConnection, NetError>","connect(address) : Address -> ConnectOptions -> Result<UnixConnection, NetError>","listen(address) : Address -> Result<UnixListener, NetError>","listen(address) : Address -> ListenOptions -> Result<UnixListener, NetError>","sendAll(connection, data)","receiveChunk(connection, limit)","receiveExactly(connection, count)","receiveUntil(connection, delimiter, limit)","receiveLine(connection, limit)","shutdownWrite(connection)","accept(listener)","close(connection)","closed?(connection)"],"summary":"","types":["String","Duration","Integer","Bool","Result<Address, NetError>","Address","Result<UnixConnection, NetError>","(ConnectOptions) -> Result<UnixConnection, NetError>","Result<UnixListener, NetError>","(ListenOptions) -> Result<UnixListener, NetError>"],"urlPath":"net/socket"},{"anchor":"type-unixconnection","kind":"type","line":299,"name":"UnixConnection","qualifiedName":"Net.Socket.Unix.UnixConnection","signatures":[],"summary":"Filesystem-domain streams for local IPC. The listener owns and removes only the socket path it successfully created.\n\n```kex\nlet address = Unix.Address.path(\"/tmp/my-service.sock\").try\nlet listener = Unix.listen(address).try\nlet client = Unix.connect(address).try\nlistener.close\n```\n\nAn opaque connected filesystem-domain byte stream.","types":[],"urlPath":"net/socket"},{"anchor":"type-unixlistener","kind":"type","line":301,"name":"UnixListener","qualifiedName":"Net.Socket.Unix.UnixListener","signatures":[],"summary":"An opaque filesystem-domain stream listener.","types":[],"urlPath":"net/socket"},{"anchor":"record-address","kind":"record","line":303,"name":"Address","qualifiedName":"Net.Socket.Unix.Address","signatures":[],"summary":"A validated absolute filesystem socket path.","types":["String"],"urlPath":"net/socket"},{"anchor":"record-connectoptions","kind":"record","line":308,"name":"ConnectOptions","qualifiedName":"Net.Socket.Unix.ConnectOptions","signatures":[],"summary":"Connection and read deadlines for a local IPC client.","types":["Duration"],"urlPath":"net/socket"},{"anchor":"record-listenoptions","kind":"record","line":317,"name":"ListenOptions","qualifiedName":"Net.Socket.Unix.ListenOptions","signatures":[],"summary":"Listener queue, stale-socket policy, and per-operation deadlines.\n\n`removeStale?` removes only a filesystem socket, never a regular file or directory that happens to occupy the requested path.","types":["Integer","Bool","Duration"],"urlPath":"net/socket"},{"anchor":"module-net-socket-unix-address","kind":"module","line":323,"name":"Net.Socket.Unix.Address","qualifiedName":"Net.Socket.Unix.Address","signatures":["path(value) : String -> Result<Address, NetError>"],"summary":"","types":["String","Result<Address, NetError>"],"urlPath":"net/socket"},{"anchor":"function-path","kind":"function","line":333,"name":"path","qualifiedName":"Net.Socket.Unix.Address.path","signatures":["path(value) : String -> Result<Address, NetError>"],"summary":"Validates a nonempty absolute Unix-domain socket path.\n\nRelative paths are rejected so ownership and cleanup always refer to one unambiguous filesystem entry.","types":["String","Result<Address, NetError>"],"urlPath":"net/socket"},{"anchor":"function-connect","kind":"function","line":343,"name":"connect","qualifiedName":"Net.Socket.Unix.connect","signatures":["connect(address) : Address -> Result<UnixConnection, NetError>","connect(address) : Address -> ConnectOptions -> Result<UnixConnection, NetError>"],"summary":"Connects to a filesystem-domain listener.\n\nUnix sockets avoid opening a network port and are a good fit for two processes on the same machine, such as a CLI and its background daemon.","types":["Address","Result<UnixConnection, NetError>","(ConnectOptions) -> Result<UnixConnection, NetError>"],"urlPath":"net/socket"},{"anchor":"function-listen","kind":"function","line":351,"name":"listen","qualifiedName":"Net.Socket.Unix.listen","signatures":["listen(address) : Address -> Result<UnixListener, NetError>","listen(address) : Address -> ListenOptions -> Result<UnixListener, NetError>"],"summary":"Binds a new path; an existing filesystem entry is never removed implicitly.\n\nThis conservative default protects regular files and also avoids taking over a socket that may still belong to a running service.","types":["Address","Result<UnixListener, NetError>","(ListenOptions) -> Result<UnixListener, NetError>"],"urlPath":"net/socket"},{"anchor":"function-sendall","kind":"function","line":359,"name":"sendAll","qualifiedName":"Net.Socket.Unix.sendAll","signatures":["sendAll(connection, data)"],"summary":"Sends every byte and returns the count.","types":[],"urlPath":"net/socket"},{"anchor":"function-receivechunk","kind":"function","line":361,"name":"receiveChunk","qualifiedName":"Net.Socket.Unix.receiveChunk","signatures":["receiveChunk(connection, limit)"],"summary":"Receives one bounded chunk; EOF is `Closed`.","types":[],"urlPath":"net/socket"},{"anchor":"function-receiveexactly","kind":"function","line":363,"name":"receiveExactly","qualifiedName":"Net.Socket.Unix.receiveExactly","signatures":["receiveExactly(connection, count)"],"summary":"Receives exactly `count` bytes or returns a typed EOF/timeout failure.","types":[],"urlPath":"net/socket"},{"anchor":"function-receiveuntil","kind":"function","line":366,"name":"receiveUntil","qualifiedName":"Net.Socket.Unix.receiveUntil","signatures":["receiveUntil(connection, delimiter, limit)"],"summary":"Receives through `delimiter`, including it, within an explicit bound.","types":[],"urlPath":"net/socket"},{"anchor":"function-receiveline","kind":"function","line":368,"name":"receiveLine","qualifiedName":"Net.Socket.Unix.receiveLine","signatures":["receiveLine(connection, limit)"],"summary":"Receives through a newline without exceeding `limit` bytes.","types":[],"urlPath":"net/socket"},{"anchor":"function-shutdownwrite","kind":"function","line":370,"name":"shutdownWrite","qualifiedName":"Net.Socket.Unix.shutdownWrite","signatures":["shutdownWrite(connection)"],"summary":"Half-closes the write side while leaving reads available.","types":[],"urlPath":"net/socket"},{"anchor":"function-accept","kind":"function","line":372,"name":"accept","qualifiedName":"Net.Socket.Unix.accept","signatures":["accept(listener)"],"summary":"Waits for and returns the next stream connection.","types":[],"urlPath":"net/socket"},{"anchor":"function-close","kind":"function","line":374,"name":"close","qualifiedName":"Net.Socket.Unix.close","signatures":["close(connection)"],"summary":"Idempotently closes a stream.","types":[],"urlPath":"net/socket"},{"anchor":"function-closed?","kind":"function","line":378,"name":"closed?","qualifiedName":"Net.Socket.Unix.closed?","signatures":["closed?(connection)"],"summary":"","types":[],"urlPath":"net/socket"},{"anchor":"module-net-socket-tls","kind":"module","line":384,"name":"Net.Socket.TLS","qualifiedName":"Net.Socket.TLS","signatures":["connect(endpoint, config) : Net.Socket.TCP.Endpoint -> ClientConfig -> Result<TLSConnection, NetError>","sendAll(connection, data)","receiveChunk(connection, limit)","close(connection)","closed?(connection)"],"summary":"","types":["String","Bool","[String]","Net.Socket.TCP.Endpoint","ClientConfig","Result<TLSConnection, NetError>"],"urlPath":"net/socket"},{"anchor":"type-tlsconnection","kind":"type","line":395,"name":"TLSConnection","qualifiedName":"Net.Socket.TLS.TLSConnection","signatures":[],"summary":"TLS client streams. Certificate and hostname verification are enabled by default; disabling verification must be an explicit configuration choice.\n\n```kex\nlet endpoint = TCP.Endpoint.host(\"example.test\", Port.from(443).try)\nlet tls = TLS.connect(endpoint, TLS.ClientConfig {\n  serverName: \"example.test\"\n}).try\ntls.close\n```\n\nAn opaque verified or explicitly unverified TLS byte stream.","types":[],"urlPath":"net/socket"},{"anchor":"record-clientconfig","kind":"record","line":397,"name":"ClientConfig","qualifiedName":"Net.Socket.TLS.ClientConfig","signatures":[],"summary":"Client handshake policy. TLS 1.2/1.3 are enabled; verification defaults on.","types":["String","Bool","[String]"],"urlPath":"net/socket"},{"anchor":"function-connect","kind":"function","line":415,"name":"connect","qualifiedName":"Net.Socket.TLS.connect","signatures":["connect(endpoint, config) : Net.Socket.TCP.Endpoint -> ClientConfig -> Result<TLSConnection, NetError>"],"summary":"Opens a direct TLS connection with a bounded handshake deadline.\n\n`serverName` drives both certificate hostname verification and SNI. Pass the DNS name from the URL, not an address it happened to resolve to.","types":["Net.Socket.TCP.Endpoint","ClientConfig","Result<TLSConnection, NetError>"],"urlPath":"net/socket"},{"anchor":"function-sendall","kind":"function","line":419,"name":"sendAll","qualifiedName":"Net.Socket.TLS.sendAll","signatures":["sendAll(connection, data)"],"summary":"Sends every plaintext byte through the TLS stream.","types":[],"urlPath":"net/socket"},{"anchor":"function-receivechunk","kind":"function","line":421,"name":"receiveChunk","qualifiedName":"Net.Socket.TLS.receiveChunk","signatures":["receiveChunk(connection, limit)"],"summary":"Receives one decrypted chunk no larger than `limit`.","types":[],"urlPath":"net/socket"},{"anchor":"function-close","kind":"function","line":423,"name":"close","qualifiedName":"Net.Socket.TLS.close","signatures":["close(connection)"],"summary":"Idempotently closes the TLS stream.","types":[],"urlPath":"net/socket"},{"anchor":"function-closed?","kind":"function","line":425,"name":"closed?","qualifiedName":"Net.Socket.TLS.closed?","signatures":["closed?(connection)"],"summary":"","types":[],"urlPath":"net/socket"},{"anchor":"module-net-http-websocket","kind":"module","line":15,"name":"Net.HTTP.WebSocket","qualifiedName":"Net.HTTP.WebSocket","signatures":["connect(url)","send(message)","receiveMessage()","session()","close()","closed?()"],"summary":"High-level RFC 6455 client messages. The runtime handles fragmentation and ping/pong frames; reconnect and heartbeat policies remain application-owned.\n\nA `Connection` delivers complete messages rather than wire frames. Your code never has to assemble fragments or answer a protocol ping, but it does decide what a dropped connection means: reconnecting may require resubscribing or replaying an application cursor, so the library cannot do that safely for you.\n\n```kex\nusing Net.HTTP.WebSocket\n\nlet socket = WebSocket.connect(\"wss://example.test/events\").try\nsocket.send(Text(\"hello\")).try\nlet message = socket.receiveMessage.try\nsocket.close\n```","types":["String","Binary","Integer","[String]","String?"],"urlPath":"net/http/websocket"},{"anchor":"type-message","kind":"type","line":22,"name":"Message","qualifiedName":"Net.HTTP.WebSocket.Message","signatures":[],"summary":"A complete high-level WebSocket message. Fragmentation and ping/pong control frames are handled by the connection runtime.\n\n`CloseMessage` carries the peer's status code and reason. Treat it as the end of the message stream even when the code describes a normal shutdown.","types":["String","Binary","Integer","String"],"urlPath":"net/http/websocket"},{"anchor":"record-clientoptions","kind":"record","line":29,"name":"ClientOptions","qualifiedName":"Net.HTTP.WebSocket.ClientOptions","signatures":[],"summary":"Client handshake policy and the maximum reassembled message size.\n\nSubprotocols are offered in preference order. The byte limit applies after fragments are reassembled, preventing a peer from bypassing the bound with many individually small frames.","types":["[String]","Integer"],"urlPath":"net/http/websocket"},{"anchor":"record-session","kind":"record","line":36,"name":"Session","qualifiedName":"Net.HTTP.WebSocket.Session","signatures":[],"summary":"Negotiated handshake information. The subprotocol is `None` when the server selected none.","types":["String?"],"urlPath":"net/http/websocket"},{"anchor":"type-connection","kind":"type","line":41,"name":"Connection","qualifiedName":"Net.HTTP.WebSocket.Connection","signatures":[],"summary":"An opaque RFC 6455 client connection. It does not reconnect automatically.","types":[],"urlPath":"net/http/websocket"},{"anchor":"module-net-http-websocket-websocket","kind":"module","line":44,"name":"Net.HTTP.WebSocket.WebSocket","qualifiedName":"Net.HTTP.WebSocket.WebSocket","signatures":["connect(url)"],"summary":"Constructors for high-level WebSocket client connections.","types":[],"urlPath":"net/http/websocket"},{"anchor":"function-connect","kind":"function","line":56,"name":"connect","qualifiedName":"Net.HTTP.WebSocket.WebSocket.connect","signatures":["connect(url)"],"summary":"Opens a `ws:` or verified `wss:` connection with default options.","types":[],"urlPath":"net/http/websocket"},{"anchor":"make-connection","kind":"make","line":70,"name":"Connection","qualifiedName":"Connection","signatures":["send(message)","receiveMessage()","session()","close()","closed?()"],"summary":"","types":[],"urlPath":"net/http/websocket"},{"anchor":"fn-send","kind":"function","line":77,"name":"send","qualifiedName":"Connection.send","signatures":["send(message)"],"summary":"Sends one masked text, binary, or close message.","types":[],"urlPath":"net/http/websocket"},{"anchor":"fn-receivemessage","kind":"function","line":95,"name":"receiveMessage","qualifiedName":"Connection.receiveMessage","signatures":["receiveMessage()"],"summary":"`receive` is a Kex process keyword, so the public method spells out the operation while preserving the plan's high-level message semantics. Reassembles fragments, validates UTF-8, and automatically answers pings.\n\nA `CloseMessage` is returned once so the application can inspect the peer's reason. Subsequent reads fail with `Closed`.","types":[],"urlPath":"net/http/websocket"},{"anchor":"fn-session","kind":"function","line":102,"name":"session","qualifiedName":"Connection.session","signatures":["session()"],"summary":"Returns handshake details negotiated with the server.","types":[],"urlPath":"net/http/websocket"},{"anchor":"fn-close","kind":"function","line":109,"name":"close","qualifiedName":"Connection.close","signatures":["close()"],"summary":"Sends a normal close frame and idempotently releases the transport.\n\nUse an explicit `CloseMessage` with `send` first when the peer needs an application-specific code or reason.","types":[],"urlPath":"net/http/websocket"},{"anchor":"fn-closed?","kind":"function","line":111,"name":"closed?","qualifiedName":"Connection.closed?","signatures":["closed?()"],"summary":"","types":[],"urlPath":"net/http/websocket"},{"anchor":"module-units-data","kind":"module","line":18,"name":"Units.Data","qualifiedName":"Units.Data","signatures":["factor(@B)","symbol(@B)","size(value, unit)","convertTo(measure, unit)","to(measure, String, in)","byteSize(value)","kilobytes(value)","megabytes(value)","gigabytes(value)","terabytes(value)","kibibytes(value)","mebibytes(value)","gibibytes(value)","tebibytes(value)"],"summary":"Data sizes: bytes, kilobytes, and their binary counterparts.\n\nOpt-in: nothing here is in scope until `using Units.Data`.\n\n```kex\nusing Units.Data\n\nmain do\n  IO.printLine(1500.megabytes.to(String))              # prints: 1500.0 MB\n  IO.printLine(1500000000.byteSize.to(String, in: Giga)) # prints: 1.5 GB\nend\n```\n\nBoth families are here and they are not the same: `KB` is 1000 bytes, `KiB` is 1024. Values built from either convert freely, because both are counted in bytes underneath, so `1.gibibytes.convertTo(MiB)` answers 1024 MiB.\n\nEvery value is a `Measure` from the prelude, so its arithmetic, comparison and `to(String)` apply unchanged.","types":[],"urlPath":"units/data"},{"anchor":"type-dataunit","kind":"type","line":25,"name":"DataUnit","qualifiedName":"Units.Data.DataUnit","signatures":[],"summary":"The data units this module names: decimal (`KB`, `MB`, `GB`, `TB`) and binary (`KiB`, `MiB`, `GiB`, `TiB`), plus the plain byte `B`.\n\n```kex\n1.KB   # 1000 bytes\n1.KiB  # 1024 bytes\n```","types":[],"urlPath":"units/data"},{"anchor":"type-dataprefix","kind":"type","line":32,"name":"DataPrefix","qualifiedName":"Units.Data.DataPrefix","signatures":[],"summary":"A decimal prefix to render a size at: `Kilo` means KB, `Mega` means MB, `Giga` means GB.\n\nData prefixes select their standard decimal byte unit, so `Mega` is MB rather than a prefix applied twice to the measure's existing unit.","types":[],"urlPath":"units/data"},{"anchor":"make-dataunit","kind":"make","line":34,"name":"DataUnit","qualifiedName":"DataUnit","signatures":["factor(@B)","symbol(@B)"],"summary":"","types":[],"urlPath":"units/data"},{"anchor":"fn-factor","kind":"function","line":35,"name":"factor","qualifiedName":"DataUnit.factor","signatures":["factor(@B)"],"summary":"","types":[],"urlPath":"units/data"},{"anchor":"fn-symbol","kind":"function","line":47,"name":"symbol","qualifiedName":"DataUnit.symbol","signatures":["symbol(@B)"],"summary":"","types":[],"urlPath":"units/data"},{"anchor":"make-unitdefinition","kind":"make","line":58,"name":"UnitDefinition","qualifiedName":"UnitDefinition","signatures":[],"summary":"","types":[],"urlPath":"units/data"},{"anchor":"make-measure","kind":"make","line":64,"name":"Measure","qualifiedName":"Measure","signatures":[],"summary":"","types":[],"urlPath":"units/data"},{"anchor":"function-size","kind":"function","line":83,"name":"size","qualifiedName":"Units.Data.size","signatures":["size(value, unit)"],"summary":"`value` of the given data unit, as a `Measure`.\n\nThe general constructor the named ones below are written on. Reach for `megabytes`, `kibibytes` and friends when the unit is known at the call site.","types":[],"urlPath":"units/data"},{"anchor":"function-convertto","kind":"function","line":104,"name":"convertTo","qualifiedName":"Units.Data.convertTo","signatures":["convertTo(measure, unit)"],"summary":"Converts a size to another data unit.\n\nDecimal and binary units convert freely, because both are counted in bytes underneath. A measure of some other dimension is an `Error`.","types":[],"urlPath":"units/data"},{"anchor":"function-to","kind":"function","line":129,"name":"to","qualifiedName":"Units.Data.to","signatures":["to(measure, String, in)"],"summary":"Renders a size at a chosen decimal prefix.\n\nData prefixes select their standard decimal byte unit. They are targets for formatting, so `Mega` means MB rather than a prefix applied twice to the measure's existing unit. A measure that is not a data size answers `None`.","types":[],"urlPath":"units/data"},{"anchor":"function-bytesize","kind":"function","line":156,"name":"byteSize","qualifiedName":"Units.Data.byteSize","signatures":["byteSize(value)"],"summary":"`value` bytes.\n\nNamed `byteSize` rather than `bytes`, which `String` already uses for its UTF-8 encoding.","types":[],"urlPath":"units/data"},{"anchor":"function-kilobytes","kind":"function","line":164,"name":"kilobytes","qualifiedName":"Units.Data.kilobytes","signatures":["kilobytes(value)"],"summary":"`value` kilobytes, 1000 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-megabytes","kind":"function","line":172,"name":"megabytes","qualifiedName":"Units.Data.megabytes","signatures":["megabytes(value)"],"summary":"`value` megabytes, 1000000 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-gigabytes","kind":"function","line":180,"name":"gigabytes","qualifiedName":"Units.Data.gigabytes","signatures":["gigabytes(value)"],"summary":"`value` gigabytes, 10^9 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-terabytes","kind":"function","line":188,"name":"terabytes","qualifiedName":"Units.Data.terabytes","signatures":["terabytes(value)"],"summary":"`value` terabytes, 10^12 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-kibibytes","kind":"function","line":196,"name":"kibibytes","qualifiedName":"Units.Data.kibibytes","signatures":["kibibytes(value)"],"summary":"`value` kibibytes, 1024 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-mebibytes","kind":"function","line":204,"name":"mebibytes","qualifiedName":"Units.Data.mebibytes","signatures":["mebibytes(value)"],"summary":"`value` mebibytes, 1024^2 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-gibibytes","kind":"function","line":212,"name":"gibibytes","qualifiedName":"Units.Data.gibibytes","signatures":["gibibytes(value)"],"summary":"`value` gibibytes, 1024^3 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"function-tebibytes","kind":"function","line":220,"name":"tebibytes","qualifiedName":"Units.Data.tebibytes","signatures":["tebibytes(value)"],"summary":"`value` tebibytes, 1024^4 bytes each.","types":[],"urlPath":"units/data"},{"anchor":"module-units-si","kind":"module","line":19,"name":"Units.SI","qualifiedName":"Units.SI","signatures":["factor(@Kilo(unit))","kind(@Kilo(unit))","symbol(@Kilo(unit))","kind(@Meter)","symbol(@Meter)","meter(value)","gram(value)","kilogram(value)","kelvin(value)","liter(value)","newton(value)","joule(value)","watt(value)","volt(value)","ampere(value)","ohm(value)","coulomb(value)","*(@Watt, @Hour)","to(measure, String)","mega(measure)","giga(measure)","milli(measure)","micro(measure)","nano(measure)","per(measure, other) : Measure -> Measure -> Measure","times(measure, other) : Measure -> Measure -> Measure","*(other)","/(other)","product(other)","quotient(other)","productKind(mass, acceleration)","quotientKind(length, time)","productSymbol(force, _, _)","quotientSymbol(speed, left, _)"],"summary":"SI units: metres, grams, watts, volts and the rest, with prefixes and dimensional arithmetic.\n\nOpt-in: nothing here is in scope until `using Units.SI`.\n\n```kex\nusing Units.SI\n\nmain do\n  IO.printLine(3.kilo.watt.to(String))          # prints: 3000.0 W\n  IO.printLine(5000.meter.kilo.to(String))      # prints: 5.0 km\n  IO.printLine((100.meter / 10.sec).to(String)) # prints: 10.0 m/s\nend\n```\n\nEvery value is a `Measure` from the prelude, so the arithmetic, conversion and comparison described there apply unchanged. What this module adds is the SI vocabulary, the prefixes, and a table of which dimension results from multiplying or dividing two others, so `2.newton * 3.meter` answers in joules and `100.meter / 10.sec` in metres per second.","types":["Unit","Measure"],"urlPath":"units/si"},{"anchor":"type-siunit","kind":"type","line":25,"name":"SIUnit","qualifiedName":"Units.SI.SIUnit","signatures":[],"summary":"The SI units this module names.\n\nEach carries its dimension (`:length`, `:mass`, `:power`, …) and its symbol, which is what a `Measure` built from it displays with.","types":[],"urlPath":"units/si"},{"anchor":"type-siprefix","kind":"type","line":35,"name":"SIPrefix","qualifiedName":"Units.SI.SIPrefix","signatures":[],"summary":"A decimal prefix applied to a unit, for display.\n\nA display prefix carries the unit it will display, for example `Kilo(Watt * Hour)`. Pass one to `to(String, in:)` to render a measure at that scale.\n\n```kex\n1500.watt.to(String, in: Kilo(Watt))   # => Just(\"1.5 kW\")\n```","types":["Unit","Unit","Unit","Unit","Unit","Unit"],"urlPath":"units/si"},{"anchor":"make-siprefix","kind":"make","line":38,"name":"SIPrefix","qualifiedName":"SIPrefix","signatures":["factor(@Kilo(unit))","kind(@Kilo(unit))","symbol(@Kilo(unit))"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-factor","kind":"function","line":39,"name":"factor","qualifiedName":"SIPrefix.factor","signatures":["factor(@Kilo(unit))"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-kind","kind":"function","line":46,"name":"kind","qualifiedName":"SIPrefix.kind","signatures":["kind(@Kilo(unit))"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-symbol","kind":"function","line":53,"name":"symbol","qualifiedName":"SIPrefix.symbol","signatures":["symbol(@Kilo(unit))"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"make-siunit","kind":"make","line":61,"name":"SIUnit","qualifiedName":"SIUnit","signatures":["kind(@Meter)","symbol(@Meter)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-kind","kind":"function","line":63,"name":"kind","qualifiedName":"SIUnit.kind","signatures":["kind(@Meter)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-symbol","kind":"function","line":76,"name":"symbol","qualifiedName":"SIUnit.symbol","signatures":["symbol(@Meter)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"make-unitdefinition","kind":"make","line":90,"name":"UnitDefinition","qualifiedName":"UnitDefinition","signatures":[],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"make-measure","kind":"make","line":96,"name":"Measure","qualifiedName":"Measure","signatures":[],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"function-meter","kind":"function","line":110,"name":"meter","qualifiedName":"Units.SI.meter","signatures":["meter(value)"],"summary":"`value` metres, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-gram","kind":"function","line":118,"name":"gram","qualifiedName":"Units.SI.gram","signatures":["gram(value)"],"summary":"`value` grams, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-kilogram","kind":"function","line":126,"name":"kilogram","qualifiedName":"Units.SI.kilogram","signatures":["kilogram(value)"],"summary":"`value` kilograms, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-kelvin","kind":"function","line":134,"name":"kelvin","qualifiedName":"Units.SI.kelvin","signatures":["kelvin(value)"],"summary":"`value` kelvin, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-liter","kind":"function","line":142,"name":"liter","qualifiedName":"Units.SI.liter","signatures":["liter(value)"],"summary":"`value` litres, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-newton","kind":"function","line":150,"name":"newton","qualifiedName":"Units.SI.newton","signatures":["newton(value)"],"summary":"`value` newtons, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-joule","kind":"function","line":158,"name":"joule","qualifiedName":"Units.SI.joule","signatures":["joule(value)"],"summary":"`value` joules, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-watt","kind":"function","line":170,"name":"watt","qualifiedName":"Units.SI.watt","signatures":["watt(value)"],"summary":"`value` watts, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-volt","kind":"function","line":178,"name":"volt","qualifiedName":"Units.SI.volt","signatures":["volt(value)"],"summary":"`value` volts, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-ampere","kind":"function","line":186,"name":"ampere","qualifiedName":"Units.SI.ampere","signatures":["ampere(value)"],"summary":"`value` amperes, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-ohm","kind":"function","line":194,"name":"ohm","qualifiedName":"Units.SI.ohm","signatures":["ohm(value)"],"summary":"`value` ohms, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"function-coulomb","kind":"function","line":202,"name":"coulomb","qualifiedName":"Units.SI.coulomb","signatures":["coulomb(value)"],"summary":"`value` coulombs, as a `Measure`.","types":[],"urlPath":"units/si"},{"anchor":"make-siunit","kind":"make","line":204,"name":"SIUnit","qualifiedName":"SIUnit","signatures":["*(@Watt, @Hour)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-*","kind":"function","line":205,"name":"*","qualifiedName":"SIUnit.*","signatures":["*(@Watt, @Hour)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"function-to","kind":"function","line":220,"name":"to","qualifiedName":"Units.SI.to","signatures":["to(measure, String)"],"summary":"Renders a measure as its value followed by its unit symbol.","types":[],"urlPath":"units/si"},{"anchor":"function-mega","kind":"function","line":256,"name":"mega","qualifiedName":"Units.SI.mega","signatures":["mega(measure)"],"summary":"The same measure, displayed with the mega- prefix.","types":[],"urlPath":"units/si"},{"anchor":"function-giga","kind":"function","line":261,"name":"giga","qualifiedName":"Units.SI.giga","signatures":["giga(measure)"],"summary":"The same measure, displayed with the giga- prefix.","types":[],"urlPath":"units/si"},{"anchor":"function-milli","kind":"function","line":269,"name":"milli","qualifiedName":"Units.SI.milli","signatures":["milli(measure)"],"summary":"The same measure, displayed with the milli- prefix.","types":[],"urlPath":"units/si"},{"anchor":"function-micro","kind":"function","line":274,"name":"micro","qualifiedName":"Units.SI.micro","signatures":["micro(measure)"],"summary":"The same measure, displayed with the micro- prefix.","types":[],"urlPath":"units/si"},{"anchor":"function-nano","kind":"function","line":279,"name":"nano","qualifiedName":"Units.SI.nano","signatures":["nano(measure)"],"summary":"The same measure, displayed with the nano- prefix.","types":[],"urlPath":"units/si"},{"anchor":"function-per","kind":"function","line":294,"name":"per","qualifiedName":"Units.SI.per","signatures":["per(measure, other) : Measure -> Measure -> Measure"],"summary":"Divides one measure by another, naming the resulting dimension.\n\nThe spelled-out form of `/`: `100.meter.per(10.sec)` and `100.meter / 10.sec` are the same call. Metres over seconds is speed, energy over time is power, force over area is pressure: the dimension table decides, and the symbol follows it.","types":["Measure"],"urlPath":"units/si"},{"anchor":"function-times","kind":"function","line":309,"name":"times","qualifiedName":"Units.SI.times","signatures":["times(measure, other) : Measure -> Measure -> Measure"],"summary":"Multiplies one measure by another, naming the resulting dimension.\n\nThe spelled-out form of `*`. Force times distance is energy, voltage times current is power, power times time is energy.","types":["Measure"],"urlPath":"units/si"},{"anchor":"make-measure","kind":"make","line":314,"name":"Measure","qualifiedName":"Measure","signatures":[],"summary":"Prefixes work both on an existing measure (`5000.meter.kilo`) and at the beginning of a postfix unit expression (`3.kilo.watt`).","types":[],"urlPath":"units/si"},{"anchor":"make-integer","kind":"make","line":332,"name":"Integer","qualifiedName":"Integer","signatures":[],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"make-float","kind":"make","line":336,"name":"Float","qualifiedName":"Float","signatures":[],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"make-measure","kind":"make","line":340,"name":"Measure","qualifiedName":"Measure","signatures":["*(other)","/(other)","product(other)","quotient(other)","productKind(mass, acceleration)","quotientKind(length, time)","productSymbol(force, _, _)","quotientSymbol(speed, left, _)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-*","kind":"function","line":341,"name":"*","qualifiedName":"Measure.*","signatures":["*(other)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn--","kind":"function","line":342,"name":"/","qualifiedName":"Measure./","signatures":["/(other)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-product","kind":"function","line":345,"name":"product","qualifiedName":"Measure.product","signatures":["product(other)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-quotient","kind":"function","line":353,"name":"quotient","qualifiedName":"Measure.quotient","signatures":["quotient(other)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-productkind","kind":"function","line":361,"name":"productKind","qualifiedName":"Measure.productKind","signatures":["productKind(mass, acceleration)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-quotientkind","kind":"function","line":369,"name":"quotientKind","qualifiedName":"Measure.quotientKind","signatures":["quotientKind(length, time)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-productsymbol","kind":"function","line":376,"name":"productSymbol","qualifiedName":"Measure.productSymbol","signatures":["productSymbol(force, _, _)"],"summary":"","types":[],"urlPath":"units/si"},{"anchor":"fn-quotientsymbol","kind":"function","line":383,"name":"quotientSymbol","qualifiedName":"Measure.quotientSymbol","signatures":["quotientSymbol(speed, left, _)"],"summary":"","types":[],"urlPath":"units/si"}],"generatedAt":"2026-08-31T00:21:50.485524575Z","package":"prelude","version":"0.4.0-alpha"}