diff --git a/content/hoon/rune/ket.md b/content/hoon/rune/ket.md index 18346b7d..7e779314 100644 --- a/content/hoon/rune/ket.md +++ b/content/hoon/rune/ket.md @@ -15,10 +15,84 @@ layout: # ^ ket ยท Casts -[`^-` ("kethep")](#--kethep), [`^+` ("ketlus")](#-ketlus), and [`^=` ("kettis")](#-kettis) let us adjust types without violating type constraints. +[`^-` ("kethep")](#kethep), [`^+` ("ketlus")](#ketlus), and [`^=` ("kettis")](#kettis) let us adjust types without violating type constraints. The `+nest` algorithm which tests subtyping is conservative; it never allows invalid nests, it sometimes rejects valid nests. +## ^_ "ketcab" {#ketcab} + +Assert that an expression nests in a type, without casting it. + +#### Syntax + +Two arguments, fixed. + +{% tabs %} + +{% tab title="Tall form" %} + +```hoon +^_ p +q +``` + +{% endtab %} + +{% tab title="Wide form" %} + +```hoon +^_(p q) +``` + +{% endtab %} + +{% tab title="Irregular form" %} + +None + +{% endtab %} + +{% endtabs %} + +#### AST + +```hoon +[%ktcb p=hoon q=hoon] +``` + +#### Produces + +The product of `.q`, with **`.q`'s own type** โ€” not `.p`'s. + +#### Discussion + +`^_` compiles `.q` using `.p`'s type as the goal, so `.q` must nest in `.p`'s +type or the expression fails to compile. Unlike [`^-`](#kethep), however, the +result keeps the type inferred for `.q`. It is a type *test* rather than a cast. + +The difference is visible in the dojo: + +``` +> ? ^_(*@ 'a') + @t +'a' + +> ? ^-(@ 'a') + @ +97 +``` + +Both accept `'a'`, because `@t` nests in `@`. `^-` casts the result to `@`, so it +prints as `97`; `^_` leaves it as `@t`, so it prints as `'a'`. + +`^_` is what [`=^`](tis.md#tisket) desugars through, which is why that rune +requires its right-hand expression to produce a cell. + +Note this rune was spelled `^#` until June 2026; on a kernel from before that +change, use `^#` instead. + +--- + ## ^| "ketbar" {#ketbar} Convert a gold core to an iron core (contravariant). diff --git a/content/hoon/rune/tis.md b/content/hoon/rune/tis.md index 0e36fc84..eedd8efc 100644 --- a/content/hoon/rune/tis.md +++ b/content/hoon/rune/tis.md @@ -513,6 +513,20 @@ This may also remind you of Haskell's State monad. Note that `=^` is subject to the same type nesting limitations as `=.`; e.g., if you have `?~` checked a list for null, you can no longer nest a regular list in the result. (In this case, use a nock `=(~ ...)` equality check instead or recast the result.) +`.r` **must produce a cell.** Since 2026, `=^` desugars through +[`^_`](ket.md#ketcab) with a cell as the goal, so a non-cell right-hand side is a +compile error rather than something that slides through: + +``` +> =| a=@ =^ b a [1 2] [b a] +[1 2] + +> =| a=@ =^ b a 5 [b a] +-need.[* *] +-have.@ud +nest-fail +``` + #### Examples The `+og` core is a stateful pseudo-random number generator. We have to change the core state every time we generate a random number, so we use `=^`: diff --git a/content/hoon/stdlib/2c.md b/content/hoon/stdlib/2c.md index 345305a7..3fe341b8 100644 --- a/content/hoon/stdlib/2c.md +++ b/content/hoon/stdlib/2c.md @@ -213,6 +213,75 @@ An `$atom`. *** +## `+clz` {#clz} + +Leading zeros. + +Counts the leading zero bits of `.b` within the block width given by `.a`. + +#### Accepts + +`.a` is a [`$bite`](1c.md#bite). + +`.b` is an `$atom`. + +#### Produces + +An `$atom`. + +#### Source + +```hoon +++ clz + ~/ %clz + |= [a=bite b=@] + =/ c=[=bloq =step] ?^(a a [a 1]) + (sub (mul (bex bloq.c) step.c) (met 0 (end a b))) +``` + +#### Examples + +``` +> (clz 3 8) +4 +``` + +--- + +## `+ctz` {#ctz} + +Trailing zeros. + +Counts the trailing zero bits of `.a`. Produces `0` for an input of `0`. + +#### Accepts + +`.a` is an `$atom`. + +#### Produces + +An `$atom`. + +#### Source + +```hoon +++ ctz + ~/ %ctz + |= a=@ + ?: =(0 a) 0 + =| i=@ud + |-(?:(=(1 (cut 0 [i 1] a)) i $(i +(i)))) +``` + +#### Examples + +``` +> (ctz 8) +3 +``` + +--- + ## `+cut` {#cut} Slice. @@ -438,6 +507,99 @@ An `$atom`. *** +## `+ham` {#ham} + +Population count. + +Counts the set bits of `.a` โ€” the Hamming weight, or popcount. + +#### Accepts + +`.a` is an `$atom`. + +#### Produces + +An `$atom`. + +#### Source + +```hoon +++ ham + ~/ %ham + |= a=@ + ?: =(0 a) 0 + =| n=@ud + =/ m (dec (met 0 a)) + |- ^- @ud + =? n =(1 (cut 0 [m 1] a)) + +(n) + ?:(=(0 m) n $(m (dec m))) +``` + +#### Examples + +``` +> (ham 255) +8 +``` + +--- + +## `+hew` {#hew} + +Cut many. + +Cuts several values out of an atom in sequence, shaped by the argument you apply +the resulting gate to, and reports where it left off. + +#### Accepts + +`.a` is a [`$bite`](1c.md#bite), the block size and starting offset. + +`.c` is an `$atom`, the source. + +The product is a wet gate; apply it to a *shape* describing how many blocks each +field should take. + +#### Produces + +A cell of the filled-in shape and the new `[bloq step]` position. + +#### Source + +```hoon +++ hew + ~/ %hew + |= [a=bite c=@] + =/ d=[=bloq =step] ?^(a a [a 0]) + ~% %fun +>+ ~ + |* b=* + ^+ [b d] + ?@ b + [(cut bloq.d [step.d b] c) bloq.d (add step.d b)] + =^ f d $(b -.b) + =^ g d $(b +.b) + [[f g] d] +``` + +#### Examples + +Cutting two blocks as one value: + +``` +> ((hew [3 0] 0xdead.beef) 2) +[48.879 bloq=3 step=2] +``` + +Cutting two single blocks into a cell: + +``` +> ((hew [3 0] 0xdead.beef) [1 1]) +[[239 190] bloq=3 step=2] +``` + +--- + ## `+lsh` {#lsh} Left-shift. @@ -797,6 +959,48 @@ An `$atom`. *** +## `+rig` {#rig} + +Convert block sizes. + +Re-expresses the step of [`$bite`](1c.md#bite) `.bite` in terms of block size `.b`. + +#### Accepts + +`.bite` is a [`$bite`](1c.md#bite). + +`.b` is a [`$bloq`](1c.md#bloq). + +#### Produces + +A `+step`. + +#### Source + +```hoon +++ rig + ~/ %rig + |= [=bite b=bloq] + ^- step + ?@ bite 0 + =/ [a=bloq c=step] bite + ?: =(a b) c + ?: (gth a b) + (lsh [0 (sub a b)] c) + =/ d [0 (sub b a)] + =/ e (rsh d c) + ?:(=(0 (end d c)) e +(e)) +``` + +#### Examples + +``` +> (rig [3 2] 4) +1 +``` + +--- + ## `+rip` {#rip} Disassemble. diff --git a/content/hoon/stdlib/4k.md b/content/hoon/stdlib/4k.md index 0e462833..eec4ac2f 100644 --- a/content/hoon/stdlib/4k.md +++ b/content/hoon/stdlib/4k.md @@ -126,6 +126,20 @@ Render as `$tape`. Renders a `$coin` `.lot` as a `$tape`. +> **Date rendering changed in 2026 (UIP-135).** The month, day, hour, minute and +> second of a `@da`/`@d` are now zero-padded to two digits with `+y-co`, where +> they were previously rendered unpadded with `+a-co`. This changes the *textual* +> output of anything that renders a date, including `(scot %da now)`: +> +> ``` +> > (scot %da ~2024.1.2..03.04.05..0006) +> ~.~2024.01.02..03.04.05..0006 +> ``` +> +> Parsing accepts both forms, so round-tripping is unaffected โ€” but code that +> string-compares rendered dates, or parses them with a strict pattern, may need +> updating. + #### Accepts `.lot` is a `$coin`, and is the sample of `+co`. @@ -161,8 +175,8 @@ A `$tape`. =. rep ['.' (y-co s.t.yod)] =. rep ['.' (y-co m.t.yod)] ['.' '.' (y-co h.t.yod)] - =. rep ['.' (a-co d.t.yod)] - =. rep ['.' (a-co m.yod)] + =. rep ['.' (y-co d.t.yod)] + =. rep ['.' (y-co m.yod)] =? rep !a.yod ['-' rep] ['~' (a-co y.yod)] :: diff --git a/content/hoon/stdlib/4n.md b/content/hoon/stdlib/4n.md index 9a494ef6..7fab586b 100644 --- a/content/hoon/stdlib/4n.md +++ b/content/hoon/stdlib/4n.md @@ -470,6 +470,56 @@ A `$toon`. --- +## `+vi` {#vi} + +Virtualization door. + +Container door for the virtualization arms [`+mole`](#mole), [`+mule`](#mule), +[`+mure`](#mure) and [`+mute`](#mute). Its sample `.for` controls whether a +scry (Nock 12) performed inside the virtualized computation is forwarded to the +real namespace. + +#### Accepts + +`.for` is a `?`. + +#### Source + +```hoon +++ vi + ~% %vi ..vi + == + %mure mure + %mute mute + == + :: forward namespace? + :: + |_ for=? +``` + +#### Discussion + +When `.for` is true, the arms build a scry handler and pass it to +[`+mink`](#mink)/[`+mock`](#mock); when it is false they pass `~`, the "no scry +handler" sentinel, so any Nock 12 inside blocks and the computation reports +failure. + +**The default forwards.** `.for` is a `?`, whose bunt is `%.y`, so `mure:vi` and +the bare aliases forward scries. Pass `|` explicitly to prevent that: + +``` +> (mure:vi |.(.^(? %cx /=//=/esse/base))) +[~ 0] + +> (~(mure vi |) |.(.^(? %cx /=//=/esse/base))) +~ +``` + +Only `+mole` and `+mule` have top-level aliases (`++ mole mole:vi`, +`++ mule mule:vi`). `+mure` and `+mute` must be reached through the door. + +--- + ## `+mole` {#mole} Typed unitary virtual. @@ -617,12 +667,16 @@ An `(each * (list tank))` - `%.y` indicates success and `%.n` indicates a crash. --- -## `+mure` {#mure} +## `+mure:vi` {#mure} Untyped unitary virtual. Kicks a `+trap`, producing its results in a `+unit` which is null if it crashed. The result is just a `$noun`, no type information is preserved. +> **Note.** `+mure` is no longer a top-level arm. It lives in the +> [`+vi`](#vi) door, and a bare `(mure ...)` fails with `-find.mure`. Use +> `mure:vi`, or `~(mure vi for)` to control namespace forwarding. + #### Accepts A `+trap`. @@ -637,7 +691,12 @@ A `(unit *)`. ++ mure |= tap=(trap) ^- (unit) - =/ ton (mink [tap %9 2 %0 1] |=((pair) ``.*(~ [%12 1+p 1+q]))) + =/ gul + ?. for ~ + => ~ + |=(a=^ ``.*(a 12+[0+2 0+3])) + :: + =/ ton (mink [tap %9 2 %0 1] gul) ?.(?=(%0 -.ton) ~ `product.ton) ``` @@ -665,12 +724,16 @@ A `(unit *)`. --- -## `+mute` {#mute} +## `+mute:vi` {#mute} Untyped virtual. Kicks a `+trap`, producing its result as a `$noun` or the tanks of any error that occurs. Similar to [`+mule`](#mule), but preserves no type information. +> **Note.** `+mute` is no longer a top-level arm. It lives in the +> [`+vi`](#vi) door, and a bare `(mute ...)` fails with `-find.mute`. Use +> `mute:vi`, or `~(mute vi for)` to control namespace forwarding. + #### Accepts `+tap` is a `+trap`. @@ -685,7 +748,12 @@ A `(each * (list tank))`, where `%.y` indicates success and `%.n` indicates fail ++ mute |= tap=(trap) ^- (each * (list tank)) - =/ ton (mock [tap %9 2 %0 1] |=((pair) ``.*(~ [%12 1+p 1+q]))) + =/ gul + ?. for ~ + => ~ + |=(a=^ ``.*(a 12+[0+2 0+3])) + :: + =/ ton (mock [tap %9 2 %0 1] gul) ?- -.ton %0 [%& p.ton] :: diff --git a/content/hoon/stdlib/5c.md b/content/hoon/stdlib/5c.md index b9906da4..f1cc794c 100644 --- a/content/hoon/stdlib/5c.md +++ b/content/hoon/stdlib/5c.md @@ -458,6 +458,56 @@ A `$vase`. --- +## `+slub` {#slub} + +Memoized [`+slap`](#slap). + +Compiles hoon `.gen` with subject `.vax`, producing a `$vase` of the result, and +caches the compilation. + +#### Accepts + +`.vax` is a `$noun` in a `$vase`, and is the subject. + +`.gen` is some `$hoon`. + +#### Produces + +A `$vase`. + +#### Source + +```hoon +++ slub + |= [vax=vase gen=hoon] ^- vase + =/ gun + => [vax=p=p.vax gen=gen ut=ut] + ~> %memo./hoon/mint + (~(mint ut p.vax) %noun gen) + [p.gun .*(q.vax q.gun)] +``` + +#### Examples + +``` +> (slub !>(b='foo') (ream '|=(a=@t [a b])')) +[#t/<1.jtd [a=@t b=@t]> q=[[[0 6] 0 7] 0 7.303.014]] +``` + +#### Discussion + +`+slub` produces the same result as [`+slap`](#slap), but wraps the `+mint` call +in a `~> %memo./hoon/mint` hint. The runtime therefore caches the compilation +keyed on `/hoon/mint`, so repeating the same compile is cheap. + +Note that this cache is a side effect with process lifetime: it is held by the +runtime, not by the caller, and is not reflected in the product. `+slub` is worth +preferring over `+slap` where the same expression is compiled repeatedly in a hot +path. The cache can be dropped with the `%drop` runtime hint or by a `|meld` +with `=ford &`. + +--- + ## `+slog` {#slog} Deify printf.