diff --git a/content/blog/literals.md b/content/blog/literals.md index 5bcf928..29f5b11 100644 --- a/content/blog/literals.md +++ b/content/blog/literals.md @@ -165,7 +165,7 @@ Since we're being quite thorough in this article, let's summarize every single a | `@uw` | unsigned base64 | `0w_____._____` | `0wbnC.8haTg` | | | `@ux` | unsigned hexadecimal | `0x____.____` | `0x5f5.e138` | | -You'll also find some irregular auras in use: `%lull`, for instance, has a `@uxblob` type. Nonstandard auras (i.e. those not listed in the table above) render as `@ux` visibly, but are still subject to nesting rules. In fact, the capital-letter suffixes one occasionally encounters (like `@tD` and `@uvJ`) are programmer annotations to mark the intended bit-width of a value. (`A` = {% math %}2^0{% /math %}, `B` = {% math %}2^1%{% /math %}, `C` = {% math %}2^2{% /math %}, `D` = {% math %}2^3{% /math %}, `E` = {% math %}2^5{% /math %}, etc.) +You'll also find some irregular auras in use: `%lull`, for instance, has a `@uxblob` type. Nonstandard auras (i.e. those not listed in the table above) render as `@ux` visibly, but are still subject to nesting rules. In fact, the capital-letter suffixes one occasionally encounters (like `@tD` and `@uvJ`) are programmer annotations to mark the intended bit-width of a value. (`A` = {% math %}2^0{% /math %}, `B` = {% math %}2^1%{% /math %}, `C` = {% math %}2^2{% /math %}, `D` = {% math %}2^3{% /math %}, `E` = {% math %}2^4{% /math %}, etc.) We also include two other literal syntaxes which don't resolve to atoms: diff --git a/content/blog/nested-core-pattern.md b/content/blog/nested-core-pattern.md index 745850a..7cc60e5 100644 --- a/content/blog/nested-core-pattern.md +++ b/content/blog/nested-core-pattern.md @@ -74,7 +74,7 @@ For instance, in `/sys/vane/ames.hoon`, we find the definitions Thus for Ames, the nested core pattern largely consists of building a list of particular moves and then pulling them back out: ```hoon - :: if processing succeded, send positive ack packet and exit + :: if processing succeeded, send positive ack packet and exit ++ send-ack |= =bone ^+ event-core @@ -121,7 +121,7 @@ Gall uses two nested cores to manage agents: `++mo` handles Arvo-level moves, w ```hoon :: ++  mo-core  . -::  +mo-abed: initialise state with the provided duct +::  +mo-abed: initialize state with the provided duct ++  mo-abed  |=(hun=duct mo-core(hen hun)) ::  +mo-abet: finalize, reversing moves ++  mo-abet  [(flop moves) gall-payload] diff --git a/content/courses/asl.md b/content/courses/asl.md index a96bf7f..fa59396 100644 --- a/content/courses/asl.md +++ b/content/courses/asl.md @@ -4,29 +4,25 @@ date = "2023-05-01" weight = 90 next_cohort = "August 2023" image = "https://storage.googleapis.com/media.urbit.org/developers/images/app-school-live.svg" -description = "App School focuses on how to build a backend Gall agent, then on connecting it to a React-based front-end. When you're done, you'll be able to produce and distribute your own Urbit apps." +description = " App School teaches you all about Urbit-based backend design using Gall agents, as well as interfacing with a React-based front-end. When you're done, you'll be able to produce and distribute your own Urbit apps." +++ -The Urbit Foundation offers cohort classes to cover the [App -School](/guides/core/app-school) curriculum jointly. +The Urbit Foundation offers live, cohort-based classes to cover the [App +School](/guides/core/app-school) curriculum. -App School focuses on how to build a backend Gall agent, then on connecting it -to a React-based front-end. When you're done, you'll be able to produce and -distribute your own Urbit apps. +Every userspace Urbit application is a **Gall agent**. App School teaches you all about Urbit-based backend design using Gall agents, as well as interfacing with a React-based front-end. When you're done, you'll be able to produce and distribute your own Urbit apps. -If you prefer to learn as part of a group with a hands-on instructor, regular +If you enjoy learning as part of a group with a hands-on instructor, regular exercises and discussions, and a completion certification, then App School Live will be a good fit for you. -By completing App School as a cohort student, you should be able to design and -implement basic user apps, produce minimal working example applications, and -contribute at the level of basic Urbit userspace bounties. +By completing App School, you will have the basic skillset to design and implement apps, pick up Urbit userspace bounties, and participate in Hackathons. The next cohort for App School Live will begin the week of August 21st, 2023. -- [Complete this form](https://forms.gle/3c8xBubvSiQfj7Tr6) to sign up for the upcomming class. +- [Complete this form](https://airtable.com/shrdZcSQrIIb6mAmx) to sign up for the upcoming class. -Until then, feel free to work through the [App School +Until then, feel free to go through the [App School docs](/guides/core/app-school). diff --git a/content/courses/csl.md b/content/courses/csl.md index 9f683c6..8ebadcf 100644 --- a/content/courses/csl.md +++ b/content/courses/csl.md @@ -1,27 +1,24 @@ +++ -title = "Core School Live" +title = "Core Academy" weight = 10 -next_cohort = "Late Summer 2023" +next_cohort = "August 28th" image = "https://storage.googleapis.com/media.urbit.org/developers/images/core-school-live.svg" -description = "Core School prepares experienced Hoon developers to work on the Arvo kernel, the Vere or New Mars runtime, and otherwise build the platform as a senior developer." +description = "Core Academy prepares experienced Hoon developers to work on the Arvo kernel, the Vere or New Mars runtime, and otherwise build the platform as a senior developer." +++ The Urbit Foundation will offer a cohort classes to train new core developers. Most core developers have been trained up on-the-job inside of Tlon, but as core development expands, other organizations will contribute. -Core School prepares experienced Hoon developers to work on the Arvo kernel, +Core Academy prepares experienced Hoon developers to work on the Arvo kernel, the Vere or New Mars runtime, and otherwise build the platform as a senior developer. -By completing Core School as a cohort student, you should be able to compose a +By completing Core Academy as a cohort student, you should be able to compose a new vane, contribute to Urbit as a core platform and infrastructure developer, and guide in-depth projects with minimal oversight (as distinct from collaboration). -The first cohort for Core School Live will take place in late summer 2023. - -- [Complete this form](https://forms.gle/gdDWFLiDV1Te65nH8) to get on our - mailing list about the next opportunity. +- [Complete this form](https://airtable.com/shr6V87yXZxJEZqyu) to sign up for Core Academy before August 6th. Until then, feel free to work through the [Arvo docs](/reference/arvo). diff --git a/content/courses/hsl.md b/content/courses/hsl.md index ee90ef9..429e0c9 100644 --- a/content/courses/hsl.md +++ b/content/courses/hsl.md @@ -1,7 +1,7 @@ +++ title = "Hoon School Live" weight = 100 -next_cohort = "March 29, 2023" +next_cohort = "September 2023" image = "https://storage.googleapis.com/media.urbit.org/developers/images/hoon-school-live.svg" description = "Hoon School Live teaches the fundamentals of Hoon with a hands-on instructor, regular exercises and discussions, and a completion certification." +++ @@ -19,13 +19,11 @@ explain subject-oriented programming. We omit some of the written Hoon School content for time, but you should be well-equipped to understand the optional lessons. -The next cohort will begin on March 29, 2023. The cohort after that is -scheduled to begin in June 2023. +The cohort will start in September until October on Thursdays at 1pm EST | 6pm CEST. -- [Complete this form](https://forms.gle/kgiPckuHaVtJng9r5) to sign up for the next Hoon School Live. +- [Complete this form](https://airtable.com/shrqzaLRLzDsHDnCX) to sign up for the next Hoon School Live. -Until then, feel free to work through the [Hoon School -docs](/guides/core/hoon-school). +Until then, feel free to work through the [Hoon School docs](/guides/core/hoon-school). ### What will you learn? diff --git a/content/guides/additional/app-workbook/_index.md b/content/guides/additional/app-workbook/_index.md index edf4bf0..2c643f5 100644 --- a/content/guides/additional/app-workbook/_index.md +++ b/content/guides/additional/app-workbook/_index.md @@ -5,11 +5,14 @@ weight = 3 insert_anchor_links = "right" +++ -## [`%ahoy`](/guides/additional/app-workbook/ahoy) +## [Building a CLI App with `%rpn`](/guides/additional/app-workbook/rpn) -## [`%dbug`](/guides/additional/app-workbook/dbug) +## [Debugging Wrapper with `%dbug`](/guides/additional/app-workbook/dbug) -## [`%flap`](/guides/additional/app-workbook/flap) +## [Hosting a Website with `%feature`](/guides/additional/app-workbook/feature) -## [`%feature`](/guides/additional/app-workbook/feature) +## [Serving a JS Game with `%flap`](/guides/additional/app-workbook/flap) +## [Ship Monitoring with `%ahoy`](/guides/additional/app-workbook/ahoy) + +## [Styled Text with `%track7`](/guides/additional/app-workbook/track7) diff --git a/content/guides/additional/app-workbook/ahoy.md b/content/guides/additional/app-workbook/ahoy.md index 5d625be..6230a3d 100644 --- a/content/guides/additional/app-workbook/ahoy.md +++ b/content/guides/additional/app-workbook/ahoy.md @@ -1,10 +1,8 @@ +++ -title = "%ahoy Ship Monitoring" -weight = 10 +title = "Ship Monitoring" +weight = 194 +++ -# `%ahoy` Ship Monitoring - The `%ahoy` desk by [~midden-fabler](https://urbit.org/ids/~midden-fabler) provides a number of agents to automatically monitor ship activity such as breaching and network uptime. This tutorial examines the `%ahoy` agent specifically with some slight simplifications to demonstrate how an Urbit-native app can be constructed. You will see how to render a front-end using Sail, employ the `++abet` nested core design pattern, construct CLI generators, and set wakeup timers using [Behn](https://developers.urbit.org/reference/glossary/behn). `%ahoy` presents a web UI at `/ahoy` rendered using [Sail](https://developers.urbit.org/guides/additional/sail) and [~paldev](https://urbit.org/ids/~paldev)'s Rudder library alongside command-line generators to add, delete, and modify ship watches. Notifications are sent using `%hark-store` if a ship hasn't been contacted after a specified amount of time. diff --git a/content/guides/additional/app-workbook/dbug.md b/content/guides/additional/app-workbook/dbug.md index 5942cfd..57877b9 100644 --- a/content/guides/additional/app-workbook/dbug.md +++ b/content/guides/additional/app-workbook/dbug.md @@ -1,6 +1,6 @@ +++ -title = "%dbug Debugging Wrapper" -weight = 40 +title = "Debugging Wrapper" +weight = 42 +++ diff --git a/content/guides/additional/app-workbook/feature.md b/content/guides/additional/app-workbook/feature.md index c788c27..979986b 100644 --- a/content/guides/additional/app-workbook/feature.md +++ b/content/guides/additional/app-workbook/feature.md @@ -1,10 +1,8 @@ +++ -title = "%feature Page Hosting" -weight = 160 +title = "Host a Website" +weight = 85 +++ -# `%feature` Page Hosting - [`%feature`](https://github.com/hanfel-dovned/Feature) by [~hanfel-dovned](https://urbit.org/ids/~hanfel-dovned) hosts a simple HTML page from an Urbit ship at an associated URL. This tutorial examines how it uses the middleware [`%schooner`](https://github.com/dalten-collective/schooner/) library by Quartus to return a web page when contacted by a web browser. You will learn how a basic site hosting app can handle HTTP requests and render a page using an `%html` mark. `%feature` presents a web page from `/app/feature-ui` at `/apps/feature/feature-ui`. These paths are both configurable by the developer. @@ -100,7 +98,7 @@ The system only handles pokes: there are no subscriptions or Arvo calls except [302 ~ [%login-redirect './apps/feature']] ?~ body.request.inbound-request [(send [405 ~ [%stock ~]]) state] - =/ json (de-json:html q.u.body.request.inbound-request) + =/ json (de:json:html q.u.body.request.inbound-request) =/ action (dejs-action +.json) (handle-action action) :: @@ -185,7 +183,7 @@ The most interesting part of the whole app is the `++handle-http` arm: [302 ~ [%login-redirect './apps/feature']] ?~ body.request.inbound-request [(send [405 ~ [%stock ~]]) state] - =/ json (de-json:html q.u.body.request.inbound-request) + =/ json (de:json:html q.u.body.request.inbound-request) =/ action (dejs-action +.json) (handle-action action) :: @@ -219,7 +217,7 @@ The most interesting part of the whole app is the `++handle-http` arm: == ``` -This arm uses the `server` library and `schooner` to produce a response of a server state and associated data. HTTP requests to `/app/feature` are checked for login authentication, while `/app/feature/public` is not. +This arm uses the `server` library and `schooner` to produce a response of a server state and associated data. HTTP requests to `/apps/feature` are checked for login authentication, while `/apps/feature/public` are not. ### `POST` diff --git a/content/guides/additional/app-workbook/flap.md b/content/guides/additional/app-workbook/flap.md index 10b709d..c248ab2 100644 --- a/content/guides/additional/app-workbook/flap.md +++ b/content/guides/additional/app-workbook/flap.md @@ -1,12 +1,8 @@ +++ -title = "%flap JS Client" -weight = 60 +title = "Serving a JS Game" +weight = 193 +++ -# `%flap` JS Client - -## Introduction - In this tutorial, we will take an off-the-shelf JavaScript game which runs in the browser and connect it to an Urbit back-end. This page assumes that you have completed some version of Hoon School and App School, whether the [live courses](/courses) or the [written docs](/guides/core/hoon-school/A-intro). Our goal is to show you one way of directly serving client code from an Urbit ship as server. _Flappy Bird_ is an "insanely irritating, difficult and frustrating game which combines a super-steep difficulty curve with bad, boring graphics and jerky movement" ([Huffington Post](https://web.archive.org/web/20140205084251/http://www.huffingtonpost.com/2014/02/03/flappy-bird-tips_n_4717406.html)). We are going to implement `%flap`, a _Flappy Bird_ leaderboard using ~paldev’s `%pals` peer tracking agent. The approach given in this tutorial will apply to any game which is primarily run in the browser and has some persistent state to retain across sessions or communicate between players at discrete intervals. Direct player-v.-player games will require other techniques to implement. @@ -339,7 +335,7 @@ The main app implements the logic for exposing and tracking data. %'POST' ?~ body.request.inbound-request [(send [405 ~ [%stock ~]]) this] - =/ json (de-json:html q.u.body.request.inbound-request) + =/ json (de:json:html q.u.body.request.inbound-request) =/ axn `action`(dejs-action +.json) (on-poke %flap-action !>(axn)) :: @@ -501,7 +497,7 @@ Now when we navigate to `localhost:8080/apps/flap`, what do we see? The game ca ### Serving Correctly -If we investigate the Developer Tools console in our browser, we see messages to the effect that resources are unable to be located. Resource paths (for `js`, `png`, and `wav` files) tell the browser from whence the resources will come when they are loaded. We have two options here as well: hot-link the resource from its GitHub or other source, or +If we investigate the Developer Tools console in our browser, we see messages to the effect that resources are unable to be located. Resource paths (for `js`, `png`, and `wav` files) tell the browser from whence the resources will come when they are loaded. We have two options here as well: hot-link the resource from its GitHub or other source or serve the resource from Urbit. If we hot-link the resources, the corresponding lines will look like this: @@ -852,7 +848,7 @@ With all of the above, you should have a working `%flappy` instance at `http://l %'POST' ?~ body.request.inbound-request [(send [405 ~ [%stock ~]]) this] - =/ json (de-json:html q.u.body.request.inbound-request) + =/ json (de:json:html q.u.body.request.inbound-request) =/ axn `action`(dejs-action +.json) (on-poke %flap-action !>(axn)) :: @@ -1111,7 +1107,7 @@ If you examine `++on-poke` in `/app/flap.hoon`, you will see that HTTP `POST` re %'POST' ?~ body.request.inbound-request [(send [405 ~ [%stock ~]]) this] -=/ json (de-json:html q.u.body.request.inbound-request) +=/ json (de:json:html q.u.body.request.inbound-request) =/ axn `action`(dejs-action +.json) (on-poke %flap-action !>(axn)) ``` diff --git a/content/guides/additional/app-workbook/rpn.md b/content/guides/additional/app-workbook/rpn.md new file mode 100644 index 0000000..657e9ed --- /dev/null +++ b/content/guides/additional/app-workbook/rpn.md @@ -0,0 +1,189 @@ ++++ +title = "Building a CLI App" +weight = 28 ++++ + +We will utilize the basic calculator app logic from the [parsing guide](/guides/additional/parsing#recursive-parsers) to produce a linked calculator agent `%rpn` supporting the following operators by the appropriate parsers: + +- numbers (as `@rs` without `.` dot prefix) (`royl-rs:so`) +- `+` lus, addition (`lus`) +- `-` hep, subtraction (`hep`) +- `*` tar, multiplication (`tar`) +- `/` fas, division (`fas`) +- `.` dot, display top of stack (`dot`) + +We will leave all regular Gall arms as their defaults, but of course poking, subscribing, and peeking should be supported in a full application. + + +## Agent Logic + +**`/sur/rpn.hoon`** + +We just need to define the expected operators that will show up in the stack. These are `@t` text constants. + +```hoon +|% ++$ op $? [%op %add] + [%op %sub] + [%op %mul] + [%op %div] + [%op %sho] + == ++$ num @rs ++$ command ?(@rs op) +-- +``` + +(`+$command` doesn't really feel like the right name here, but we're pattern-matching with the demo `/app/shoe.hoon`.) + +**`/lib/rpn.hoon`** + +These are the parsing rules that the CLI agent will use. We could include these directly in the agent file but we'll post them to a library file. + +```hoon +|% +++ num royl-rs:so +++ op-add (cook |=(p=@ ?:(=('+' p) op+%add ~)) lus) +++ op-sub (cook |=(p=@ ?:(=('-' p) op+%sub ~)) hep) +++ op-mul (cook |=(p=@ ?:(=('*' p) op+%mul ~)) tar) +++ op-div (cook |=(p=@ ?:(=('/' p) op+%div ~)) fas) +++ op-sho (cook |=(p=@ ?:(=('.' p) op+%sho ~)) dot) +++ ops ;~(pose op-add op-sub op-mul op-div op-sho) +-- +``` + +**`/app/rpn.hoon`** + +```hoon +++ state-0 + $: %0 + stack=(list ?(@rs op:rpn)) + == +``` + +**`++command-parser`** + +We want this arm to wait until `RETURN` is pressed so we `++stag` the value with `|` `FALSE`/`%.n`. + +```hoon +++ command-parser + |= =sole-id:shoe + ^+ |~(nail *(like [? command:rpn])) + %+ stag | + (cook command:rpn ;~(pose num:rpnlib ops:rpnlib)) +``` + +**`++on-command`** + +This arm pushes values onto the stack, displays the stack, then checks to parse for the result of an operation. + +```hoon +++ on-command + |= [=sole-id:shoe =command:rpn] + ^- (quip card _this) + =/ old-stack (weld stack ~[command]) + =/ new-stack (process:rpnlib old-stack) + :_ this(stack new-stack) + :~ [%shoe ~ sole+klr+~[(crip "{} →")]] + [%shoe ~ sole+klr+~[[[`%br ~ `%g] (crip "{}") ~]]] + == +``` + +For this we add a helper arm to `/lib/rpn.hoon` which takes each entry, makes sure it is a `@rs` atom, and carries out the operation. (This could probably be made more efficient.) + +**`/lib/rpn.hoon`** + +```hoon +/- rpn +:: * * * +++ process + |= stack=(list command:rpn) + ^- (list command:rpn) + ~| "Failure processing operation on stack {}" + ?~ stack !! + ?- `command:rpn`(snag 0 (flop stack)) + [%op %add] + =/ augend ;;(@rs `command:rpn`(snag 1 (flop stack))) + =/ addend ;;(@rs `command:rpn`(snag 2 (flop stack))) + (flop (weld ~[(add:rs augend addend)] (slag 3 (flop stack)))) + :: + [%op %sub] + =/ minuend ;;(@rs `command:rpn`(snag 1 (flop stack))) + =/ subtrahend ;;(@rs `command:rpn`(snag 2 (flop stack))) + (flop (weld ~[(sub:rs minuend subtrahend)] (slag 3 (flop stack)))) + :: + [%op %mul] + =/ multiplicand ;;(@rs `command:rpn`(snag 1 (flop stack))) + =/ multiplier ;;(@rs `command:rpn`(snag 2 (flop stack))) + (flop (weld ~[(mul:rs multiplicand multiplier)] (slag 3 (flop stack)))) + :: + [%op %div] + =/ numerator ;;(@rs `command:rpn`(snag 1 (flop stack))) + =/ denominator ;;(@rs `command:rpn`(snag 2 (flop stack))) + (flop (weld ~[(div:rs numerator denominator)] (slag 3 (flop stack)))) + :: + [%op %sho] + ~& > "{<(snag 1 (flop stack))>}" + (flop (slag 1 (flop stack))) + :: + @rs + stack + == +``` + +### Linking + +After a `%sole` agent has been `|install`ed, it should be registered for Dojo to cycle input to it using `|link`. + +```hoon +|link %rpn +``` + +Now `Ctrl`+`X` allows you to switch to that app and evaluate expressions using it. + +```hoon +gall: booted %rpn +> 50 +~ → +~[.50] + +> 25 +~[.50] → +~[.50 .25] + +> - +~[.50 .25] → +~[.-25] + +> 5 +~[.-25] → +~[.-25 .5] + +> / +~[.-25 .5] → +~[.-0.19999999] + +> 5 +~[.-0.19999999] → +~[.-0.19999999 .5] + +> * +~[.-0.19999999 .5] → +~[.-0.99999994] + +> 1 +~[.-0.99999994] → +~[.-0.99999994 .1] + +> / +~[.-0.99999994 .1] → +~[.-1] +``` + + +## Exercises + +- Extend the calculator app to support modulus as `%` cen. +- Extend the calculator app so it instead operates on `@rd` values. Either use `++cook` to automatically convert the input values from a `1.23`-style input to the `.~1.23` `@rd` style or build a different input parser from the entries in `++royl:so`. +- Extend the calculator app so that it can support named variables (using `@tas`) with `=` tis. What new data structure do you need? For convenience, expose the result of the last operation as `ans` (a feature of TI graphing calculators and MATLAB, among other programs). +- The calculator app stack isn't really a proper CS stack with push and pop operations. Refactor it to use such a type. diff --git a/content/guides/additional/app-workbook/track7.md b/content/guides/additional/app-workbook/track7.md new file mode 100644 index 0000000..17fdee7 --- /dev/null +++ b/content/guides/additional/app-workbook/track7.md @@ -0,0 +1,174 @@ ++++ +title = "Styled Text" +weight = 199 ++++ + +In this tutorial, we examine how to produce `styx` styled text strings and output them to the terminal from an agent. + +## `%shoe` CLI Session Manager + +`%shoe` is responsible to manage attached agent sessions. It adds a few arms to the standard Gall agent, namely: + +- `++command-parser` is the input parser, similar to the work we were carrying out just above. This parses every input and only permits valid keystrokes (think of Dojo real-time parsing). +- `++tab-list` provides autocompletion options. We can ignore for now. +- `++on-command` is called whenever a valid command is run. This produces the actual effects. +- `++can-connect` supports `|link` connexion to the app. +- `++on-connect` provides particular session support when a user connects. We can ignore for now. +- `++on-disconnect` provides particular session support when a user disconnects. We can ignore for now. + +To get started with text parsers and CLI agents, we need to focus on `++command-parser` and `++on-command`. But first, the agent's structure and state: + + +The agent will adopt a two-stage process, wherein a value is put on the stack then the stack is checked for any valid operations. + +### `++command-parser` + +The input parser can simply accept whole words or single inputs, or parse complex expressions (as Dojo does with Hoon). + +This results in a noun of `+$command-type` based on the specific application. The example `/app/shoe.hoon` agent defines: + +```hoon ++$ command + $? %demo + %row + %table + == +``` + +and later uses this as: + +```hoon + ++ command-parser + |= =sole-id:shoe + ^+ |~(nail *(like [? command])) + %+ stag & + (perk %demo %row %table ~) +``` + +where the unfamiliar parser components are: + +- `++stag` adds a label, here `&` pam `TRUE`/`%.y` to indicate that the command should be run immediately when it matches. (We won't want this below so we will `++stag` a `|` `FALSE`/`%.n`.) +- `++perk` parses a fork in the type. + +### `++on-command` + +This arm accepts a session ID and a command resulting from `++command-parser`. It produces a regular `(quip card _this)` so you can modify agent state and produce effects here. + + +## `%sole` Effects + +`%sole` is responsible for producing effects. If you want to yield effects to the command line from your CLI agent (which you often do), this is a great place to work. + +`%sole-effect`s are head-tagged by time and produce a variety of terminal effects, from text to bells, colors, and other screen effects. + + +## `$styx` Styled Text String + +A `klr` effect uses a `styx`, or styled text string. The relevant data structures are in `/sys/lull.hoon`: + +```hoon ++$ deco ?(~ %bl %br %un) :: text decoration ++$ stye (pair (set deco) (pair tint tint)) :: decos/bg/fg ++$ styl %+ pair (unit deco) :: cascading style + (pair (unit tint) (unit tint)) :: ++$ styx (list $@(@t (pair styl styx))) :: styled text ++$ tint $@ ?(%r %g %b %c %m %y %k %w %~) :: text color + [r=@uxD g=@uxD b=@uxD] :: 24bit true color +``` + +- `$deco` is a text decoration, here `%bl` blinking, `%br` bright (bold), and `%un` underlined. +- `$tint` is a color, either explicitly the terminal red/green/blue/cyan etc. or a 24-bit true color value. +- `$stye` composes these into a style which will be applied to a string. +- `$styl` similarly composes styles together. +- `$styx` pairs styles with cords. + +This means that composing styled text correctly can require explicitly nesting statements in rather a complicated way. + +For instance, to produce a bold string with hex color `#123456`, we could produce the `sole-effect`: + +```hoon +^- sole-effect:sole +:- klr +^- styx +~[[[`%br ~ `[r=0x12 g=0x34 b=0x56]] 'Hello Mars!' ~]] +``` + +- [~ropdeb-sormyr, "Styled output - requirements and progress" ~2016.8.2 Urbit fora post](https://github.com/urbit/fora-posts/blob/0238536650dfc284f14295d350f9acada0341480/archive/posts/~2016.8.2..21.19.29..2ab8~.md) + + +## Agent Logic + +Here is an agent that will accept a single character and produce a line with varying random colors of that character. + +**`/app/track7.hoon`** + +```hoon +/+ default-agent, dbug, shoe, sole +|% ++$ versioned-state + $% state-0 + == ++$ state-0 %0 ++$ card card:agent:shoe ++$ command @t +-- +%- agent:dbug +=| state-0 +=* state - +^- agent:gall +%- (agent:shoe command) +^- (shoe:shoe command) +|_ =bowl:gall ++* this . + default ~(. (default-agent this %|) bowl) + leather ~(. (default:shoe this command) bowl) +++ on-init on-init:default +++ on-save !>(state) +++ on-load + |= old=vase + ^- (quip card _this) + `this(state !<(state-0 old)) +++ on-poke on-poke:default +++ on-peek on-peek:default +++ on-arvo on-arvo:default +++ on-watch on-watch:default +++ on-leave on-leave:default +++ on-agent on-agent:default +++ on-fail on-fail:default +++ command-parser + |= =sole-id:shoe + ^+ |~(nail *(like [? command])) + (stag & (boss 256 (more gon qit))) +++ on-command + |= [=sole-id:shoe =command] + ^- (quip card _this) + :_ this + ^- (list card) + :~ :+ %shoe ~ + ^- shoe-effect:shoe + :- %sole + ^- sole-effect:sole :- %klr + ^- styx + =/ idx 0 + =| fx=styx + =/ rng ~(. og eny:bowl) + |- + ?: =(80 idx) fx + =^ huer rng (rads:rng 256) + =^ hueg rng (rads:rng 256) + =^ hueb rng (rads:rng 256) + %= $ + idx +(idx) + fx `styx`(weld fx `styx`~[[[`%br ~ `[r=`@ux`huer g=`@ux`hueg b=`@ux`hueb]] command ~]]) + == == +++ can-connect + |= =sole-id:shoe + ^- ? + ?| =(~zod src.bowl) + (team:title [our src]:bowl) + == +++ on-connect on-connect:leather +++ on-disconnect on-disconnect:leather +++ tab-list tab-list:leather +-- +``` diff --git a/content/guides/additional/aqua.md b/content/guides/additional/aqua.md index 425524b..df8936f 100644 --- a/content/guides/additional/aqua.md +++ b/content/guides/additional/aqua.md @@ -4,7 +4,7 @@ description = "Learn to write tests with Aqua" weight = 5 +++ -# Concepts +## Concepts Aqua (short for "aquarium", alluding to the idea that you're running multiple ships in a safe, artificial environment and watching them @@ -14,7 +14,7 @@ within a single host. pH is a library of functions designed to make it easy to write integration tests using Aqua. -# First test +## First test To run your first pH test, run the following commands: @@ -64,7 +64,7 @@ breaches, mock http clients or servers, or anything you can imagine. Check out `/lib/ph/io.hoon` for other available functions, and look at other tests in `/ted/ph/` for inspiration. -# Reference +## Reference Aqua has the following commands: diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md new file mode 100644 index 0000000..9575377 --- /dev/null +++ b/content/guides/additional/generators.md @@ -0,0 +1,241 @@ ++++ +title = "Generators" +weight = 73 ++++ + +Generator files provide a way for users to interact with code "scripts" through the Dojo prompt. There are +three basic kinds of generators: + +1. Bare or naked generators, standalone computations that can accept input and carry out a single calculation. +2. `%say` generators, scripts which can utilize the full system knowledge (`now`, `our`, `eny`) and accept + optional input arguments. +3. `%ask` generators, scripts driven by interactive prompts. + +(Threads have some commonalities with generators, _q.v._) + +Generators are a Dojo concept, although they can also be applied to agents (such as `+dbug`). This guide will +show you how to build and invoke all kinds of generators. + + +## Bare Generators + +A basic generator is a gate, a core with a `$` buc arm and a sample. + +The Dojo will supply the sample directly to the core in the `$` buc arm. + +A bare generator must be a gate but can have more complicated internal structure, as with all Hoon code. It does +not know about entropy `eny`, ship identity `our`, or the timestamp `now`. + +**`/gen/add-one.hoon`** + +```hoon +|= n=@ud +=< +(add-one n) +|% +++ add-one + |= a=@ud + ^- @ud + (add a 1) +-- +``` + +Invoke as `+add-one 5`. + +You could in principle use a `|*` bartar wet gate as well, but other cores don't pattern-match to what Dojo expects. + + +## `%say` Generators + +A `%say` generator can have zero, many, or optional arguments, unlike a bare generator. It can also have access to +system variables like `now`, `our`, and `eny`. + +For instance, the following generator can be run with no arguments: + +**`/gen/say.hoon`** + +```hoon {% copy=true %} +:- %say +|= * +:- %noun +(add 40 2) +``` + +```hoon +> +say +42 +``` + +A `%say` generator is structurally a head-tagged cell of a gate which returns a head-tagged cell of a mark and a value +(or a `cask`). + +The head tag over the entire generator is always `%say`. The `cask` tag is most commonly `%noun`. + +We use `%say` generators when we want to provide something else in Arvo, the Urbit operating system, with metadata about +the generator's output. This is useful when a generator is needed to pipe data to another program, a frequent occurrence. + +To that end, `%say` generators use `mark`s to make it clear, to other Arvo computations, exactly what kind of data their +output is. A `mark` is akin to a MIME type on the Arvo level. A `mark` describes the data in some way, indicating that +it's an `%atom`, or that it's a standard such as `%json`, or even that it's an application-specific data structure like +`%talk-command`. + +The gate sample follows this pattern, with undesired elements stubbed out by `*`: + +```hoon +|= $: :: environment + $: now=@da :: timestamp + eny=@uvJ :: entropy + bec=beak :: clay beak + == + :: :: unnamed args + $= + $: arg=@ :: required arguments + == + ~ + == + :: :: named args + $= + $: named-arg=@ :: optional arguments + == + ~ + == + == +``` + +The Dojo will modify the sample by inserting `%~` (constant null) at the end of each collection, since the Dojo adapts +the input arguments into a list (either the unnamed/required argument list or the named/optional argument list). + +### Zero arguments + +`/gen/vats.hoon` is commonly used to check on the status of installed desks. It can be invoked with optional arguments: + +``` +> +vats +%base + /sys/kelvin: [%zuse 414] + base hash ends in: drceb + %cz hash ends in: drceb + app status: running + pending updates: ~ + +> +vats, =verb %.n +%base + /sys/kelvin: [%zuse 414] + base hash ends in: drceb + %cz hash ends in: drceb + app status: running + pending updates: ~ + +> +vats, =filt %suspended +``` + +### Optional arguments + +Let's look at an example that uses all three parts. + +**`/gen/dice.hoon`** + +```hoon {% copy=true %} +:- %say +|= [[now=@da eny=@uvJ bec=beak] [n=@ud ~] [bet=@ud ~]] +:- %noun +[(~(rad og eny) n) bet] +``` + +This is a very simple dice program with an optional betting functionality. In the code, our sample specifies faces on all +of the Arvo data, meaning that we can easily access them. We also require the argument `[n=@ud ~]`, and allow the +_optional_ argument `[bet=@ud ~]`. + +We can run this generator like so: + +```hoon +> +dice 6, =bet 2 +[4 2] + +> +dice 6 +[5 0] + +> +dice 6 +[2 0] + +> +dice 6, =bet 200 +[0 200] + +> +dice +nest-fail +``` + +Notice how the `,` com works to separate arguments and how the name of the optional argument must be included. + +We get a different value from the same generator between runs, something that isn't possible with a bare generator. Another +novelty is the ability to choose to not use the second argument. + +- [Hoon School, “1.9 Generators”](/guides/core/hoon-school/generators) + + +## `%ask` Generators + +We use an `%ask` generator when we want to create an interactive program that prompts for inputs as it runs, rather than +expecting arguments to be passed in at the time of initiation. + +Like `%say` generators, `%ask` generators are head-tagged cells of gates, but with `%ask`. + +The code below is an `%ask` generator that checks if the user inputs `"blue"` when prompted [per a classic Monty Python +scene](https://www.youtube.com/watch?v=L0vlQHxJTp0). + +**`/gen/axe.hoon`** + +```hoon {% copy=true mode="collapse" %} +/- sole +/+ generators +=, [sole generators] +:- %ask +|= * +^- (sole-result (cask tang)) +%+ print leaf+"What is your favorite color?" +%+ prompt [%& %prompt "color: "] +|= t=tape +%+ produce %tang +?: =(t "blue") + :~ leaf+"Oh. Thank you very much." + leaf+"Right. Off you go then." + == +:~ leaf+"Aaaaagh!" + leaf+"Into the Gorge of Eternal Peril with you!" +== +``` + +Run the generator from the Dojo: + +```hoon +> +axe + +What is your favorite color? +: color: +``` + +Instead of simply returning something, your Dojo's prompt changed from `~sampel-palnet:dojo>` to `~sampel-palnet:dojo: color:`, +and now expects additional input. Let's give it an answer: + +```hoon +: color: red +Into the Gorge of Eternal Peril with you! +Aaaaagh! +``` + +`%ask` generators return `sole-effect`s. For more information on these, consult the [guide on command-line agents](/guides/additional/cli). + +`%ask` generators can also accept arguments, although this is uncommon. + + +## Generators for Agents + +Generators can furthermore interact specifically with agents. + +The [`+dbug` agent](/guides/additional/app-workbook/dbug) is invoked against an agent to display internal state. + +Any app can implement generators to wrap raw pokes (see [`%ahoy`](/guides/additional/app-workbook/ahoy) for instance). + +For instance, `:dojo|wipe` is equivalent to `:dojo +dojo/wipe`. This pokes the `%dojo` agent with the output from running the generator located at `/gen/dojo/wipe.hoon`. + +The Hood/Helm tooling like `|install` are generators automatically routed by Dojo to the correct agent. `|commit`, for instance, is equivalent to `:hood +hood/commit`. `%hood` generators are special-cased because it is the system app. diff --git a/content/guides/additional/http-api-guide.md b/content/guides/additional/http-api-guide.md index 1ad62a7..d70b5b2 100644 --- a/content/guides/additional/http-api-guide.md +++ b/content/guides/additional/http-api-guide.md @@ -685,7 +685,7 @@ as a reference: [eyre-ext-ref]: /reference/arvo/eyre/external-api-ref [eyre-guide]: /reference/arvo/eyre/guide -[http-api-src]: https://github.com/urbit/urbit/tree/master/pkg/npm/http-api +[http-api-src]: https://github.com/urbit/js-http-api [eyre]: /reference/glossary/eyre [vane]: /reference/glossary/vane [arvo]: /reference/glossary/arvo diff --git a/content/guides/additional/json-guide.md b/content/guides/additional/json-guide.md index 119825e..8b832de 100644 --- a/content/guides/additional/json-guide.md +++ b/content/guides/additional/json-guide.md @@ -10,8 +10,8 @@ Urbit represents JSON data with the `$json` structure (defined in `lull.hoon`). JSON data on the web is encoded in text, so Urbit has two functions in `zuse.hoon` for dealing with this: -- [`+en-json:html`](/reference/hoon/zuse/2e_2-3#en-jsonhtml) - For printing `$json` to a text-encoded form. -- [`+de-json:html`](/reference/hoon/zuse/2e_2-3#de-jsonhtml) - For parsing text-encoded JSON to a `$json` structure. +- [`+en:json:html`](/reference/hoon/zuse/2e_2-3#enjsonhtml) - For printing `$json` to a text-encoded form. +- [`+de:json:html`](/reference/hoon/zuse/2e_2-3#dejsonhtml) - For parsing text-encoded JSON to a `$json` structure. You typically want `$json` data converted to some other `noun` structure or vice versa, so Urbit has three collections of functions for this purpose, also in `zuse.hoon`: @@ -21,7 +21,7 @@ You typically want `$json` data converted to some other `noun` structure or vice The relationship between these types and functions look like this: -![json diagram](https://media.urbit.org/docs/json-diagram.svg) +![json diagram](https://media.urbit.org/docs/json-diagram-v2.svg) Note this diagram is a simplification - the `+dejs:format` and `+enjs:format` collections in particular are tools to be used in writing conversion functions rather than simply being used by themselves, but it demonstrates the basic relationships. Additionally, it is less common to do printing/parsing manually - this would typically be handled automatically by Eyre, though it may be necessary if you're retrieving JSON data via the web client vane Iris. @@ -157,10 +157,10 @@ Now we can try calling the `+to-js` function with our data to convert it to `$js ] ``` -Let's also see how that `$json` would look as real JSON encoded in text. We can do that with `+en-json:html`: +Let's also see how that `$json` would look as real JSON encoded in text. We can do that with `+en:json:html`: ``` -> (crip (en-json:html (to-js:user-lib usr))) +> (en:json:html (to-js:user-lib usr)) '{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}' ``` @@ -281,13 +281,13 @@ Let's look at an example. Here's a gate that takes in some `$json`, decodes it w Let's try it: ``` -> +of-test (need (de-json:html '{"foo":"Hello"}')) +> +of-test (need (de:json:html '{"foo":"Hello"}')) 'Hello!!!' -> +of-test (need (de-json:html '{"bar":true}')) +> +of-test (need (de:json:html '{"bar":true}')) 'Yes' -> +of-test (need (de-json:html '{"baz":["a","b","c"]}')) +> +of-test (need (de:json:html '{"baz":["a","b","c"]}')) 'abc' ``` @@ -322,13 +322,13 @@ Let's look at a practical example. Here's a generator you can save in the `%base Let's try it: ``` -> +ou-test (need (de-json:html '{"foo":"hello","bar":true,"baz":[1,2,3,4]}')) +> +ou-test (need (de:json:html '{"foo":"hello","bar":true,"baz":[1,2,3,4]}')) ['hello' %.y {1 2 3 4}] -> +ou-test (need (de-json:html '{"foo":"hello","bar":true}')) +> +ou-test (need (de:json:html '{"foo":"hello","bar":true}')) ['hello' %.y {}] -> +ou-test (need (de-json:html '{"foo":"hello"}')) +> +ou-test (need (de:json:html '{"foo":"hello"}')) [%key 'bar'] dojo: hoon expression failed ``` @@ -433,7 +433,7 @@ First, we'll save the code above as `user.hoon` in the `/mar` directory our of ` Let's quickly create a `$json` object to work with: ``` -> =jon (need (de-json:html '{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}')) +> =jon (need (de:json:html '{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}')) > jon [ %o p @@ -492,7 +492,7 @@ Let's test it out by giving it our `$user` data: Finally, let's see how that looks as JSON encoded in text: ``` -> (crip (en-json:html (user-to-json usr))) +> (en:json:html (user-to-json usr)) '{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}' ``` diff --git a/content/guides/additional/noun-channels.md b/content/guides/additional/noun-channels.md new file mode 100644 index 0000000..e3c83ff --- /dev/null +++ b/content/guides/additional/noun-channels.md @@ -0,0 +1,102 @@ ++++ +title = "Eyre noun channels" +description = "A low-level overview of talking to Eyre's channel system in noun mode." +weight = 40 ++++ + +So far, developers have typically used JSON to interact with Urbit ships through +Eyre's HTTP interface. As of kernel version `[%zuse 413]`, however, Eyre also +supports sending and received nouns directly. At this stage, there are limited +options for dealing with nouns in other languages, so this guide will only cover +the channel mechanics on a low-level. You may, however, be interested in the +[`@urbit/nockjs`](https://github.com/urbit/nockjs) package and the +work-in-progress [`json-bgon` PR for +`@urbit/js-http-api`](https://github.com/urbit/js-http-api/pull/4). + +{% callout %} + +If you are not familiar with low-level Eyre channel mechanics, please have a +read through the [Eyre guide](/reference/arvo/eyre/guide) first. + +{% /callout %} + +Eyre will create a noun channel if a `PUT` request to open a new channel +includes the following HTTP header: + +``` +content-type: application/x-urb-jam +``` + +...and the body contains the [`++jam`](/reference/hoon/stdlib/2p#jam) of a +`list` of `$channel-request`s with +[`@uw`](/reference/hoon/auras#table-of-auras) base64 encoding. + +A `channel-request` is defined in `eyre.hoon` as: + +```hoon +:: channel-request: an action requested on a channel +:: ++$ channel-request + $% :: %ack: acknowledges that the client has received events up to :id + :: + [%ack event-id=@ud] + :: %poke: pokes an application, validating :noun against :mark + :: + [%poke request-id=@ud ship=@p app=term mark=@tas =noun] + :: %poke-json: pokes an application, translating :json to :mark + :: + [%poke-json request-id=@ud ship=@p app=term mark=@tas =json] + :: %watch: subscribes to an application path + :: + [%subscribe request-id=@ud ship=@p app=term =path] + :: %leave: unsubscribes from an application path + :: + [%unsubscribe request-id=@ud subscription-id=@ud] + :: %delete: kills a channel + :: + [%delete ~] + == + +``` +So, given the following (trivial) `(list channel-request)`: + +```hoon +[%delete ~]~ +``` + +...it is jammed to the following HEX: + +``` +0xACAE8CAD8CAC8F805 +``` + +...then encoded in the following `@uw`-style base64 string in the request body: + +``` +0w2I.HEOJz.aOfw5 +``` + +If the body of the request is not correctly encoded as described above, it will +fail with a `400` status. + +If successful, you can then make a `GET` request to open an event stream for the +newly created channel. The `GET` request must include the following header: + +``` +x-channel-format: application/x-urb-jam +``` + +If the `GET` request is for an existing channel which is not already in noun +mode, it will fail with a `406` status code. You cannot change the channel mode +once the channel has been established. If the header is missing, Eyre will +assume you're asking for JSON mode, so it will also fail due to the channel mode +mismatch. + +If the `GET` request is successful, you'll start receiving SSE events containing `@uw`-encoded jams of the following structure: + +```hoon +[request-id=@ud channel-event] +``` + +See the [`$channel-event`](/reference/arvo/eyre/data-types#channel-event) entry +in the data type reference for more details. diff --git a/content/guides/additional/parsing.md b/content/guides/additional/parsing.md index 61fb62e..699fa75 100644 --- a/content/guides/additional/parsing.md +++ b/content/guides/additional/parsing.md @@ -274,6 +274,25 @@ One common scenario where `+cold` sees play is when writing [command line interface (CLI) apps](/guides/additional/cli-tutorial). We usher the reader there to find an example where `+cold` is used. +### [`+less`](/reference/hoon/stdlib/4f/#less) + +`+less` builds a `rule` to exclude matches to its first argument. It is commonly +used to filter out an undesired match. + +``` +> (;~(less buc next) [[1 1] " "]) +[p=[p=1 q=2] q=[~ [p=' ' q=[p=[p=1 q=2] q=""]]]] + +> (;~(less ace next) [[1 1] " "]) +[p=[p=1 q=1] q=~] +``` + +Here we see that the first case refuses to parse `buc` `$` (which is not present), so +the `ace` ` ` succeeds (via `+next` which matches any character). + +The second case attempts to parse the excluded character `ace` ` ` and fails on the +first character as it should. + ### [`+knee`](/reference/hoon/stdlib/4f/#knee) Another important function in the parser builder library is `+knee`, used for building diff --git a/content/guides/additional/remote-scry.md b/content/guides/additional/remote-scry.md new file mode 100644 index 0000000..c8b4105 --- /dev/null +++ b/content/guides/additional/remote-scry.md @@ -0,0 +1,216 @@ ++++ +title = "Remote Scry" +description = "Learn about scrying over the network" +weight = 5 ++++ + +To [scry](/reference/glossary/scry) is to perform a *read* from Urbit's +referentially transparent namespace. In other words, it's a function from a +`path` to a `noun` (although in some cases, the resulting type may be more +constrained). Previously we only supported scrying within the same ship, but +from Kernel version `[%zuse 413]`, it is possible to scry from *other* ships. + +{% callout %} + +**Warning** + +1. It should also be noted that, while responses are signed, encryption has not + yet been implemented. + +2. The initial release of `[%zuse 413]` had a bug in the remote scry client + implementation that causes crashes and failed downloads. This issue was + fixed in [this PR](https://github.com/urbit/urbit/pull/6617), which was + [released](https://github.com/urbit/urbit/releases/tag/urbit-os-v2.141) (at + the same Kelvin) on June 1, 2023. Any ships that are still on the initial + `[%zuse 413]` Kelvin release will continue to experience this bug. + + The recommended approach to dealing with this is to include a timer in clients + to fall back to using Ames for a download if a remote scry request doesn't + succeed within a certain amount of time. This is what Clay uses, and its main + advantage is that client ships on older pre-remote-scry Kelvins can still + download data from newer server ships. + +{% /callout %} + +## Lifecycle of a scry + +When you think of scry, you probably think of [`.^` +(dotket)](/reference/hoon/rune/dot#-dotket). However, since networking is +asynchronous, this is not a suitable interface for remote scry. Instead, a ship +that wants to read from a remote part of the namespace will have to pass a +`%keen` task to its Ames, which then cooperates with Vere to produce the +desired data. In some future event when the result is available, Ames gives it +back as a `%tune` gift. From the requester's perspective, this is the entire +default lifecycle of a remote scry request. + +Of course, you need to know how `%keen` and `%tune` look to be able to use +them. There are also a few exceptions to this default lifecycle. We'll go +through all of this in a moment, but first, let's look at what kind of data is +possible to scry. + +## Publishing + +At the moment, there are two vanes that can handle remote scry requests: +[Clay](/reference/arvo/clay/clay) and [Gall](/reference/arvo/gall/gall). Clay +uses it to distribute source code in a more efficient manner than is possible +with conventional Ames, but conceptually it only extends its [local +scries](/reference/arvo/clay/scry) over the network, with the notable +difference that you can't scry at the *current* time, since the requester +doesn't know when the request reaches the publisher. Additionally, the paths +are modified so that the vane and care are specified separately, like so: +`/c/x/1/base/sys/hoon/hoon`. + +Gall is more interesting. First, let's clear up a possible misunderstanding +that could easily come up: remote scry does *not* involve calling an agent's +`+on-peek` arm. `+on-peek` scries always happen at the current time, and since +the requester can't know at which time the publisher handles the request, these +aren't possible to reliably serve. + +Instead, agents *ask* Gall to `%grow` paths in the namespace on their behalf. +Gall will take care of incrementing version numbers, so that the same path +never maps to different nouns. The agent can also ask Gall to delete data, +either at a specific version number, or everything up to and including a +version number. Concretely, we've extended `$note:agent:gall` to include the +following cases: + +```hoon ++$ note + $% ... + [%grow =path =page] :: publish + [%tomb =case =path] :: delete one + [%cull =case =path] :: delete up to + == +``` + +Here's an example sequence of cards that use these: + +```hoon +[%pass /call/back/path %grow /foo atom+'lorem'] :: /foo version 0 +[%pass /call/back/path %grow /foo atom+'ipsum'] :: /foo version 1 +[%pass /call/back/path %grow /foo atom+'dolor'] :: /foo version 2 +[%pass /call/back/path %grow /foo atom+'sit'] :: /foo version 3 + +[%pass /call/back/path %tomb ud+3 /foo] :: delete /foo version 3 +[%pass /call/back/path %cull ud+1 /foo] :: delete /foo 0 through 1 + +[%pass /call/back/path %grow /foo atom+'amet'] :: /foo version 4 +[%pass /call/back/path %grow /foo/bar atom+123] :: /foo/bar version 0 +``` + +After this sequence of cards we would have the following mappings (assuming the +agent that emits them is named `%test`): + +```hoon +/g/x/2/test//foo -> [%atom 'dolor'] +/g/x/4/test//foo -> [%atom 'amet'] +/g/x/0/test//foo/bar -> [%atom 123] +``` + +Let's pick apart the first one of these paths. + +```hoon +/g :: g for Gall +/x :: a care of %x generally means "normal read" +/2 :: version number +/test :: the agent that published the data +/ :: ??? +/foo :: the path that the data is published on +``` + +What's that lone `/` before the path? It signifies that this data is published +by *Gall* itself, instead of the `+on-peek` arm in the `%test` agent. As part +of the remote scry release, we will *reserve* part of the scry namespace for +Gall, effectively *preventing* any agents from directly publishing at those +paths. Though as we've seen, they can do it indirectly, by asking Gall to do it +for them using `%grow`. + +As long as the extra `/` is included, Gall will serve scries with care `%x` at +both specific revision numbers and at arbitrary times. If the extra `/` is not +included, the scry has to happen at the current time, since we don't cache old +results of calling `+on-peek`. + +### Additional Gall cares + +Apart from supporting reads using the `%x` care, Gall now also supports three new cares: + +- `%t` lists all subpaths that are bound under a path (only supported at the + current time, i.e. not remotely!). +- `%w` gives the latest revision number for a path (only supported at the + current time, i.e. not remotely!). +- `%z` gives the hash identifier of the value bound at the path (supported at + any time and at specific revisions, but not remotely). + +All of these require the extra `/` to be present in the path, just as with `%x`. + +## Scrying tasks + +With this, we're ready to look at all the new tasks to, and gifts from, Ames: + +```hoon ++$ task + $% ... + [%keen =ship =path] :: peek [ship /vane/care/case/spur] + [%yawn =ship =path] :: cancel request from arvo + [%wham =ship =path] :: cancels all scry requests from any vane + ... + == +:: ++$ gift + $% ... + [%tune spar roar=(unit roar)] + ... + == +``` + +At this point, most of these should be very clear, but briefly: + +- We pass `[%keen =ship =path]` to Ames to request to read from `path` on + `ship`. Example: + ```hoon + [%pass /call/back/path %arvo %a %keen ~sampel /c/x/4/base/sys/hoon/hoon] + ``` + +- We pass `[%yawn =ship =path]` to tell Ames that we're no longer interested in + a response. Example: + ```hoon + [%pass /call/back/path %arvo %a %yawn ~sampel /g/x/4/test//foo] + ``` + +- We pass `[%wham =ship =path]` to tell Ames that *no-one* on this ship is + interested in a response. Example: + ```hoon + [%pass /call/back/path %arvo %a %wham ~sampel /g/x/4/test//foo] + ``` + +- Ames gives the following to the original requester(s), either when it has a + response, or when the request gets `%wham`ed: + ```hoon + [%tune [=ship =path] roar=(unit roar)] + ``` + The outer `unit` of `roar` will be `~` if Ames doesn't have a + response, but may have one in the future. Otherwise, it will + contain a signature and the data. The data in the + [`$roar`](/reference/arvo/ames/data-types#roar) may be `~`, + meaning that there is no value at this path and will never be + one. + +## `-keen` + +In addition to the above interface offered to agents, there is also support for +making scry requests from threads using `+keen` in `lib/strandio`. It accepts a +`[=ship =path]` and returns a `(unit page)`. There is also a [thread `ted/keen` +that demonstrates +this](https://github.com/urbit/urbit/blob/i/5788/remote-scry/pkg/arvo/ted/keen.hoon). +You can run it from the dojo using `-keen [ship path]`. For example, this reads +the `%noun` mark's source code out of `~zod`'s `%kids` desk, try it! + +``` +-keen [~zod /c/x/1/kids/mar/noun/hoon] +``` + +## Additional reading + +- [Gall scry reference](/reference/arvo/gall/scry): Reference documentation of + Gall's vane-level and agent-level scry interface. + +- [Ames API reference](/reference/arvo/ames/tasks): Reference documentation of `task`s that can be passed to Ames, including those for remote scries. diff --git a/content/guides/additional/udon.md b/content/guides/additional/udon.md new file mode 100644 index 0000000..bbaffed --- /dev/null +++ b/content/guides/additional/udon.md @@ -0,0 +1,179 @@ ++++ +title = "Udon (Markdown-esque)" +description = "Learn the basics of Udon" +weight = 97 ++++ + +Udon is a domain-specific language for composing documents. Udon is very similar +to Markdown, but with some minor variation in syntax and some additional +Urbit-related affordances. + +Udon files are compiled to `manx`es (Urbit's XHTML/XML representation), so can +easily be used to publish documents to the browser. Udon also allows you to +embed arbitrary [Sail](/guides/additional/sail) syntax, which itself allows +embedding arbitrary Hoon, so it can be quite powerful for dynamic content when +compiled against an appropriate subject. + +This document will walk through the basics of Udon and its syntax. + +## Basic example + +Here's an example of an Udon file and its various allowed syntax. + +```` +;> + +# H1 + +## H2 + +### H3 + +#### H4 + +##### H5 + +###### H6 + +This is a paragraph with _italics_, *bold* and +`inline code`. Sentences can be hard wrapped. + + +- unordered +- list + ++ ordered ++ list + +[link](https://urbit.org) + +![image](https://media.urbit.org/guides/additional/dist/wut.svg) + +``` +fenced codeblock +(note language spec not supported) +``` + +horizontal rule: +--- + +> block quotes + may be hard-wrapped if indented + +Backslash at end\ +of line adds linebreak + +Udon syntax may be prefixed with \*backslashes\* to escape. + +Hoon atom literals like ~sampel-palnet and ~.foo will +be rendered as inline code. + +;table + ;tr + ;td: Arbitrary + ;td: Sail + == + ;tr + ;td: is + ;td: allowed + == +== +```` + +## Syntax summary + +- The first line of a `.udon` document *must* be a single rune: `;>`. This tells + the compiler to interpret everything following as udon. +- **Paragraphs**: Content on a single line will be made into a paragraph. + Paragraphs may be hard-wrapped, so consecutive lines of text will become a + single paragraph. The paragraph will be ended by an empty line or other block + element. +- **Headers**: lines beginning with 1-6 `#`s followed by a single space and then + some content (e.g. `## foo`) will be made into headers. The number of `#`s + dictates the header level. +- **Italics**: content wrapped in single `_`s (e.g. `_foo_`) will be made italic. +- **Bold**: content wrapped in single `*`s (e.g. `*foo*`) will be made bold. +- **Unordered lists**: lines beginning with `-` followed by a space will be made + into items in a list. List lines can be hard-wrapped, with two spaces + beginning each subsequent line to be included in the list. Lists can be nested + by indenting the `-`s a further two spaces for each level of nesting. +- **Ordered lists**: lines beginning with `+` followed by a space will be made into + ordered lists, and numbered in the order they appear. These have the same + wrapping and nesting logic as unordered lists. +- **Links**: this is standard markdown syntax: square bracks containing the display + content and then parentheses containing the URL, e.g. + `[foo](http://example.com)`. The URL may also be a relative link or an anchor + link. +- **Images**: this is also standard markdown; a link with an exclamation mark at the + beginning, e.g. `![foo](http://example.com/image.png)`. The square brackets + contain the alt-text and the the parentheses contain the image URL. +- **Inline code**: text wrapped in single backticks will be rendered verbatim in a + monospace font. +- **Fenced codeblocks**: Triple-backticks on their own line begin and end a + codeblock. All lines in between will be rendered verbatim in a monospace font. + Note that udon does not support a language specification after the opening + backticks like markdown does. +- **Horizontal rules**: Three or more hyphens (`---`) will create a horizontal rule. +- **Block quotes**: a line beginning with `>` creates a block quote. This may be + hard-wrapped, as long as the next line is indented two spaces. Block quotes + may contain anything, including other blockquotes. +- **Line breaks**: A line ending in a single backslash will have a line break + inserted at the end, so it will not flow together with the subsequent line as + is usually the case. +- **Escape characters**: You may prefix Udon syntax with a backslash to have it + treated as the literal text. +- **Hoon literals and wings**: Udon will automatically render any values with + atom aura syntax as inline code. It'll also render arms like `++foo:bar`, + `+$baz`, and `+*foo:bar:baz, as inline code.` +- **Sail**: this is hoon's native XML syntax. Udon will parse it, execute it, and + include the `+$manx`es produced in the resulting document. This means you can + embed arbitrary hoon in the document. + +{% callout %} + +Note that Udon is quite strict on its syntax, and may fail to parse if it's + incorrect. + +{% /callout %} + +## Udon Mode + +An Udon file has a `.udon` extension (an `%udon` mark). + +The first thing in an Udon file must be the `micgar` rune: `;>` + +Micgar tells the Hoon compiler to interpret everything afterwards as Udon. +Udon-mode ends at the end of the file; there's no way to terminate micgar before +that. Udon is therefore useful for whole documents rather than embedding +snippets in other Hoon files. + +The Hoon compiler will produce a `manx` as a result. + +To scry out a file, compile it against the standard library, and stringify the +resulting XHTML, you can do: + +``` +%- crip +%- en-xml:html +!< manx +%+ slap !>(..zuse) +%- ream +.^(@t %cx /=the-desk=/the-file/udon) +``` + +Note you may want to provide more than just `..zuse` in the subject (like a +`bowl`), or if you're automatically building untrusted code, you may want to +provide less. It depends on your use case. + +You can alternatively import and build udon files at compile time with a [`/*` +(fastar) Ford rune](/reference/hoon/rune/fas#-fastar) specifying an `%elem` mark +(which produces a `manx`), although note it compiles the Udon against an empty +subject, so Hoon in embedded Sail won't have access to standard library +functions. A mark conversion gate from `%udon` to `%elem` is another option. + +## Examples + +The [Docs App](https://urbit.org/applications/~pocwet/docs) includes a [a few +files written in +Udon](https://github.com/tinnus-napbus/docs-app/tree/main/bare-desk/doc) which +are useful as a reference. diff --git a/content/guides/additional/workbook/_index.md b/content/guides/additional/workbook/_index.md index bab9221..db35d94 100644 --- a/content/guides/additional/workbook/_index.md +++ b/content/guides/additional/workbook/_index.md @@ -14,3 +14,9 @@ insert_anchor_links = "right" ## [Roman Numerals](/guides/additional/workbook/roman) ## [Solitaire Cipher](/guides/additional/workbook/solitaire) + +## [Water between Towers](/guides/additional/workbook/water-towers) + +## [ABC Blocks](/guides/additional/workbook/abc-blocks) + +## [Luhn Number](/guides/additional/workbook/luhn-number) \ No newline at end of file diff --git a/content/guides/additional/workbook/abc-blocks.md b/content/guides/additional/workbook/abc-blocks.md new file mode 100644 index 0000000..09066eb --- /dev/null +++ b/content/guides/additional/workbook/abc-blocks.md @@ -0,0 +1,574 @@ ++++ +title = "ABC Blocks" +weight = 11 ++++ + +## Challenge: ABC Blocks + +You are given a collection of blocks with two letters of the alphabet on each block. A complete alphabet is guaranteed among all sides of the blocks. You would like to check if a given word can be written with the provided set of blocks. + +An example set of blocks: +``` + (F E) + (A W) + (Q V) + (B M) + (X H) + (N P) + (I Z) + (G U) + (S R) + (K Y) + (T L) + (O C) + (J D) + (A N) + (O B) + (E R) + (F S) + (L Y) + (P C) + (Z M) +``` + +Your task for this challenge is to write a generator `abc-blocks`. It takes a cell of two arguments. The first argument is a `(list (pair @t @t))` which represents the input set of blocks. The second argument is a `@t` which represents the word that you'd like to check. + +Your generator should first check if the input blocks cover all letters of the alphabet. If not, the generator should fail (possibly returning an error message). It should also check if the input word only has alphabetical characters (no spaces, numbers, or special characters). Otherwise it should fail. Then, it should check whether the word can be spelled with the blocks, either returning a `%.y` or `%.n`. It should not care about case, for the blocks or for the word. + +Example usage: +``` +> +abc-blocks [[['a', 'b'] ['c' 'd'] ['e' 'f'] ~] 'fad'] +dojo: naked generator failure + +> +abc-blocks [[['a', 'b'] ['c' 'd'] ['e' 'f'] ['g' 'h'] ['i' 'j'] ['k' 'l'] ['m' 'n'] ['o' 'p'] ['q' 'r'] ['s' 't'] ['u 'v'] ['w' 'x] ['y' z] ~] '12%-3'] +dojo: naked generator failure + +> +abc-blocks [[['a', 'B'] ['C' 'd'] ['e' 'F'] ['G' 'h'] ['i' 'J'] ['K' 'l'] ['m' 'N'] ['o' 'P'] ['Q' 'r'] ['s' 'T'] ['U 'v'] ['w' 'X'] ['y' Z'] ~] 'cat'] +%.y + +> +abc-blocks [[['a', 'B'] ['C' 'd'] ['e' 'F'] ['G' 'h'] ['i' 'J'] ['K' 'l'] ['m' 'N'] ['o' 'P'] ['Q' 'r'] ['s' 'T'] ['U 'v'] ['w' 'X'] ['y' Z'] ~] 'CAT'] +%.y + +> +abc-blocks [[['a', 'B'] ['C' 'd'] ['e' 'F'] ['G' 'h'] ['i' 'J'] ['K' 'l'] ['m' 'N'] ['o' 'P'] ['Q' 'r'] ['s' 'T'] ['U 'v'] ['w' 'X'] ['y' Z'] ~] 'BAT'] +%.n +``` + +## Unit Tests + +Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior. + +```hoon +/+ *test +/= abc-blocks /gen/abc-blocks +|% +:: test for failure of incomplete alphabet +:: +++ test-01 + =/ blocks `(list (pair @t @t))`[['a' 'b'] ['c' 'd'] ['e' 'f'] ['g' 'h'] ['i' 'j'] ['k' 'l'] ['m' 'n'] ['o' 'p'] ['q' 'q'] ['s' 't'] ['u' 'v'] ['w' 'x'] ['y' 'z'] ~] + =/ word `@t`'foo' + %- expect-fail |. (abc-blocks blocks word) +++ test-02 + =/ blocks `(list (pair @t @t))`[['a' 'b'] ['c' 'd'] ['e' 'f'] ['g' 'h'] ['i' 'j'] ['k' 'l'] ['m' 'n'] ['q' 'r'] ['s' 't'] ['u' 'v'] ['w' 'x'] ['y' 'z'] ~] + =/ word `@t`'foo' + %- expect-fail |. (abc-blocks blocks word) +++ test-03 + =/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['E' 'F'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'A'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ~] + =/ word `@t`'foo' + %- expect-fail |. (abc-blocks blocks word) +++ test-04 + =/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['e' 'f'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'A'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ['A' 'B'] ['j' 'x']~] + =/ word `@t`'foo' + %- expect-fail |. (abc-blocks blocks word) +++ test-05 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'foo' + %- expect-fail |. (abc-blocks blocks word) +:: test for failure of input word +:: +++ test-06 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'foo bar' + %- expect-fail |. (abc-blocks blocks word) +++ test-07 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'foo1bar' + %- expect-fail |. (abc-blocks blocks word) +++ test-08 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'foo!bar' + %- expect-fail |. (abc-blocks blocks word) +:: test for success with various capitalizations and alphabets +:: +++ test-09 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'TRAP' + %+ expect-eq + !> %.y + !> (abc-blocks blocks word) +++ test-10 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'trap' + %+ expect-eq + !> %.y + !> (abc-blocks blocks word) +++ test-11 + =/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'tRaP' + %+ expect-eq + !> %.y + !> (abc-blocks blocks word) +++ test-12 + =/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'a'] ['c' 't'] ['r' 'o'] ['p' 'n'] ['e' 'y'] ~] + =/ word `@t`'TRAP' + %+ expect-eq + !> %.y + !> (abc-blocks blocks word) +++ test-13 + =/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ~] + =/ word `@t`'trap' + %+ expect-eq + !> %.y + !> (abc-blocks blocks word) +++ test-14 + =/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~] + =/ word `@t`'fsixqhgjvtrnyyb' + %+ expect-eq + !> %.y + !> (abc-blocks blocks word) +:: test for being unable to make a word +:: +++ test-15 + =/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~] + =/ word `@t`'fsixqhgjvtrnyyyb' + %+ expect-eq + !> %.n + !> (abc-blocks blocks word) +++ test-16 + =/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~] + =/ word `@t`'fsixqhgujvtrnyyb' + %+ expect-eq + !> %.n + !> (abc-blocks blocks word) +++ test-17 + =/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~] + =/ word `@t`'AAA' + %+ expect-eq + !> %.n + !> (abc-blocks blocks word) +++ test-18 + =/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['E' 'F'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'T'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ~] + =/ word `@t`'AGENTT' + %+ expect-eq + !> %.n + !> (abc-blocks blocks word) +++ test-19 + =/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['E' 'F'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'T'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ['S' 'T'] ~] + =/ word `@t`'AGENTtT' + %+ expect-eq + !> %.n + !> (abc-blocks blocks word) +++ test-20 + =/ blocks `(list (pair @t @t))`[['A' 'Z'] ['A' 'Z'] ['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~] + =/ word `@t`'ZAZAZ' + %+ expect-eq + !> %.n + !> (abc-blocks blocks word) +-- +``` + +## Solutions + +_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere._ + +### Solution #1 + +_By ~dozreg-toplud. In the process, he found and fixed a bug in the implementation of `++curr`._ + +```hoon +:: +abc-blocks: a solution to the HSL challenge #2 +:: +:: https://github.com/tamlut-modnys/template-hsl-abc-blocks +:: Takes a cell of arguments [blocks=(list (pair @t @t)) word=@t], +:: produces a flag. +:: Crashes if the alphabet is not represented in the blocks, or if there are +:: non-alphabetical characters in the blocks or in the word. +:: Produces %.y if the word can be written with the given list of blocks, +:: %.n otherwise +:: +:: Solving this challenge revealed a bug in ++curr implementation! Refer to +:: the bottom of the file. +:: +|^ +:: Main part: +:: +|= [blocks=(list (pair @t @t)) word=@t] +^- ? +=/ word-tape=tape (trip word) +:: Convert input values to lowercase +:: +=. word-tape (cass word-tape) +=. blocks + %+ turn + blocks + |= [a=@t b=@t] + ^- (pair @t @t) + :- (crip (cass (trip a))) + (crip (cass (trip b))) +:: Define alphabet +:: +=/ alphabet=(set @t) (silt "abcdefghijklmnopqrstuvwxyz") +:: Assert: only alphabetical characters in the blocks +:: +?. %+ levy + blocks + |= [a=@t b=@t] + ^- ? + &((~(has in alphabet) a) (~(has in alphabet) b)) + ~_ leaf+"non-alphabetical character in blocks" + !! +:: Assert: only alphabetical characters in the word +:: +?. %+ levy + word-tape + |= =cord + ^- ? + (~(has in alphabet) cord) + ~_ leaf+"non-alphabetical character in word" + !! +:: Assert: complete alphabet among the blocks +:: +?. :: Iterate for block list indices i: + :: + =+ i=0 + |- ^- ? + :: if the alphabet set is empty, then the blocks contain all the letters + :: + ?: =(~ alphabet) + %.y + :: else, if we reached the end of the block list, then the opposite is true + :: + ?: =(i (lent blocks)) + %.n + :: else, delete letters on a block from the alphabet and continue + :: + =+ [a b]=(snag i blocks) + $(i +(i), alphabet (~(del in (~(del in alphabet) b)) a)) + ~_ leaf+"not complete alphabet in blocks" + !! +:: check if we can compose the word with the blocks +:: +(check blocks word-tape) +:: +:: Helping functions +:: ++check: checks if the word can be composed with the given blocks +:: +++ check + |= [blocks=(list (pair @t @t)) word=tape] + ^- ? + :: Self-reference + :: + =* this-gate ..$ + :: The word can be composed if it's empty, ... + :: + ?~ word %.y + :: ... or if the list of indices of blocks that contain i.word is not empty + :: and t.word can be composed with at least one list of the blocks made by + :: removing one of the blocks that contain i.word. + :: + :: Logical OR on a list (%.n if the list is empty) + :: + %+ lien + :: (list of lists of blocks made by removing one block that contains + :: i.word for each such block) + :: + %+ turn + :: (list of block indices that contain i.word) + :: + (find-in-blocks i.word blocks) + :: (gate that removes a block from a list of blocks by an index) + :: + (curr (curr oust blocks) 1) + :: (gate that applies ++check to a given list of blocks and t.word) + :: + (curr this-gate t.word) +:: ++ find-in-blocks: returns a list of block indices that contain +:: a given letter +:: +++ find-in-blocks + |= [letter=@t blocks=(list (pair @t @t))] + ^- (list @) + =+ i=0 + =| =(list @) + :: Iterate over elements of blocks + :: + |- + ?~ blocks + list + :: If a block contains the letter, append its index to the list + :: + =? list |(=(letter -:i.blocks) =(letter +:i.blocks)) (snoc list i) + $(i +(i), blocks t.blocks) +:: ++curr: rewrite ++curr from stdlib because the original has a bug +:: (https://github.com/urbit/urbit/issues/6655) +:: +++ curr + |* [a=$-(^ *) c=*] + |* b=_,.+<-.a + (a b c) +:: +-- +``` + +### Solution #2 + +_By ~bantus-follus_ + +```hoon +|= [blocks=(list (pair @t @t)) word=@t] +=< +=/ alphacheck (alphabet-check merged-blocks) +?. (character-check word) + ~| "Input word contains invalid characters." !! +=/ spellcheck (spell-check word) +spellcheck +|% +++ alphabet "abcdefghijklmnopqrstuvwxyz" +:: +:: merges all blocks into a single tape +++ merged-blocks (merge blocks) +:: +:: turns all blocks into individual tapes +++ tape-blocks (turn (turn (turn (turn blocks pair-to-list) crip) trip) cass) +++ merge + |= blocks=(list (pair @t @t)) + ^- tape + (cass (trip (crip `(list @t)`(zing (turn blocks pair-to-list))))) +:: +:: converts each pair to a (list @t) +++ pair-to-list + |= input=(pair @t @t) + ^- (list @t) + [-:input +:input ~] +:: +:: checks if input blocks cover all letters of the alphabet +++ alphabet-check + |= input=tape + ^- ? + =/ i 0 + |- + ?: =(i 26) + %.y + ?~ (find [(snag i alphabet)]~ input) + ~| "Full alphabet not found. {<(snag i alphabet)>} not in blocks" !! + $(i +(i)) +:: +:: checks if input word has valid chaaracters. %.y means all characters are valid +++ character-check + |= word=@t + ^- ? + =/ i 0 + =/ tapeword (cass (trip word)) + |- + ?: =(+(i) (lent tapeword)) + %.y + ?~ (find [(snag i tapeword)]~ alphabet) + %.n + $(i +(i)) +:: +:: checks if the word can be spelled using the input blocks +++ spell-check + |= word=@t + ^- ? + =/ tapeword (cass (trip word)) + =/ tape-blocks tape-blocks + =/ i 0 + =/ letter (snag i tapeword) + |- + ?: =(+(i) (lent tapeword)) + =/ blockcheck (check-blocks [tape-blocks letter]) + ?. check:blockcheck + %.n + %.y + =/ blockcheck (check-blocks [tape-blocks letter]) + ?. check:blockcheck + %.n + $(i +(i), letter (snag +(i) tapeword), tape-blocks (oust [num:blockcheck 1] tape-blocks)) +:: cycles through blocks, checking for a letter +++ check-blocks + |= [tape-blocks=(list tape) letter=@t] + ^- [num=@ check=?] + =/ i 0 + =/ block (snag i tape-blocks) + |- + ?: =(+(i) (lent tape-blocks)) + ?~ (find [letter]~ block) + [~ %.n] + [i %.y] + ?~ (find [letter]~ block) + $(i +(i), block (snag +(i) tape-blocks)) + [i %.y] + -- +``` + +### Solution #3 +_By ~dannul-bortux_ + +```hoon +!: +|= [inlist=(list [@t @t]) inword=@t] +^- $?(%.y %.n) +:: If, input list is empty +:: +?: =(0 (lent inlist)) + :: Then, throw error + :: + ~| 'Error - input list cannot be empty' + !! +=< (validate-input inlist (cass (trip inword))) +|% +++ validate-input + |= [blocks=(list [@t @t]) cword=tape] + =/ lblocks (to-lowercase blocks) + ?: ?& (validate-alpha-only cword) + (validate-complete-alpha lblocks) + (validate-word lblocks cword) + == + %.y + %.n +++ validate-alpha-only + |= w=tape + =/ i 0 + :: =/ tword (trip w) + |- + ?: =(i (lent w)) + %.y + ?. ?& (gte `@ud`(snag i w) 97) + (lte `@ud`(snag i w) 122) + == + !! + %= $ + i +(i) + == +++ validate-complete-alpha + |= blocks=(list [@t @t]) + =/ alphabet "abcdefghijklmnopqrstuvwxyz" + =/ bltape (block-taper blocks) + :: ~& "bl tape is {}" + :: =/ bltape "abcdefghijklmnopqrstuvwxyz" + =/ i 0 + |- + ?: =(i (lent alphabet)) + :: ~& "returning yes" + %.y + ?: =(~ (find (trip (snag i alphabet)) bltape)) + :: ~& "returning no at letter: {<(snag i alphabet)>}" + !! + %= $ + :: alphabet (remove-letters alphabet (snag i blocks)) + i +(i) + == + :: %.n + :: ++ remove-letters + :: |= [in=tape let=[@t @t]] + :: ~& "removing letters" + + :: in +++ block-taper + |= b=(list [@t @t]) + =/ i 0 + =/ bltape *tape + |- + ?: =(i (lent b)) + bltape + :: ~& +2:(snag i `(list [@t @t])`b) + %= $ + bltape (snoc (snoc bltape +2:(snag i `(list [@t @t])`b)) +3:(snag i `(list [@t @t])`b)) + :: bltape (snoc bltape 'a') + i +(i) + == +++ validate-word + |= [blocks=(list [@t @t]) cword=tape] + =/ wordcombos `(list tape)`(get-combos blocks) + :: ~& "validating word" + :: ~& wordcombos + =/ i 0 + |- + ?: =(i (lent wordcombos)) + %.n + :: ~& (snag i wordcombos) + ?: (word-compare (snag i wordcombos) cword) + %.y + %= $ + i +(i) + == + :: ?: ?& (validate-alph-aonly ) + :: (validate-complete-alpha ) + :: (validate-word ) + :: == + :: %.y + :: %.n +++ get-combos +|= n=(list [@t @t]) +=/ i 1 +=/ outlist `(list tape)`(snoc `(list tape)`(snoc *(list tape) (trip +2:(snag 0 `(list [@t @t])`n))) (trip +3:(snag 0 `(list [@t @t])`n))) +:: ~& outlist +|- +?: =(i (lent n)) + outlist +:: ?: =(i 0) +:: %= $ +:: outlist (snoc `(list tape)`(snoc `(list tape)`outlist (trip +2:(snag 0 `(list [@t @t])`n))) (trip +3:(snag 0 `(list [@t @t])`n))) +:: i +(i) +:: == +=/ j 0 +=/ temp *(list tape) +|- +?: =(j (lent outlist)) + %= ^$ + outlist temp + i +(i) + == +%= $ + :: temp (snoc (snoc `(list tape)`outlist (trip +2:(snag 0 `(list [@t @t])`n))) (trip +3:(snag 0 `(list [@t @t])`n))) + temp (snoc `(list tape)`(snoc `(list tape)`temp (snoc (snag j outlist) +2:(snag i `(list [@t @t])`n))) (snoc (snag j outlist) +3:(snag i `(list [@t @t])`n))) + j +(j) +== +:: %= $ +:: i +(i) +:: j 3 +:: == +++ word-compare +|= [combo=tape cword=tape] +=/ i 0 +:: ~& combo +:: ~& cword +|- +:: ~& combo +?: =(i (lent cword)) + %.y +?: =(~ (find (trip (snag i cword)) combo)) + %.n +%= $ + combo (oust [+3:(find (trip (snag i cword)) combo) 1] combo) + i +(i) +== +++ to-lowercase + |= blocks=(list [@t @t]) + =/ lcase *(list [@t @t]) + =/ i 0 + |- + ?: =(i (lent blocks)) + :: lcase + :: ~& lcase + lcase + =/ m (crip (cass (trip +2:(snag i blocks)))) + =/ n (crip (cass (trip +3:(snag i blocks)))) + %= $ + lcase (snoc `(list [@t @t])`lcase [m n]) + :: lcase (snoc `(list [@t @t])`lcase ['a' 'b']) + i +(i) + == + :: blocks + :: %.n +-- +``` diff --git a/content/guides/additional/workbook/competitive.md b/content/guides/additional/workbook/competitive.md index ff4ef0d..3c46881 100644 --- a/content/guides/additional/workbook/competitive.md +++ b/content/guides/additional/workbook/competitive.md @@ -3,11 +3,9 @@ title = "Competitive Programming" weight = 10 +++ -# Competitive Programming - Let's take a quick look at implementations for some common tasks in Hoon. I assume that you want to use library tools whenever possible, but you can delve into the source code itself if you want to know more. -### Sorting +## Sorting - Given a `list` of values, sort the values according to a given rule and produce a `list`. @@ -44,7 +42,7 @@ If something isn't a `list`, the easiest way to sort it is to convert it to a `l ~[1 2 3 4 5 6 7 8 9 10] ``` -### Bitwise Operations +## Bitwise Operations Bitwise operations are necessary when you are closely packing data into binary formats or otherwise dealing with binary data. Basically there are three scenarios: @@ -52,7 +50,7 @@ Bitwise operations are necessary when you are closely packing data into binary f 2. Working with MIME-type data. Urbit has standard library support for yielding and parsing these, but it's sometimes a bit confusing where they may be located. 3. Directly processing particular kinds of data streams, like audio or video data. On Urbit, you'll be serving these or interfacing with an external service. (Remember, Urbit is more like a server than a GPU.) -#### Binary Operations +### Binary Operations If you are working with packed binary data, you'll typically print the atom data with a `@ux` unsigned hexadecimal aura. @@ -153,7 +151,7 @@ Standard bitwise logical operations are available: 0b1111.1111.1111.0100 ``` -#### MIME Data Operations +### MIME Data Operations [MIME data types](https://en.wikipedia.org/wiki/MIME) allow HTTP communications to include rich content. The `++html` core in the standard library provides quite a few operations for encoding and decoding MIME-typed values. @@ -187,7 +185,7 @@ There are operations for JSON, [Base58](https://en.wikipedia.org/wiki/Binary-to- Urbit furthermore has a notion of _jammed noun_, which is essentially a serialization (marshaling, pickling) of a noun into an atom. -### Primes and Factors +## Primes and Factors To calculate a prime number, the tried-and-true method is the Sieve of Eratosthenes, which is an elimination algorithm. In other words, we need to be able to calculate factors of a given positive integer. We're actually going to do an even simpler (and less efficient) method here, and leave the Sieve as an exercise to the reader. @@ -234,7 +232,7 @@ Now that we can find factors, it should be straightforward to find primes. In t - How would you change this algorithm to the more efficient Sieve of Eratosthenes? -### Pragmatic Input/Output +## Pragmatic Input/Output While Hoon has a sophisticated text parsing library, the primitives are rather low-level and we won't assume that you want to directly implement a parser using them in a rapid-fire competitive environment. @@ -245,11 +243,11 @@ Fortunately, there is a regular expression library you can incorporate into your - https://github.com/lynko/re.hoon -### Functional Operators +## Functional Operators Hoon is a functional programming language, so naturally it supports the basic collective operations which functional programming languages expect. -#### Curry +### Curry [_Currying_](https://en.wikipedia.org/wiki/Currying) describes taking a function of multiple arguments and reducing it to a set of functions that each take only one argument. _Binding_, an allied process, is used to set the value of some of those arguments permanently. Hoon has a left-bind `++cury` and a right-bind `++curr`. @@ -265,7 +263,7 @@ Hoon is a functional programming language, so naturally it supports the basic co 117 ``` -#### Map +### Map The Map operation describes applying a function to each item of a set or iterable object, resulting in the same final number of items transformed. In Hoon terms, we would say slamming a gate on each member of a `list` or `set`. The standard library arms that accomplish this include [`++turn`](https://developers.urbit.org/reference/hoon/stdlib/2b#turn) for a `list`, [`++run:in`](https://developers.urbit.org/reference/hoon/stdlib/2h#repin) for a `set`, and [`++run:by`](https://developers.urbit.org/reference/hoon/stdlib/2i#runby) for a `map`. @@ -273,7 +271,7 @@ The Map operation describes applying a function to each item of a set or iterabl > (turn `(list @ud)`~[1 2 3 4 5] one) ``` -#### Reduce +### Reduce The Reduce operation applies a function as a sequence of pairwise operations to each item, resulting in one summary value. The standard library arms that accomplish this are [`++roll`](https://developers.urbit.org/reference/hoon/stdlib/2b#roll) and [`++reel`](https://developers.urbit.org/reference/hoon/stdlib/2b#reel) for a `list`, [`++rep:in`](https://developers.urbit.org/reference/hoon/stdlib/2h#repin) for a `set`, and [`++rep:by`](https://developers.urbit.org/reference/hoon/stdlib/2i#repby) for a `map`. @@ -284,7 +282,7 @@ The Reduce operation applies a function as a sequence of pairwise operations to b=120 ``` -#### Filter +### Filter The Filter operation applies a true/false function to each member of a collection, resulting in some number of items equal to or fewer than the size of the original set. In Hoon, the library arms that carry this out include [`++skim`](https://developers.urbit.org/reference/hoon/stdlib/2b#skim), [`++skid`](https://developers.urbit.org/reference/hoon/stdlib/2b#skid), [`++murn`](https://developers.urbit.org/reference/hoon/stdlib/2b#murn) for a `list`, and [`++rib:by`](https://developers.urbit.org/reference/hoon/stdlib/2i#ribby) for a `map`. @@ -297,7 +295,7 @@ An interesting feature of Hoon is that it really prefers functions-that-produce- - [Functional Programming](https://developers.urbit.org/guides/core/hoon-school/Q-func) - This module will discuss some gates-that-work-on-gates and other assorted operators that are commonly recognized as functional programming tools. -### Floating-Point Operations +## Floating-Point Operations `@ud` unsigned decimal operations are, of course, postive-integer-only. For floating-point maths, you will need to work with `@rs` for 32-bit single-precision IEEE 754 floating-point atoms. These are prefixed with a single `.` which is _not_ a decimal point. @@ -332,7 +330,7 @@ Equivalent mathematical operations for `@rs` values are available in the `++rs` (I picked the above set of examples after perusing the excellent book [Antti Laaksonen (2017) _Guide to Competitive Programming: Learning and Improving Algorithms Through Contests_](https://link.springer.com/book/10.1007/978-3-319-72547-5).) -### Debugging and Troubleshooting +## Debugging and Troubleshooting Static typing with compile-time type checking turns out to be a secret strength of Hoon. Once you've satisfied the typechecker, your code is often surprisingly free of bugs (compared to e.g. Python). diff --git a/content/guides/additional/workbook/gleichniszahlenreihe.md b/content/guides/additional/workbook/gleichniszahlenreihe.md index d2481f6..8dbcc12 100644 --- a/content/guides/additional/workbook/gleichniszahlenreihe.md +++ b/content/guides/additional/workbook/gleichniszahlenreihe.md @@ -3,7 +3,7 @@ title = "Gleichniszahlenreihe" weight = 30 +++ -# Challenge: The Look-and-Say Sequence +## Challenge: The Look-and-Say Sequence _Gleichniszahlenreihe_, or the [look-and-say sequence](https://en.wikipedia.org/wiki/Look-and-say_sequence), is constructed from an aural description of a sequence of numbers. @@ -13,7 +13,7 @@ This is a fairly complicated program. You need a few parts: the ability to tak - Compose a `%say` generator which carries out the look-and-say sequence calculation for a given input. The input should be a number which indicates which value in the sequence is desired (e.g. 1→1, 2→11, 3→21). -## Solutions +## Solutions _These solutions were submitted by the Urbit community as part of the Hoon School Live ~2022.2 cohort. They are made available under both the [MIT license](https://mit-license.org/) and the [CC0 license](https://creativecommons.org/share-your-work/public-domain/cc0). We ask you to acknowledge authorship should you utilize these elsewhere._ diff --git a/content/guides/additional/workbook/luhn-number.md b/content/guides/additional/workbook/luhn-number.md new file mode 100644 index 0000000..52ec170 --- /dev/null +++ b/content/guides/additional/workbook/luhn-number.md @@ -0,0 +1,574 @@ ++++ +title = "Luhn Number" +weight = 128 ++++ + +## Challenge: Luhn Number + +The Luhn test is used by some credit card companies to distinguish valid credit card numbers from what could be a random selection of digits. + +A Luhn number is a sequence of digits that passes the following test: + +1. Reverse the order of the digits. +2. Take the first, third, and every odd-numbered digit in the reversed digits and sum them to form `s1` +3. Taking the second, fourth, and every even-numbered digit in the reversed digits: + 1. Multiply each by two. Within each doubled digit, sum those digits (if the answer is greater than nine) to form partial sums. + 2. Sum the partial sums of the even digits to form `s2` +4. If `s1 + s2` ends in zero then the original number is in the form of a valid credit card number as verified by the Luhn test. + +For example, if the trial number is 49927398716: + +``` +Reverse the digits: + 61789372994 +Sum the odd digits: + 6 + 7 + 9 + 7 + 9 + 4 = 42 = s1 +The even digits: + 1, 8, 3, 2, 9 + Two times each even digit: + 2, 16, 6, 4, 18 + Sum the digits of each multiplication: + 2, 7, 6, 4, 9 + Sum the last: + 2 + 7 + 6 + 4 + 9 = 28 = s2 + +s1 + s2 = 70 ends in zero, which means that 49927398716 passes the Luhn test +``` + +Your task for this challenge is as follows. First you will write a library file `lib/luhn-number` with a core containing an arm named `++validate`. `validate` will be a gate that takes as input a `tape` which is a sequence of digits, and returns either a `%.y` or `%.n` if the number is a Luhn number or not. + +Example usage: +``` +> =ln -build-file %/lib/luhn-number/hoon +> (validate:ln "49927398716") +%.y +> (validate:ln "1234") +%.n +``` + +Next you will write a generator file `gen/luhn-number` which takes as input a `tape` which consists of digits or the `*` character, such as: +``` +"*1*25**574*18403" +"****" +"584" +``` + +It will return a `(list tape)` which contains all of the Luhn numbers that fit that format. The numbers should be in lexicographic order (smallest to largest by first digit, then second digit, and so on). You may choose to import and use your `++validate` arm, or perhaps use some other strategy. + +Example usage: +``` +> +luhn-number "**123" +["01123" "15123" "20123" "39123" "44123" "58123" "63123" "77123" "82123" "96123" ~] + +> +luhn-number "123" +~ + +> +luhn-number "49927398716" +[49927398716 ~] +``` + +Some notes: +* We take the input as a `tape` rather than a `@ud` because a potential credit card number can have leading zeros. + +* Note that in Hoon, we index starting from 0 -- so the first digit will be in the 0th index, second in 1st index, and so on. + +* This website may be of use for both checking if a number is Luhn and generating a list from missing digits: https://www.dcode.fr/luhn-algorithm + +* Don't worry about numbers with less than 2 digits, or improperly formatted input (with letters and spaces etc.). You can assume that the input tape will have the correct format. + +## Unit Tests + +Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior. + +```hoon +/+ *test +/+ ln=luhn-number +/= luhn-number /gen/luhn-number +|% +:: test valid numbers +:: +++ test-01 + %+ expect-eq + !> %.y + !> (validate:ln "49927398716") +++ test-02 + %+ expect-eq + !> %.y + !> (validate:ln "1234567812345670") +++ test-03 + %+ expect-eq + !> %.y + !> (validate:ln "4417123456789105") +++ test-04 + %+ expect-eq + !> %.y + !> (validate:ln "20210917131347022") +++ test-05 + %+ expect-eq + !> %.y + !> (validate:ln "1806040794512") +++ test-06 + %+ expect-eq + !> %.y + !> (validate:ln "9856849794512") +++ test-07 + %+ expect-eq + !> %.y + !> (validate:ln "5995841300227") +++ test-08 + %+ expect-eq + !> %.y + !> (validate:ln "00") +++ test-09 + %+ expect-eq + !> %.y + !> (validate:ln "34") +++ test-10 + %+ expect-eq + !> %.y + !> (validate:ln "00005991") +++ test-11 + %+ expect-eq + !> %.y + !> (validate:ln "02310568590238405") +:: test invalid numbers +:: +++ test-12 + %+ expect-eq + !> %.n + !> (validate:ln "1234") +++ test-13 + %+ expect-eq + !> %.n + !> (validate:ln "92") +++ test-14 + %+ expect-eq + !> %.n + !> (validate:ln "00001463") +++ test-15 + %+ expect-eq + !> %.n + !> (validate:ln "754717798") +++ test-16 + %+ expect-eq + !> %.n + !> (validate:ln "507274573") +++ test-17 + %+ expect-eq + !> %.n + !> (validate:ln "2342352356198234238") +++ test-18 + %+ expect-eq + !> %.n + !> (validate:ln "02310568590238406") +++ test-19 + %+ expect-eq + !> %.n + !> (validate:ln "5019876543217144") +++ test-20 + %+ expect-eq + !> %.n + !> (validate:ln "220743131719012023") +:: test number generation +:: +++ test-21 + %+ expect-eq + !> `(list tape)`["01123" "15123" "20123" "39123" "44123" "58123" "63123" "77123" "82123" "96123" ~] + !> (luhn-number "**123") +++ test-22 + %+ expect-eq + !> `(list tape)`~ + !> (luhn-number "123") +++ test-23 + %+ expect-eq + !> `(list tape)`["12345690" ~] + !> (luhn-number "12345690") +++ test-24 + %+ expect-eq + !> `(list tape)`["023259872" "123259871" "223259870" "323259879" "423259878" "523259877" "623259876" "723259875" "823259874" "923259873" ~] + !> (luhn-number "*2325987*") +++ test-25 + %+ expect-eq + !> `(list tape)`["845927593912820" ~] + !> (luhn-number "8459275*3912820") +++ test-26 + %+ expect-eq + !> `(list tape)`["00" "18" "26" "34" "42" "59" "67" "75" "83" "91" ~] + !> (luhn-number "**") +++ test-27 + %+ expect-eq + !> `(list tape)`["4002" "4192" "4242" "4382" "4432" "4572" "4622" "4762" "4812" "4952" ~] + !> (luhn-number "4**2") +++ test-28 + %+ expect-eq + !> `(list tape)`["10017" "10157" "10207" "10397" "10447" "10587" "10637" "10777" "10827" "10967" "11007" "11197" "11247" "11387" "11437" "11577" "11627" "11767" "11817" "11957" "12047" "12187" "12237" "12377" "12427" "12567" "12617" "12757" "12807" "12997" "13037" "13177" "13227" "13367" "13417" "13557" "13607" "13797" "13847" "13987" "14027" "14167" "14217" "14357" "14407" "14597" "14647" "14787" "14837" "14977" "15057" "15107" "15297" "15347" "15487" "15537" "15677" "15727" "15867" "15917" "16097" "16147" "16287" "16337" "16477" "16527" "16667" "16717" "16857" "16907" "17087" "17137" "17277" "17327" "17467" "17517" "17657" "17707" "17897" "17947" "18077" "18127" "18267" "18317" "18457" "18507" "18697" "18747" "18887" "18937" "19067" "19117" "19257" "19307" "19497" "19547" "19687" "19737" "19877" "19927" ~] + !> (luhn-number "1***7") +-- +``` + +## Solutions + +_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere._ + +### Solution #1 + +_By ~dozreg-toplud._ + +`lib/luhn-number.hoon` +```hoon +:: lib/luhn-number.hoon +:: Library for HSL challenge #3 +:: +|% +:: ++validate: gate defined in the challenge +:: +++ validate + |= a=tape + ^- ? + =. a (flop a) + =/ a-digits=(list @) (turn a (cury slav %ud)) + =/ s1=@ (roll (skim-odd a-digits) add) + =; s2=@ + =(0 (mod (add s1 s2) 10)) + %+ roll + (skim-even a-digits) + :: (gate that adds digits of 2*i to the accumulator) + :: + |= [i=@ acc=@] + =+ i2=(mul i 2) + :(add acc (div i2 10) (mod i2 10)) +:: ++skim-odd: return elements of a list with odd indices (1-indexed) +:: +++ skim-odd + |* a=(list) + ^+ a + ?~ a + ~ + ?~ t.a + ~[i.a] + [i.a $(a t.t.a)] +:: ++skim-even: return elements of a list with even indices (1-indexed) +:: +++ skim-even + |* a=(list) + ^+ a + ?: |(?=(~ a) ?=(~ t.a)) + ~ + [i.t.a $(a t.t.a)] +:: +-- +``` + +`gen/luhn-number.hoon` +```hoon +:: gen/luhn-number.hoon +:: Naked generator for HSL challenge #3 +:: +/+ *luhn-number +:: +|= a=tape +^- (list tape) +=* this-gate ..$ +=/ index-tar=(unit @) (find "*" a) +:: if no * in `a`, +:: +?~ index-tar + :: then return empty list if `a` is not a Luhn number, else return list + :: with `a` + :: + ?. (validate a) + ~ + ~[a] +:: else, replace first * with a digit, call this gate for each digit 0-9, +:: weld the results +:: +=/ dry-snap ^~((bake snap ,[tape @ char])) +%- zing +%+ turn + "0123456789" +(cork (cury (cury dry-snap a) u.index-tar) this-gate) +``` + +### Solution #2 + +_By ~pardun-nollev._ + +`lib/luhn-number.hoon` +```hoon +|% +++ validate + |= input=tape + =/ input (flop input) + =(0 (mod (add (get-s1 input) (get-s2 input)) 10)) +++ get-s1 + |= input=tape + ^- @ud + (roll (odd-digits input) add) + ++ get-s2 + |= input=tape + ^- @ud + (roll (multiply-digits (convert-digits-to-text (double-digits input))) add) +:: take a tape +:: convert to @ud and sum + ++ sum-digits + |= input=tape + ^- @ud + (roll (turn input |=(a=@t (rash a dem))) add) +:: take list of tapes +:: convert to digits +:: multiply each list of digits + ++ multiply-digits + |= input=(list tape) + ^- (list @ud) + (turn input |=(a=tape (sum-digits a))) +:: take each number +:: convert to tape +++ convert-digits-to-text + |= input=(list @ud) + ^- (list tape) + (turn input |=(a=@ud (scow %ud a))) +:: get even digits and multiply by two +++ double-digits + |= input=tape + ^- (list @ud) + (turn (even-digits input) |=(a=@ud (mul a 2))) +++ get-element + |= [idx=@ud input=tape] + ^- tape + ?: (gte (lent input) +(idx)) + `tape`~[(snag idx input)] + ~ +++ odd-digits + |= input=tape + ^- (list @ud) + =/ output=tape ~ + |- + ?: =(input ~) + (turn output |=(a=@t (rash a dem))) + %= $ + output (weld output (get-element 0 input)) + input (oust [0 2] input) + == +++ even-digits + |= input=tape + ^- (list @ud) + =/ output=tape ~ + |- + ?: =(input ~) + (turn output |=(a=@t (rash a dem))) + %= $ + output (weld output (get-element 1 input)) + input (oust [0 2] input) + == +-- +``` + +`gen/luhn-number.hoon` + +```hoon +/+ luhn-number +|= input=tape +=< +(skim `(list tape)`(get-permutations input) validate:luhn-number) +|% +++ digits "0123456789" +++ get-permutations + |= input=tape + =/ output=(list tape) ~[input] + =/ idx 0 + |- + ?: =(idx (lent input)) + output + %= $ + output (churn-numbers idx output) + idx +(idx) + == +++ churn-numbers + |= [idx=@ud input=(list tape)] + ^- (list tape) + (zing (turn input |=(a=tape (generate-perms idx a)))) +++ generate-perms + |= [idx=@ud input=tape] + ^- (list tape) + ?: =((snag idx input) '*') + (turn digits |=(a=@t (snap input idx a))) + ~[input] +-- +``` + +### Solution #3 + +_By ~motdeg-bintul_ + +`lib/luhn-number` + +```hoon +:: lib/luhn-number.hoon +:: Your code goes here +:: +|% +++ validate +|= a=tape +&(=((checkdits a) 0) (gth (lent a) 0)) +++ checkdits +|= a=tape +=/ totalsum (add (s1 a) (s2 a)) +=/ sumtape (trip `@t`(scot %ud totalsum)) +=/ digits (scan sumtape (star dit)) +:: ~& (odds a) +:: ~& (doubler a) +:: ~& `(list @)`(getsums (doubler a)) +:: ~& (s1 a) +:: ~& (s2 a) +:: ~& totalsum +?: (lte totalsum 9) + +2:digits +(snag (sub (lent +3:digits) 1) `(list @ud)`+3:digits) +++ odds +|= a=tape +=/ reverse (flop a) +=/ count 0 +|- +^- (list @ud) +|- +?: (gte count (lent reverse)) + ~ +:- (scan (trip (snag count reverse)) dit) +$(count (add 2 count)) +++ s1 +|= a=tape +(roll `(list @ud)`(odds a) add) +++ evens +|= a=tape +=/ reverse (flop a) +=/ count 1 +|- +^- (list @ud) +|- +?: (gte count (lent reverse)) + ~ +:- (scan (trip (snag count reverse)) dit) +$(count (add 2 count)) +++ double +|= [a=@] +(mul 2 a) +++ doubler +|= a=tape +(turn `(list @ud)`(evens a) double) +++ adddit +|= [a=(list @ud) b=@ud] +=/ list1 a +=/ list2 `(list @t)`(turn list1 (cury scot %ud)) +=/ count b +=/ digits (scan (trip (snag count list2)) (star dit)) +=/ d1 (snag 0 digits) +?: =((lent digits) 1) + `@ud`d1 +?: (gth (lent digits) 1) + `@ud`(add d1 (snag 1 digits)) +~ +++ getsums +|= a=(list @ud) +=/ nums a +=/ count 0 +|- +?: (lth count (lent nums)) +:- (adddit nums count) +$(count (add 1 count)) +?: =(count (lent nums)) + ~ +$(count (add 1 count)) +++ s2 +|= a=tape +=/ nums (doubler a) +(roll `(list @)`(getsums nums) add) +-- +``` + +`gen/luhn-number` + +```hoon +:: gen/luhn-number.hoon +:: Your code goes here +:: +/= ln /lib/luhn-number +|= a=tape +=< +(checkmissing a) +|% + ++ missingnums + |= a=tape + =/ count 0 + |- + ?: =(count (lent a)) + ~ + ?: =((snag count a) '*') + :- count + $(count (add 1 count)) + ?: =(count (sub (lent a) 1)) + ~ + $(count (add 1 count)) + ++ replaceast + |= a=tape + =/ pos `(list @)`(missingnums a) + =/ count 0 + =/ newtape a + =/ num `@t`(scot %ud 0) + |- + ?: =(count (sub (lent pos) 1)) + `(list @t)`(snap newtape (snag count pos) num) + %= $ + newtape (snap newtape (snag count pos) num) + count (add count 1) + == + ++ replacedigits + |= [a=tape b=@ud] + =/ count 0 + =/ dits (trip (crip (replaceast a))) + =/ newdits (a-co:co b) + =/ flopdits (flop newdits) + =/ indexcap (sub (lent flopdits) 1) + =/ pos (flop `(list @ud)`(missingnums a)) + =/ newnum `tape`dits + |- + ?: =(count (lent newdits)) + newnum + %= $ + newnum `tape`(snap newnum (snag count pos) (snag count flopdits)) + count (add 1 count) + == + ++ testnewnum + |= a=tape + =/ format a + =/ count 0 + =/ countdit (a-co:co count) + =/ newnum `tape`~ + =/ pos `(list @ud)`(missingnums format) + =/ dgtlent (lent pos) + |- + ^- (list tape) + ?: &(=((lent (a-co:co count)) (add 1 dgtlent)) =((validate:ln newnum) %.y)) + [newnum ~] + ?: =((lent (a-co:co count)) (add 1 dgtlent)) + ~ + ?: =((validate:ln newnum) %.y) + :- newnum + %= $ + count (add 1 count) + newnum (replacedigits format count) + countdit (a-co:co count) + == + ?: =((lent countdit) (add 1 dgtlent)) + ~ + %= $ + count (add 1 count) + newnum (replacedigits format count) + countdit (trip `@t`(scot %ud count)) + == + ++ checkmissing + |= a=tape + ?: &(=((missingnums a) ~) =((validate:ln a) %.y)) + `(list tape)`[a ~] + (testnewnum a) +-- +``` diff --git a/content/guides/additional/workbook/rhonda.md b/content/guides/additional/workbook/rhonda.md index b70fb8e..310f736 100644 --- a/content/guides/additional/workbook/rhonda.md +++ b/content/guides/additional/workbook/rhonda.md @@ -3,7 +3,7 @@ title = "Rhonda Numbers" weight = 48 +++ -# Challenge: Rhonda Numbers +## Challenge: Rhonda Numbers A Rhonda number is a positive integer _n_ that satisfies the property that, for [a given base _b_](https://en.wikipedia.org/wiki/Radix), the product of the base-_b_ digits of _n_ is equal to _b_ times the sum of _n_'s prime factors. Only composite bases (non-prime bases) have Rhonda numbers. diff --git a/content/guides/additional/workbook/roman.md b/content/guides/additional/workbook/roman.md index 003a563..44cc717 100644 --- a/content/guides/additional/workbook/roman.md +++ b/content/guides/additional/workbook/roman.md @@ -3,7 +3,7 @@ title = "Roman Numerals" weight = 50 +++ -# Challenge: Printing and Parsing Roman Numerals +## Challenge: Printing and Parsing Roman Numerals Roman numerals constitute a numeral system capable of expressing positive integers by additive values (rather than place-number notation). Additive series are produced by summing values in a series, as `iii` → 3, while subtractive values are produced by prepending certain smaller values ahead of a larger value, as `ix` → 9. @@ -36,7 +36,7 @@ Roman numerals constitute a numeral system capable of expressing positive intege **Note**: This design pattern is not optimal since analysis over a union of some types can be difficult to carry out, and it would be better to either separate the generators or use a flag. In this case, the pattern works because we are distinguishing an atom from a cell. -## Unit Tests +## Unit Tests Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior. @@ -866,7 +866,7 @@ Following a principle of test-driven development, we compose a series of tests w -- ``` -## Solutions +## Solutions _These solutions were submitted by the Urbit community as part of a competition in ~2022.6. They are made available under both the [MIT license](https://mit-license.org/) and the [CC0 license](https://creativecommons.org/share-your-work/public-domain/cc0). We ask you to acknowledge authorship should you utilize these elsewhere._ diff --git a/content/guides/additional/workbook/solitaire.md b/content/guides/additional/workbook/solitaire.md index df8a41b..ed2a5da 100644 --- a/content/guides/additional/workbook/solitaire.md +++ b/content/guides/additional/workbook/solitaire.md @@ -3,9 +3,7 @@ title = "Solitaire Cipher" weight = 60 +++ -# Solitaire Cipher - -## Challenge: Solitaire Encryption Cipher +## Challenge: Solitaire Encryption Cipher The [Solitaire or Pontifex algorithm](https://en.wikipedia.org/wiki/Solitaire_%28cipher%29) is a cryptographic algorithm designed by cryptographer [Bruce Schneier](https://www.schneier.com/academic/solitaire/) based on coordinating two decks of cards so that they can be used to communicate between two field agents. Given a standard deck of 52 playing cards and two distinguishable jokers, a message may be encrypted as a keystream, or sequence of values combined with the message to encrypt or decrypt it. The algorithm features prominently in Neal Stephenson's novel _Cryptonomicon_. @@ -43,7 +41,7 @@ To generate one character: The foregoing hyperlinks showcase worked examples of Solitaire in action. -## Solutions +## Solutions _This solution was produced by ~rabsef-bicrym. It is made available under the [GNU GPL](https://github.com/rabsef-bicrym/urbitasofia/blob/master/LICENSE). (Note that this is different from the other code snippets on this site, which are made available under the [MIT license](https://mit-license.org/)._ diff --git a/content/guides/additional/workbook/water-towers.md b/content/guides/additional/workbook/water-towers.md new file mode 100644 index 0000000..aaa236e --- /dev/null +++ b/content/guides/additional/workbook/water-towers.md @@ -0,0 +1,381 @@ ++++ +title = "Water Towers" +weight = 230 ++++ + +## Challenge: Water between Towers + +In a two-dimensional world, we begin with a bar-chart, or rows of unit-width 'towers' of arbitrary height. Then it rains, completely filling all convex enclosures in the chart with water. + +``` +9 ██ 9 ██ +8 ██ 8 ██ +7 ██ ██ 7 ██≈≈≈≈≈≈≈≈██ +6 ██ ██ ██ 6 ██≈≈██≈≈≈≈██ +5 ██ ██ ██ ████ 5 ██≈≈██≈≈██≈≈████ +4 ██ ██ ████████ 4 ██≈≈██≈≈████████ +3 ██████ ████████ 3 ██████≈≈████████ +2 ████████████████ ██ 2 ████████████████≈≈██ +1 ████████████████████ 1 ████████████████████ +``` + +Your task for this challenge is to write a generator `water-towers`. It will take as input a `(list @ud)`, with each number representing the height of a tower from left to right. It will output a `@ud` representing the units of water that can be contained within the structure. + +Example usage: +``` +> +water-towers [5 3 7 2 6 4 5 9 1 2 ~] +14 +``` + +## Unit Tests + +Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior. + +```hoon +/+ *test +/= water-towers /gen/water-towers +|% +++ test-01 + %+ expect-eq + !> `@ud`2 + !> (water-towers [1 5 3 7 2 ~]) +++ test-02 + %+ expect-eq + !> `@ud`14 + !> (water-towers [5 3 7 2 6 4 5 9 1 2 ~]) +++ test-03 + %+ expect-eq + !> `@ud`35 + !> (water-towers [2 6 3 5 2 8 1 4 2 2 5 3 5 7 4 1 ~]) +++ test-04 + %+ expect-eq + !> `@ud`0 + !> (water-towers [5 5 5 5 ~]) +++ test-05 + %+ expect-eq + !> `@ud`0 + !> (water-towers [5 6 7 8 ~]) +++ test-06 + %+ expect-eq + !> `@ud`0 + !> (water-towers [8 7 7 6 5 4 3 2 ~]) +++ test-07 + %+ expect-eq + !> `@ud`0 + !> (water-towers [0 1 6 7 10 7 6 1 0 ~]) +++ test-08 + %+ expect-eq + !> `@ud`0 + !> (water-towers [100 0 0 0 0 0 0 0 ~]) +++ test-09 + %+ expect-eq + !> `@ud`7 + !> (water-towers [100 0 0 0 0 0 0 0 1 ~]) +++ test-10 + %+ expect-eq + !> `@ud`50 + !> (water-towers [10 0 0 0 0 0 10 ~]) +++ test-11 + %+ expect-eq + !> `@ud`4 + !> (water-towers [8 7 8 7 8 7 8 7 8 ~]) +++ test-12 + %+ expect-eq + !> `@ud`40 + !> (water-towers [0 1 2 3 4 5 4 3 2 1 1 2 3 4 5 4 3 2 1 1 2 3 4 5 4 3 2 1 0 ~]) +-- +``` + +## Solutions + +_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere._ + +### Solution #1 + +_By ~dannul-bortux. A model for literate programming in Hoon._ + +```hoon +:: +:: A gate for computing volume of water collected between towers. +:: +:: Take a list (of type list @ud), with each value representing the height of +:: a tower from left to right. Outputs a @ud representing the units of water +:: that can be contained within the structure. +:: +:: Our approach involves calculating the total volume of rainfall or water by +:: aggregating the water volume from each tower location. For a specific +:: tower location. water volume is determined by subtracting the “height” +:: of the tower with maximum rainfall (“total height with water”) from the +:: height of the tower alone. Tower heights are given by corresponding values +:: in the input list. +:: +:: The “total height with water” at a location is determined by the height of +:: surrounding boundary towers within our structure. Each tower location will +:: have at most two boundary towers: one boundary tower on either side (left +:: and right). The left boundary tower is defined as the highest tower to the +:: left of our specified tower location. The right boundary tower is defined +:: as the highest tower to the right of our specified tower location. The +:: value of “total height with water” at a location is equal to the lesser of +:: the two boundary tower heights (the minimum of the left boundary tower +:: height vs. right boundary tower height). When less than two boundary +:: towers are present, the “total height with water” is equal to the height +:: of the tower itself because no water can be contained without boundaries. +:: +|= inlist=(list @ud) +^- @ud +:: If, input list is empty +:: +?: =(0 (lent inlist)) + :: Then, throw error + :: + ~| 'Error - input list cannot be empty' + !! +=< (compute-totalvol inlist) +|% +:: +:: +compute-totalvol: Gets total volume of water by summing water at each +:: individual location. +:: +:: Moves left to right iterating over each location (index in list). +:: Determines waterfall at each location and aggregates all waterfall to +:: find and return total volume. +:: +++ compute-totalvol + |= [n=(list @ud)] + ^- @ud + :: i is face for iterating over all index/locations + :: + =/ i 0 + :: tot is face for aggregating volume of water + :: + =/ tot 0 + |- + :: If, we're at end of input list + :: + ?: =(i (lent n)) + :: then, return total + :: + tot + :: else, compute water volume at current index, add to total, and increment i + :: + %= $ + tot (add tot (compute-indvol i n)) + i +(i) + == +:: +:: +compute-indvol: Computes volume at an individual location. +:: +:: Computes volume at an individual location (index in input list) by +:: subtracting tower height from “total height with water”. “Total height +:: with water” will be determined at a particular location by the height of +:: “boundary towers” for that location. +:: +++ compute-indvol + |= [loc=@ud n=(list @ud)] + ^- @ud + (sub (compute-waterheight loc n) (snag loc `(list @ud)`n)) +:: +:: +compute-waterheight: Measures the “total height with water” at a specified +:: index/location. +:: +:: “Total height with water” at a particular location is measured using the +:: heights (value) at the left and right boundary towers. The lesser of these +:: two values (left height vs right height) is equal to the “total height +:: with water” at our input location. +:: +:: Right boundary tower is the tallest tower to the right of the location-- +:: i.e. highest value (height) with higher index. The left boundary tower is +:: the tallest tower to the left of the location i.e. highest value (height) +:: with lower index. +:: +:: The “find-boundaryheight” arm iterates left to right and works for +:: measuring height of the right boundary tower. For the left boundary tower +:: we can use a mirror approach. We reverse the input list and adjust the +:: input index accordinglyto move right-to-left. +:: +:: In the case where no right or left boundary tower exists, our +:: “find-boundaryheight” arm will return the tower height at our current +:: index (indicating no water present) and we correctly compute 0 water +:: volume in our compute-indvol arm. +:: +++ compute-waterheight + |= [loc=@ud n=(list @ud)] + ^- @ud + :: rbth is a face for our "right boundary tower height" computed using our + :: "find-boundaryheight" arm moving left to right + :: + =/ rbth (find-boundaryheight loc n) + :: lbth is a face for our "right boundary tower height" computed using our + :: "find-boundaryheight" arm moving (mirrored) right to left + :: + =/ lbth (find-boundaryheight (sub (lent n) +(loc)) (flop n)) + :: If, right boundary tower height is less than left boundary tower height, + :: + ?: (lth rbth lbth) + :: then, return right boundary tower height + :: + rbth + :: else, return left boundary tower height + :: + lbth +:: +:: +find-boundaryheight: Computes the height of the highest tower to the right +:: of the input location +:: +:: Moves left to right starting at input location until the end of input +:: list. Tracks height of each tower location with a height greater than +:: height at corresponding input location. +:: +++ find-boundaryheight + |= [loc=@ud n=(list @ud)] + ^- @ud + :: i is face used to iterate over input list starting one past input index + :: + =/ i +(loc) + :: bheight is face used to measure boundary tower heights--i.e. any tower + :: heights greater than height at input location. At start, bheight is set to + :: input location height. If no greater heights are found, input location + :: height is returned (indicating no higher boundary towers found). + :: + =/ bheight (snag loc n) + |- + :: If, we are at the end of our input + :: + ?: (gte i (lent n)) + :: then, return boundary tower height + :: + bheight + :: else, if current tower height is greater than currently stored boundary + :: tower height, replace boundary tower height. Incr iteration idx. + :: + %= $ + bheight ?: (gth (snag i n) bheight) + (snag i n) + bheight + i +(i) + == +-- +``` + + + +### Solution #2 +_By ~racfer-hattes. A short and elegant solution._ + +```hoon +=> +|% +++ go + |= [current=@ud previous=(list @ud) next=(list @ud)] + =/ left-peak (roll previous max) + =/ right-peak (roll next max) + =/ min-peak (min left-peak right-peak) + =/ water-level + ?: (lth min-peak current) 0 + (sub min-peak current) + ?~ next water-level + (add water-level $(current i.next, next t.next, previous [current previous])) +-- +|= xs=(list @ud) +?~ xs 0 +%- go [i.xs ~ t.xs] +``` + +### Solution #3 +_By ~dozreg-toplud. Another very literate and clean solution._ + + +```hoon +:: +water-towers: a solution to the HSL challenge #1 +:: +:: https://github.com/tamlut-modnys/template-hsl-water-towers +:: Takes a (list @ud) of tower heights, returns the number of the units of +:: water that can be held in the given structure. +:: +|= towers=(list @ud) +^- @ud +=< +:: x, y are horizontal and vertical axes +:: +=| water-counter=@ud +=/ x-last-tower=@ud (dec (lent towers)) +=/ y-highest-tower=@ud (roll towers max) +:: iterate along y axis from y=0 +:: +=/ y=@ud 0 +|- +^- @ud +:: if, y > max(towers) +:: +?: (gth y y-highest-tower) + :: then, return water-counter + :: + water-counter +:: else, iterate along x axis from x=1 +:: +=/ x=@ud 1 +|- +^- @ud +:: if, x = x(last tower) +:: +?: =(x x-last-tower) + :: then, go to the next y + :: + ^$(y +(y)) +:: else, increment water-counter if the point [x y] is not occupied by a tower +:: and has towers to the left and right on the same y, after go to the next x +:: +=? water-counter ?& (not-tower x y) + (has-tower-left x y) + (has-tower-right x y) + == + +(water-counter) +$(x +(x)) +:: +:: Core with helping functions +:: +|% +:: ++not-tower: returns %.y if the coordinate [x y] is free from a tower, +:: %.n if occupied. +:: +++ not-tower + |= [x=@ud y=@ud] + ^- ? + (gth y (snag x towers)) +:: ++has-tower-left: returns %.y if there is a tower with height >= y to +:: the left from x, %.n otherwise. Enabled computation caching to only test +:: each point once. +:: +++ has-tower-left + |= [x=@ud y=@ud] + ~+ + ^- ? + :: no towers to the left from the 0th tower + :: + ?: =(x 0) + %.n + :: check recursively to the left + :: + ?| (gte (snag (dec x) towers) y) + $(x (dec x)) + == +:: ++has-tower-right: returns %.y if there is a tower with height >= y to +:: the right from x, %.n otherwise. Enabled computation caching to only test +:: each point once. +:: +++ has-tower-right + |= [x=@ud y=@ud] + ~+ + ^- ? + :: no towers to the right from the last tower + :: + ?: =(x (dec (lent towers))) + %.n + :: check recursively to the right + :: + ?| (gte (snag +(x) towers) y) + $(x +(x)) + == +:: +-- +``` diff --git a/content/guides/core/app-school-full-stack/1-types.md b/content/guides/core/app-school-full-stack/1-types.md index 6ea5465..d714cc2 100644 --- a/content/guides/core/app-school-full-stack/1-types.md +++ b/content/guides/core/app-school-full-stack/1-types.md @@ -14,7 +14,7 @@ The best place to start when building a new agent is its type definitions in its Let's look at each of these questions in turn, and put together our agent's `/sur` file, which we'll call `/sur/journal.hoon`. -### 1. Basic types +## 1. Basic types Our journal entries will just be plain text, so a simple `@t` will work fine to store their contents. Entries will be organized by date, so we'll also need to @@ -34,7 +34,7 @@ The structure for a journal entry can therefore be: +$ entry [=id =txt] ``` -### 2. Actions +## 2. Actions Now that we know what a journal entry looks like, we can think about what kind of actions/commands our agent will handle in its `++on-poke` arm. For our @@ -54,7 +54,7 @@ We can create a tagged union structure for these actions, like so: == ``` -### 3. Updates +## 3. Updates Updates are a little more complicated than our actions. Firstly, our front-end needs to be able to retrieve an initial list of journal entries to display. Once @@ -113,7 +113,7 @@ milliseconds since the Unix Epoch: == ``` -### 4. State +## 4. State We need to store two things in our state: the journal entries and the update log. We could just use a couple of `map`s like so: diff --git a/content/guides/core/app-school-full-stack/3-json.md b/content/guides/core/app-school-full-stack/3-json.md index bd3253a..7d70221 100644 --- a/content/guides/core/app-school-full-stack/3-json.md +++ b/content/guides/core/app-school-full-stack/3-json.md @@ -8,7 +8,7 @@ this section, we'll briefly look at how JSON works in Urbit, and write a library to convert our agent's structures to and from JSON for our front-end. JSON data comes into Eyre as a string, and Eyre parses it with the -[`++de-json:html`](/reference/hoon/zuse/2e_2-3#de-jsonhtml) function in +[`++de:json:html`](/reference/hoon/zuse/2e_2-3#dejsonhtml) function in [`zuse.hoon`](/reference/hoon/zuse). The hoon type it's parsed to is `$json`, which is defined as: @@ -29,7 +29,7 @@ agent (unless the mark specified is already `%json`, in which case it will be delivered directly). Outbound facts will go through the same process in reverse - converted from the agent's native mark to `$json`, then encoded in a string by Eyre using -[`++en-json:html`](/reference/hoon/zuse/2e_2-3#en-jsonhtml) and delivered +[`++en:json:html`](/reference/hoon/zuse/2e_2-3#enjsonhtml) and delivered to the web client. The basic flow for both inbound messages (pokes) and outbound messages (facts and scry results) looks like this: @@ -171,7 +171,7 @@ complex nested `$json` decoding function can be built up in this manner. For example: ``` -> =js %- need %- de-json:html +> =js %- need %- de:json:html ''' { "foo": "hello", diff --git a/content/guides/core/app-school-full-stack/6-react-setup.md b/content/guides/core/app-school-full-stack/6-react-setup.md index 9f86def..25028ea 100644 --- a/content/guides/core/app-school-full-stack/6-react-setup.md +++ b/content/guides/core/app-school-full-stack/6-react-setup.md @@ -10,175 +10,113 @@ React app front-end. Node.js must be installed, and can be downloaded from their [website](https://nodejs.org/en/download). With that installed, we'll have the -`npm` package manager available. The first thing we'll do is globally install -the `create-react-app` package with the following command: +`npm` package manager available and its utility binaries like `npx` to help +set up our project. The first thing we'll do is create a project using the +[`create-landscape-app`](https://www.npmjs.com/package/@urbit/create-landscape-app) +template with the following command: ```sh -npm install -g create-react-app -``` - -Once installed, we can use it to create a new `journal-ui` directory and setup a -new React app in it with the following command: - -```sh -create-react-app journal-ui +npx @urbit/create-landscape-app +✔ What should we call your application? … journal +✔ What URL do you use to access Urbit? … http://127.0.0.1:8080 ``` We can then open our new directory: -```sh -cd journal-ui +```sh {% copy=true %} +cd journal/ui ``` Its contents should look something like this: ``` -journal-ui -├── node_modules +ui +├── index.html ├── package.json ├── package-lock.json -├── public -├── README.md +├── postcss.config.js +├── tailwind.config.js +├── vite.config.js └── src ``` -## Install `http-api` +## Install dependencies -Inside our React app directory, let's install the `@urbit/http-api` NPM package: +Inside our React app directory, let's install the NPM packages used by +our project: -```sh -npm i @urbit/http-api +```sh {% copy=true %} +npm i ``` -We also install a handful of other packages for the UI components -(`bootstrap@5.1.3 react-bootstrap@2.2.0 react-textarea-autosize@8.3.3 -date-fns@2.28.0 react-bottom-scroll-listener@5.0.0 react-day-picker@7.4.10`), -but that's not important to our purposes here. +This command will install the Urbit interface package (i.e. `@urbit/http-api`) +and all the other packages used by our React application. When building from +scratch with `create-landscape-app`, this includes a number of useful +development libraries that enable automatic refresh on file edits (i.e. `vite` +and `@vitejs/plugin-react-refresh`) and simple page styling (i.e. +`tailwindcss`). The remainder of this tutorial will focus primarily on how the +Urbit interface package is used to communicate with a live ship from within a +React application. -## Additional tweaks +## Basic app setup -Our front-end will be served directly from the ship by the `%docket` app, where -a user will open it by clicking on its homescreen tile. Docket serves such -front-ends with a base URL path of `/apps/[desk]/`, so in our case it will be -`/apps/journal`. In order for our app to be built with correct resource paths, -we must add the following line to `package.json`: +With all the basics now in place, we can begin work on the app itself. For this +simple demonstration, we'll be working just with the `src/app.jsx` file, which +contains the rendering logic for our React application. Before we look at the +full front-end source for our journal app, let's first review the simpler +default code provided by `create-landscape-app` to cover some Urbit API and +React basics. -```json -"homepage": "/apps/journal/", +### Urbit API setup + +First, let's open up `src/app.jsx` and look at the import statements at the top +of this file: + +```javascript +import React, { useEffect, useState } from 'react'; +import Urbit from '@urbit/http-api'; +import { scryCharges } from '@urbit/api'; +import { AppTile } from './components/AppTile'; ``` -Our app also needs to know the name of the ship it's being served from in order -to talk with it. The `%docket` agent serves a small file for this purpose at -`[host]/session.js`. This file is very simple and just contains: +The first two of these statements are very common in Urbit React applications; +the first imports the React library and a few of its important functions (to be +covered in a moment) and the second imports the `Urbit` class, which will be +used subsequently to enable browser-to-ship communication. -```js -window.ship = "sampel-palnet"; +Next, the code sets up the `Urbit` API object as a global variable, which +allows the browser-to-ship connection to be established *exactly once* when the +page is first being loaded: + +```javascript +const api = new Urbit('', '', window.desk); +api.ship = window.ship; ``` -`sampel-palnet` will of course be replaced by the actual name of the ship. We -include this script by adding the following line to the `` section of -`public/index.html`: - -``` - -``` - -## Basic API setup - -With everything now setup, we can begin work on the app itself. In this case -we'll just edit the existing `App.js` file in the `/src` directory. The first thing is to import the `Urbit` class from `@urbit/http-api`: - -```js -import Urbit from "@urbit/http-api"; -``` - -We also need to import a few other things, mostly relating to UI components (but -these aren't important for our purposes here): - -```js -import React, { Component } from "react"; -import "bootstrap/dist/css/bootstrap.min.css"; -import "react-day-picker/lib/style.css"; -import TextareaAutosize from "react-textarea-autosize"; -import Button from "react-bootstrap/Button"; -import Card from "react-bootstrap/Card"; -import Stack from "react-bootstrap/Stack"; -import Tab from "react-bootstrap/Tab"; -import Tabs from "react-bootstrap/Tabs"; -import ToastContainer from "react-bootstrap/ToastContainer"; -import Toast from "react-bootstrap/Toast"; -import Spinner from "react-bootstrap/Spinner"; -import CloseButton from "react-bootstrap/CloseButton"; -import Modal from "react-bootstrap/Modal"; -import DayPickerInput from "react-day-picker/DayPickerInput"; -import endOfDay from "date-fns/endOfDay"; -import startOfDay from "date-fns/startOfDay"; -import { BottomScrollListener } from "react-bottom-scroll-listener"; -``` - -Inside the existing `App` class: - -```js -class App extends Component { -``` - -...we'll clear out the existing demo code and start adding ours. The first thing -is to define our app's state. We'll look at most of the state entries in the -next section. For now, we'll just consider `status`. - -```js -state = { - // ..... - status: null, - // ..... -}; -``` - -Next, we'll setup the `Urbit` API object in `componentDidMount`. We could do -this outside the `App` class since we're adding it to `window`, but we'll do it -this way so it's all in one place: - -```js -componentDidMount() { - window.urbit = new Urbit(""); - window.urbit.ship = window.ship; - window.urbit.onOpen = () => this.setState({status: "con"}); - window.urbit.onRetry = () => this.setState({status: "try"}); - window.urbit.onError = (err) => this.setState({status: "err"}); - this.init(); -}; -``` - -The first thing we do is create a new instance of the `Urbit` class we imported -from `@urbit/http-api`, and save it to `window.urbit`. The `Urbit` class -constructor takes three arguments: `url`, `desk` and `code`, of which only `url` +The first statement creates a new instance of the `Urbit` class we imported +from `@urbit/http-api`, and saves it to the `api` variable. The `Urbit` class +constructor takes three arguments: `url`, `code`, and `desk`, of which only `url` is mandatory. - `url` is the URL of the ship we want to talk to. Since our React app will be - served by the ship, we can just leave it as an empty `""` string and let + served by the ship, we can just leave it as an empty `''` string and let `Urbit` use root-relative paths. -- `desk` is only necessary if we want to run threads through Eyre, and since - we're not going to do that, we can exclude it. -- `code` is the web login code for authentication, but since the user will - already have logged in, we can also exclude that. +- `code` is the web login code for authentication. Since the user will already + have logged in, we can also leave it as an empty `''` string. +- `desk` is only necessary if we want to run threads through Eyre. This example + doesn't submit any such requests, but the `desk` is set anyway for + demonstration purposes. -Therefore, we call the class contructor with just the empty `url` string: +The second statement sets the ship name in our `Urbit` instance. Eyre requires +the ship name be specified in all requests; if we don't set it, Eyre will +reject all the messages we send. Fortunately, `create-landscape-app` handles +this detail by automatically initializing the active ship's name to the +variable `window.ship`, so we just set `api.ship` to this value. -```js -window.urbit = new Urbit(""); -``` - -Next, we need to set the ship name in our `Urbit` instance. Eyre requires the -ship name be specified in all requests, so if we don't set it, Eyre will reject -all the messages we send. We previously included `session.js` which sets -`window.ship` to the ship name, so we just set `window.urbit.ship` as that: - -```js -window.urbit.ship = window.ship; -``` - -Next, we set three callbacks: `onOpen`, `onRetry`, and `onError`. These -callbacks are fired when the state of our channel connection changes: +While not referenced in the `create-landscape-app` default code, the `Urbit` +class has three additional callbacks that can be set: `onOpen`, `onRetry`, and +`onError`. These callbacks are fired when the state of our channel connection +changes: - `onOpen` is called when a connection is established. - `onRetry` is called when a channel connection has been interrupted (such as by @@ -188,22 +126,103 @@ callbacks are fired when the state of our channel connection changes: - `onError` is called with an `Error` message once all retries have failed, or otherwise when a fatal error occurs. -We'll look at how we handle these cases in the next section. For now, we'll just -set the `status` entry in the state to either `"con"`, `"try"`, or `"err"` as -the case may be. Note that it's not mandatory to set these callbacks, but -leaving connection problems unhandled is usually a bad idea. +We'll look at how we can use these callbacks in the next section. Note that +it's not mandatory to set these callbacks, but leaving connection problems +unhandled is usually a bad idea. -The last thing we do is call: +### React app setup -```js -this.init(); +Finally, let's take a quick look at the React rendering logic for our +application. React rendering occurs within components, which are defined either +as classes (e.g. `class A extends Component { /* ... */ }`) or functions (e.g. +`function A() { /* ... */ }`). While recent React versions support both styles, +the latter "modern" style is preferred and used by most Urbit React +applications. + +Our code defines a few components, but we'll just focus on the primary +component for this tutorial; this component is defined as a functional +component named `App`: + +```javascript +export function App() { + /* ... */ +} ``` -This function will fetch initial entries and subscribe for updates. We'll look -at it in the next section. +As is common for React components, the first thing we'll define in our `App` +component is its state. In React, modifying a component's state causes it to be +re-rendered, so state variables should be carefully chosen to constitute all +"display-affecting" values. In modern React, component state is defined using +the [`useState()`] hook, which returns a pair of `[stateVariable, +setStateVariableFunction]`. Since our default `create-landscape-app` code just +displays the list of apps installed on a ship, it only needs to store this list +as its state: + +```javascript +const [apps, setApps] = useState(); +``` + +With the state established, we now define the code responsible for populating +this state. The canonical way to grab data from an external service/system in +React is to use the [`useEffect()`] hook. This function takes two arguments: +(1) the callback function for loading the external data and (2) a list of all +state variables dependencies, which will cause re-invocations of the first +argument when modified. Our app just needs to load the list of apps on our ship +(called `charges`) once, so its [`useEffect()`] invocation is simple: + +```javascript +useEffect(() => { + async function init() { + const charges = (await api.scry(scryCharges)).initial; + setApps(charges); + } + + init(); +}, []); +``` + +The last step is to return the HTML that will be used to render our component +in the browser. This HTML must adhere to the syntactic rules of +[JSX](https://en.wikipedia.org/wiki/JSX_(JavaScript)), which allow for greater +flexibility through extensions like embedded JavaScript (contained in curly +brace enclosures). Our component renders each app it found when scrying our +ship as a tile accompanied by its title and description: + +```javascript {% mode="collapse" %} +return ( +
+
+

Welcome to hut

+

Here's your urbit's installed apps:

+ {apps && ( +
    + {Object.entries(apps).map(([desk, app]) => ( +
  • + +
    +

    + {app.title || desk} +

    + {app.info &&

    {app.info}

    } +
    +
  • + ))} +
+ )} +
+
+); +``` + +With this brief primer complete, we'll take a closer look at our journal +application's front-end and how it utilizes the Urbit HTTP API in the next +section. ## Resources +- [React Tutorial](https://react.dev/learn/tutorial-tic-tac-toe) - A tutorial + walking through the basics of writing a modern React application. + - [HTTP API Guide](/guides/additional/http-api-guide) - Reference documentation for `@urbit/http-api`. @@ -214,3 +233,7 @@ at it in the next section. - [`@urbit/http-api` source code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The source code for the `@urbit/http-api` NPM package. + + +[`usestate()`]: https://react.dev/reference/react/useState +[`useeffect()`]: https://react.dev/reference/react/useEffect diff --git a/content/guides/core/app-school-full-stack/7-app-logic.md b/content/guides/core/app-school-full-stack/7-app-logic.md index a0a47a6..2e5a447 100644 --- a/content/guides/core/app-school-full-stack/7-app-logic.md +++ b/content/guides/core/app-school-full-stack/7-app-logic.md @@ -3,58 +3,72 @@ title = "7. React app logic" weight = 8 +++ -With the basic things setup, we can now go over the logic of our app. We'll just -focus on functions that are related to ship communications using the `Urbit` -object we previously setup, and ignore UI components and other helper functions. +Now that we've reviewed the basics of setting up an Urbit React app, we can +dive into the more complex logic that drives our [journal app's +front-end](https://github.com/urbit/docs-examples/tree/main/journal-app/ui). +We'll focus on the app's main component `App` (defined in +[`src/app.jsx`](https://github.com/urbit/docs-examples/tree/main/journal-app/ui/src/app.jsx)) +and how it leverages functions related to ship communications using the `Urbit` +object. For more information on UI components and other helper functions, see +the [resources section](#resources). ## State -In the previous section we just mentioned the connection `status` field of our -state. Here's the full state of our App: +In the previous section, we introduced how React components use [`useState()`] +to declare state variables within components. The main `App` component in our +journal app contains a number of these statements to manage its many +constituents and sub-components: -```js {% copy=true %} -state = { - entries: [], // list of journal entries for display - drafts: {}, // edits which haven't been submitted yet - newDraft: {}, // new entry which hasn't been submitted yet - results: [], // search results - searchStart: null, // search query start date - searchEnd: null, // search query end date - resultStart: null, // search results start date - resultEnd: null, // search results end date - searchTime: null, // time of last search - latestUpdate: null, // most recent update we've received - entryToDelete: null, // deletion target for confirmation modal - status: null, // connection status (con, try, err) - errorCount: 0, // number of errors so far - errors: new Map(), // list of error messages for display -}; +```javascript +// Control/Meta State // +const [subEvent, setSubEvent] = useState({}); +const [latestUpdate, setLatestUpdate] = useState(null); +const [status, setStatus] = useState(null); +const [errorCount, setErrorCount] = useState(0); +const [errors, setErrors] = useState(new Map()); + +// Journal State // +const [entries, setEntries] = useState([]); +const [drafts, setDrafts] = useState({}); +const [newDraft, setNewDraft] = useState({}); +const [entryToDelete, setEntryToDelete] = useState(null); + +// Search State // +const [results, setResults] = useState([]); +const [searchMeta, setSearchMeta] = useState({ + time: null, + start: null, + end: null, +}); ``` We'll see how these are used subsequently. ## Initialize -The first thing our app does is call `init()`: +After defining its state, the next thing our `App` component does is define a +function called `init()`, which is one of the first functions called during its +bootstrapping process: -```js -init = () => { - this.getEntries().then( +```javascript +const init = () => { + getEntries().then( (result) => { - this.handleUpdate(result); - this.setState({ latestUpdate: result.time }); - this.subscribe(); + setSubEvent(result); + setLatestUpdate(result.time); + subscribe(); }, (err) => { - this.setErrorMsg("Connection failed"); - this.setState({ status: "err" }); + addError("Connection failed"); + setStatus("err"); } ); }; ``` This function just calls `getEntries()` to retrieve the initial list of journal -entries then, if that succeeded, it calls `subscribe()` to subscribe for new +entries; then, if that succeeded, it publishes this update with `setSubEvent()` +and `setLatestUpdate()` and then calls `subscribe()` to subscribe for new updates. If the initial entry retrieval failed, we set the connection `status` and save an error message in the `errors` map. We'll look at what we do with errors later. @@ -63,13 +77,13 @@ errors later. ![entries screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/entries.png) -The `getEntries` function scries our `%journal` agent for up to 10 entries -before the oldest we currently have. We call this initially, and then each time +The `getEntries()` function scries our `%journal` agent for up to 10 entries +before the oldest we currently have. We call this initially and then each time the user scrolls to the bottom of the list. -```js -getEntries = async () => { - const { entries: e } = this.state; +```javascript +const getEntries = async () => { + const e = entries; const before = e.length === 0 ? Date.now() : e[e.length - 1].id; const max = 10; const path = `/entries/before/${before}/${max}`; @@ -93,27 +107,27 @@ direct GET requests allow other marks too. The `Urbit.scry` method returns a Promise which will contain an HTTP error message if the scry failed. We handle it with a `.then` expression back in the function that called it, either [`init()`](#initialize) or `moreEntries()`. If -the Promise is successfuly, the results are passed to the -[`handleUpdate`](#updates) function which appends the new entries to the -existing ones in state. +the Promise is successfully evaluated, the results are passed to the +[`setSubEvent()`](#updates) function, which appends the new entries to the +existing ones via a [`useEffect()`] hook (more on this [below](#updates)). ## Subscription A subscription to the `/updates` path of our `%journal` agent is opened with our `subscribe()` function: -```js -subscribe = () => { +```javascript +const subscribe = () => { try { window.urbit.subscribe({ app: "journal", path: "/updates", - event: this.handleUpdate, - err: () => this.setErrorMsg("Subscription rejected"), - quit: () => this.setErrorMsg("Kicked from subscription"), + event: setSubEvent, + err: () => addError("Subscription rejected"), + quit: () => addError("Kicked from subscription"), }); } catch { - this.setErrorMsg("Subscription failed"); + addError("Subscription failed"); } }; ``` @@ -124,7 +138,8 @@ object: - `app` - the target agent. - `path` - the `%watch` path we're subscribing to. - `event` - a function to handle each fact the agent sends out. We call our - `handleUpdate` function, which we'll describe below. + `setSubEvent()` function to set off a cascade to update the interface; + this process is described [below](#updates). - `err` - a function to call if the subscription request is rejected (nacked). We just display an error in this case. - `quit` - a function to call if we get kicked from the subscription. We also @@ -137,86 +152,130 @@ keep track of these IDs in your app's state. ## Updates -This `handleUpdate` function handles all updates we receive. It's called -whenever an event comes in for our subscription, and it's also called with the -results of [`getEntries`](#getting-entries) and [`getUpdates`](#error-handling) -(described later). +The architecture for updating a React interface based on incoming facts from an +`Urbit` subscription tends to follow a common pattern constituted of three +major parts: -It's a bit complex, but basically it just checks whether the JSON object is -`add`, `edit`, `delete`, or `entries`, and then updates the state appropriately. -The object it's receiving is just the `$update` structure converted to JSON by -the mark conversion functions we wrote previously. +1. A [`useState()`] call that creates an update object field as part of the + main component's state: + ```javascript + const [subEvent, setSubEvent] = useState({}); + ``` +2. An `Urbit.subscribe` call that passes the update object's setter function as + its `event` field: + ```javascript + window.urbit.subscribe({/* ... */, event: setSubEvent}); + ``` +3. A [`useEffect()`] invocation that triggers off of the update object, which + contains the logic for handling subscription updates: + ```javascript + useEffect(() => {/* ... */}, [subEvent]); + ``` -```js -handleUpdate = (upd) => { - const { entries, drafts, results, latestUpdate } = this.state; - if (upd.time !== latestUpdate) { - if ("entries" in upd) { - this.setState({ entries: entries.concat(upd.entries) }); - } else if ("add" in upd) { - const { time, add } = upd; - const eInd = this.spot(add.id, entries); - const rInd = this.spot(add.id, results); - const toE = - entries.length === 0 || add.id > entries[entries.length - 1].id; - const toR = this.inSearch(add.id, time); +The key piece of this architecture is the [`useEffect()`] trigger, which is +called whenever an event comes in on the subscription wire (achieved by +including the subscription object `subEvent` as a re-invocation trigger in +[`useEffect()`]'s second argument). In our application, this hook is also +triggered by calls to [`getEntries()`](#getting-entries) and +[`getUpdates()`](#error-handling), which will be described in greater detail +later. + +The trigger code is a bit complex, but in broad brushstrokes it just checks the +header of the incoming JSON object (i.e. one of `add`, `edit`, `delete`, or +`entries`) and then updates the state appropriately. The object it's receiving +is just the `$update` structure converted to JSON by the mark conversion +functions we wrote previously. + +```javascript {% mode="collapse" %} +useEffect(() => { + const getDataIndex = (id, data) => { + let low = 0; + let high = data.length; + while (low < high) { + let mid = (low + high) >>> 1; + if (data[mid].id > id) low = mid + 1; + else high = mid; + } + return low; + }; + + const isInSearch = (id, time) => ( + searchMeta.time !== null && + time >= searchMeta.time && + searchMeta.start.getTime() <= id && + searchMeta.end.getTime() >= id + ); + + if (subEvent.time !== latestUpdate) { + if ("entries" in subEvent) { + // NOTE: `BottomScrollListener` can fire on top of `init`, which can + // cause entries to be double loaded; we trim duplicates to avoid overlap + const [existing, incoming] = [entries, subEvent.entries]; + const oldestExistingId = existing.length === 0 + ? Date.now() + : existing[existing.length - 1].id; + let newestIncomingInd = getDataIndex(oldestExistingId, incoming); + newestIncomingInd += newestIncomingInd < incoming.length + && incoming[newestIncomingInd].id >= oldestExistingId; + setEntries(existing.concat(incoming.slice(newestIncomingInd))); + } else if ("add" in subEvent) { + const { time, add } = subEvent; + const eInd = getDataIndex(add.id, entries); + const rInd = getDataIndex(add.id, results); + const toE = entries.length === 0 || add.id > entries[entries.length - 1].id; + const toR = isInSearch(add.id, time); toE && entries.splice(eInd, 0, add); toR && results.splice(rInd, 0, add); - this.setState({ - ...(toE && { entries: entries }), - ...(toR && { results: results }), - latestUpdate: time, - }); - } else if ("edit" in upd) { - const { time, edit } = upd; + toE && setEntries([...entries]); + toR && setResults([...results]); + setLatestUpdate(time); + } else if ("edit" in subEvent) { + const { time, edit } = subEvent; const eInd = entries.findIndex((e) => e.id === edit.id); const rInd = results.findIndex((e) => e.id === edit.id); const toE = eInd !== -1; - const toR = rInd !== -1 && this.inSearch(edit.id, time); + const toR = rInd !== -1 && isInSearch(edit.id, time); if (toE) entries[eInd] = edit; if (toR) results[rInd] = edit; (toE || toR) && delete drafts[edit.id]; - this.setState({ - ...(toE && { entries: entries }), - ...(toR && { results: results }), - ...((toE || toR) && { drafts: drafts }), - latestUpdate: time, - }); - } else if ("del" in upd) { - const { time, del } = upd; + toE && setEntries([...entries]); + toR && setResults([...results]); + (toE || toR) && setDrafts({...drafts}); + setLatestUpdate(time); + } else if ("del" in subEvent) { + const { time, del } = subEvent; const eInd = entries.findIndex((e) => e.id === del.id); const rInd = results.findIndex((e) => e.id === del.id); const toE = eInd !== -1; - const toR = this.inSearch(del.id, time) && rInd !== -1; + const toR = isInSearch(del.id, time) && rInd !== -1; toE && entries.splice(eInd, 1); toR && results.splice(rInd, 1); (toE || toR) && delete drafts[del.id]; - this.setState({ - ...(toE && { entries: entries }), - ...(toR && { results: results }), - ...((toE || toR) && { drafts: drafts }), - latestUpdate: time, - }); + toE && setEntries([...entries]); + toR && setResults([...results]); + (toE || toR) && setDrafts({...drafts}); + setLatestUpdate(time); } } -}; +}, [subEvent]); ``` ## Add, edit, delete ![add screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/add.png) -When a user writes a new journal entry and hits submit, the `submitNew` function -is called. It uses the `Urbit.poke` method to poke our `%journal` agent. +When a user writes a new journal entry and hits submit, the `createEntry()` +function is called. It uses the `Urbit.poke` method to poke our `%journal` +agent. -```js -submitNew = (id, txt) => { +```javascript +const createEntry = (id, txt) => { window.urbit.poke({ app: "journal", mark: "journal-action", json: { add: { id: id, txt: txt } }, - onSuccess: () => this.setState({ newDraft: {} }), - onError: () => this.setErrorMsg("New entry rejected"), + onSuccess: () => setDraft({}), + onError: () => setError("New entry rejected"), }); }; ``` @@ -239,35 +298,38 @@ The `Urbit.poke` method takes five arguments: `onSuccess` and `onError` are optional, but it's usually desirable to handle these cases. -The `delete` and `submitEdit` functions are similar to `submitNew`, but for the -`%del` and `%edit` actions rather than `%add`: +The `deleteEntry()` and `editEntry()` functions are similar to `createEntry()`, +but for the `%del` and `%edit` actions rather than `%add`: ![edit screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/edit.png) -```js -submitEdit = (id, txt) => { - if (txt !== null) { +```javascript +const editEntry = (id, txt) => { + if (txt === null) { + delete drafts[id]; + setDrafts({...drafts}); + } else { window.urbit.poke({ app: "journal", mark: "journal-action", json: { edit: { id: id, txt: txt } }, - onError: () => this.setErrorMsg("Edit rejected"), + onError: () => setError("Edit rejected"), }); - } else this.cancelEdit(id); + } }; ``` ![delete screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/delete.png) -```js -delete = (id) => { +```javascript +const deleteEntry = (id) => { window.urbit.poke({ app: "journal", mark: "journal-action", - json: {"del": {"id": id}}, - onError: ()=>this.setErrorMsg("Deletion rejected") - }) - this.setState({rmModalShow: false, entryToDelete: null}) + json: { del: { id: id } }, + onError: () => setError("Deletion rejected"), + }); + setDeleteId(null); }; ``` @@ -279,73 +341,66 @@ our agent. ![search screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/search.png) -When searching for entries between two dates, the `getSearch` function is +When searching for entries between two dates, the `searchEntries()` function is called, which uses the `Urbit.scry` method to scry for the results in a similar fashion to [`getEntries`](#getting-entries), but using the `/x/entries/between/[start]/[end]` endpoint. -```js -getSearch = async () => { - const { searchStart: ss, searchEnd: se, latestUpdate: lu } = this.state; - if (lu !== null && ss !== null && se !== null) { - let start = ss.getTime(); - let end = se.getTime(); - if (start < 0) start = 0; - if (end < 0) end = 0; - const path = `/entries/between/${start}/${end}`; - window.urbit - .scry({ - app: "journal", - path: path, - }) - .then( - (result) => { - this.setState({ - searchTime: result.time, - searchStart: null, - searchEnd: null, - resultStart: ss, - resultEnd: se, - results: result.entries, - }); - }, - (err) => { - this.setErrorMsg("Search failed"); - } - ); - } else { - lu !== null && this.setErrorMsg("Searh failed"); - } +```javascript +const searchEntries = async () => { + const start = Math.max(inputStart.getTime(), 0); + const end = Math.max(inputEnd.getTime(), 0); + window.urbit.scry({ + app: "journal", + path: `/entries/between/${start}/${end}`, + }).then( + (result) => { + setInputStart(null); + setInputEnd(null); + setResults(result.entries); + setSearchMeta({ + time: result.time, + start: inputStart, + end: inputEnd + }); + }, + (err) => { + setError("Search failed"); + } + ); }; ``` ## Error handling -When the channel connection is interrupted, the `Urbit` object will begin trying to reconnect. On each attempt, it sets the connection `status` to `"try"`, as we specified for the `onRetry` callback. When this is set, a "reconnecting" message is displayed at the bottom of the screen: +When the channel connection is interrupted, the `Urbit` object will begin +trying to reconnect. On each attempt, it sets the connection `status` to +`"try"`, as we specified for the `onRetry` callback. When this is set, a +"reconnecting" message is displayed at the bottom of the screen: ![reconnecting screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/reconnecting.png) -If all three reconnection attempts fail, the `onError` callback is fired and we replace the "reconnecting" message with a "reconnect" button: +If all three reconnection attempts fail, the `onError` callback is fired and we +replace the "reconnecting" message with a "reconnect" button: ![reconnect screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/reconnect.png) When clicked, the following function is called: -```js -reconnect = () => { +```javascript +const reconnect = () => { window.urbit.reset(); - const latest = this.state.latestUpdate; - if (latest === null) { - this.init(); + if (latestUpdate === null) { + init(); } else { - this.getUpdates().then( + getUpdates().then( (result) => { - result.logs.map((e) => this.handleUpdate(e)); - this.subscribe(); + result.logs.map(setSubEvent); + subscribe(); }, (err) => { - this.setErrorMsg("Connection failed"); - this.setState({ status: "err" }); + addError("Connection failed"); + setStatus("err"); } ); } @@ -363,10 +418,9 @@ Since we've reset the channel, we don't know if we've missed any updates. Rather than having to refresh our whole state, we can use the `getUpdates()` function to get any missing update: -```js -getUpdates = async () => { - const { latestUpdate: latest } = this.state; - const since = latest === null ? Date.now() : latest; +```javascript +const getUpdates = async () => { + const since = latestUpdate === null ? Date.now() : latestUpdate; const path = `/updates/since/${since}`; return window.urbit.scry({ app: "journal", @@ -381,18 +435,21 @@ recent than `latestUpdate`, which is always set to the last logged action we received. The `getUpdates` function returns a Promise to the `reconnect` function above which called it. The `reconnect` function handles it in a `.then` expression, where the success case passes each update retrieved to the -[`handleUpdate`](#updates) function, updating our state. +[`setSubEvent()`](#updates) function, updating our state. Lastly, as well as handling channel connection errors, we also handle errors such as poke nacks or failed scries by printing error messages added to the -`error` map by the `setErrorMsg` function. You could of course handle nacks, -kicks, scry failures, etc differently than just printing an error, it depends on -the needs of your app. +`error` map by the `setErrorMsg()` function. You could of course handle nacks, +kicks, scry failures, etc differently than just printing an error; it depends +on the needs of your app. ![search failed screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/search-failed.png) ## Resources +- [React Tutorial](https://react.dev/learn/tutorial-tic-tac-toe) - A tutorial + walking through the basics of writing a modern React application. + - [HTTP API Guide](/guides/additional/http-api-guide) - Reference documentation for `@urbit/http-api`. @@ -403,3 +460,7 @@ the needs of your app. - [`@urbit/http-api` source code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The source code for the `@urbit/http-api` NPM package. + + +[`usestate()`]: https://react.dev/reference/react/useState +[`useeffect()`]: https://react.dev/reference/react/useEffect diff --git a/content/guides/core/app-school-full-stack/8-desk.md b/content/guides/core/app-school-full-stack/8-desk.md index ef837f5..0b77839 100644 --- a/content/guides/core/app-school-full-stack/8-desk.md +++ b/content/guides/core/app-school-full-stack/8-desk.md @@ -34,20 +34,20 @@ There's a handful of extra files we need in the root of our desk: We only have one agent to start, so `desk.bill` is very simple: -``` +``` {% copy=true %} :~ %journal == ``` Likewise, `sys.kelvin` just contains: -``` -[%zuse 417] +``` {% copy=true %} +[%zuse 414] ``` The `desk.docket-0` file is slightly more complicated: -``` +``` {% copy=true %} :~ title+'Journal' info+'Dear diary...' @@ -107,24 +107,21 @@ Once created, we can mount it to the unix filesystem. In the dojo of a fake ship: -``` -> |merge %journal our %webterm ->= -> |mount %journal ->= +``` {% copy=true %} +|new-desk %journal +|mount %journal ``` Now we can browse to it in the unix terminal: -```sh -cd ~/zod/journal +```sh {% copy=true %} +cd /path/to/zod/journal ``` -Currently it has the same files as the `%webterm` desk, so we need to delete -those: +Currently it just contains some skeleton files, so we need to delete those: -```sh -rm -r . +```sh {% copy=true %} +rm -rI /path/to/zod/journal/* ``` Apart from the kernel and standard library, desks need to be totally @@ -132,53 +129,56 @@ self-contained, including all mark files and libraries necessary to build them. For example, since our app contains a number of `.hoon` files, we need the `hoon.hoon` mark, and its dependencies. The easiest way to ensure our desk has everything it needs is to copy in the "dev" versions of the `%base` and -`%garden` desks. To do this, we first clone the Urbit git repository: +`%garden` desks. To do this, we first clone the Urbit and Landscape git repositories: -```sh +```sh {% copy=true %} git clone https://github.com/urbit/urbit.git urbit-git +git clone https://github.com/tloncorp/landscape.git landscape-git ``` -If we navigate to the `pkg` directory in the cloned repo: +If we navigate to the `pkg` directory in the cloned `urbit` repo: -```sh -cd ~/urbit-git/pkg +```sh {% copy=true %} +cd /path/to/urbit-git/pkg ``` -...we can combine the `base-dev` and `garden-dev` desks with the included -`symbolic-merge.sh` script: +...we can combine the `base-dev` and Landscape `desk-dev` desks with the +included `symbolic-merge.sh` script: -```sh +```sh {% copy=true %} ./symbolic-merge.sh base-dev journal -./symbolic-merge.sh garden-dev journal +./symbolic-merge.sh ../../landscape-git/desk-dev journal ``` Now, we copy the contents of the new `journal` folder into our empty desk: -```sh -cp -rL journal/* ~/zod/journal/ +```sh {% copy=true %} +cp -rL journal/* /path/to/zod/journal/ ``` -Note we've used the `L` flag to resolve symbolic links, because the dev-desks -contain symlinks to files in the actual `arvo` and `garden` folders. +Note we've used the `L` flag to resolve symbolic links. We can copy across all of our own files too: -```sh -cp -r ~/ourfiles/* ~/zod/journal/ +```sh {% copy=true %} +cp -r /path/to/ourfiles/* /path/to/zod/journal/ ``` Finally, in the dojo, we can commit the whole lot: -``` +``` {% copy=true %} |commit %journal ``` ## Glob -The next step is to build our front-end and upload the files to our ship. In the -`journal-ui` folder containing our React app, we can run: +The next step is to build our front-end and upload the files to our ship. If +you haven't yet downloaded the journal front-end source files, you can grab +them from [their repository](https://github.com/urbit/docs-examples). In the +folder containing our React app (`journal-app/ui` relative to the repository +base directory), we can run: -```sh +```sh {% copy=true %} npm run build ``` @@ -186,7 +186,7 @@ This will create a `build` directory containing the compiled front-end files. To upload it to our ship, we need to first install the `%journal` desk. In the dojo: -``` +``` {% copy=true %} |install our %journal ``` @@ -208,7 +208,7 @@ If we now return to the homescreen of our ship, we'll see our tile displayed, an The last thing we need to do is publish our app, so other users can install it from our ship. To do that, we just run the following command in the dojo: -``` +``` {% copy=true %} :treaty|publish %journal ``` diff --git a/content/guides/core/app-school-full-stack/9-final.md b/content/guides/core/app-school-full-stack/9-final.md index dee3f70..e53e933 100644 --- a/content/guides/core/app-school-full-stack/9-final.md +++ b/content/guides/core/app-school-full-stack/9-final.md @@ -27,7 +27,7 @@ here](https://github.com/urbit/urbit/tree/master/pkg/npm/api). Here is the reference material for each section of this walkthrough. -#### Types +### Types - [App School /sur section](/guides/core/app-school/7-sur-and-marks#sur) - This section of App School covers writing a `/sur` structure library for @@ -38,7 +38,7 @@ Here is the reference material for each section of this walkthrough. This section of `zuse.hoon` contains all the functions for working with `mop`s, and is well commented. -#### Agent +### Agent - [App School I](/guides/core/app-school/intro) - App School I covers all aspects of writing Gall agents in detail. @@ -52,7 +52,7 @@ Here is the reference material for each section of this walkthrough. The `agentio` library in the `%base` desk contains a large number of useful functions which making writing Gall agents easier. -#### JSON +### JSON - [The JSON Guide](/guides/additional/json-guide) - The stand-alone JSON guide covers JSON encoding/decoding in great detail. @@ -69,7 +69,7 @@ Here is the reference material for each section of this walkthrough. - [Eyre Overview](/reference/arvo/eyre/eyre) - This section of the Eyre vane documentation goes over the basic features of the Eyre vane. -#### Marks +### Marks - [The Marks section of the Clay documentation](/reference/arvo/clay/marks/marks) - This section of the Clay vane documentation covers mark files comprehensively. @@ -80,7 +80,7 @@ Here is the reference material for each section of this walkthrough. - [The JSON Guide](/guides/additional/json-guide) - This also covers writing mark files to convert to/from JSON. -#### Eyre +### Eyre - [The Eyre vane documentation](/reference/arvo/eyre/eyre) - This section of the vane docs covers all aspects of Eyre. @@ -91,7 +91,7 @@ Here is the reference material for each section of this walkthrough. documentation walks through using Eyre's external API at a low level (using `curl`). -#### React App Setup and Logic +### React App Setup and Logic - [HTTP API Guide](/guides/additional/http-api-guide) - Reference documentation for `@urbit/http-api`. @@ -104,7 +104,7 @@ Here is the reference material for each section of this walkthrough. code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The source code for the `@urbit/http-api` NPM package. -#### Desk and Glob +### Desk and Glob - [App publishing/distribution docs](/guides/additional/software-distribution) - Documentation covering third party desk composition, publishing and diff --git a/content/guides/core/app-school/10-scry.md b/content/guides/core/app-school/10-scry.md index a3413c6..07f7703 100644 --- a/content/guides/core/app-school/10-scry.md +++ b/content/guides/core/app-school/10-scry.md @@ -15,71 +15,29 @@ define _scry endpoints_ which allow data to be requested from their states. The endpoints can process the data in any way before returning it, but they cannot alter the actual state - scries can only read, not modify. -Most of the time, scry requests are handled by Arvo, which routes the request to -the appropriate vane. When you scry a Gall agent you actually scry Gall itself. -Gall interprets the request, runs it on the specified agent, and then returns -the result. Scries are performed with the -[dotket](/reference/hoon/rune/dot#-dotket) (`.^`) rune. Here's a summary of -their format: +Gall itself defines some special vane-level endpoints [as described in its scry +reference](/reference/arvo/gall/scry), but most scries to Gall are routed to +particular agents and handled by them instead. Agent scries are what we'll +focus on here. + +Scries are performed with the [dotket](/reference/hoon/rune/dot#-dotket) (`.^`) +rune. Here's a summary of their format: ![scry summary diagram](https://storage.googleapis.com/media.urbit.org/docs/arvo/scry-diagram-v2.svg) -A note on `care`s: Cares are most carefully implemented by Clay, where they specify -submodules and have tightly defined behaviors. For Gall agents, most of these -don't have any special behavior, and are just used to indicate the general kind -of data produced by the endpoint. There are a handful of exceptions to this: -`%d`, `%e`, `%u` and `%x`. - -#### `%d` - -A scry to Gall with a `%d` `care` and no `path` will produce the `desk` in which -the specified agent resides. For example: - -``` -> .^(desk %gd /=hark-store=) -%garden -> .^(desk %gd /=hood=) -%base -``` - -#### `%e` - -A scry to Gall with a `%e` `care`, a `desk` rather than agent in the `desk` -field of the above diagram, and no path, will produce a set of all installed -agents on that desk and their status. For example: - -``` -> .^((set [=dude:gall live=?]) %ge /=garden=) -{ [dude=%hark-system-hook live=%.y] - [dude=%treaty live=%.y] - [dude=%docket live=%.y] - [dude=%settings-store live=%.y] - [dude=%hark-store live=%.y] -} -``` - -#### `%u` - -A scry to Gall with a `%u` `care` and no `path` will check whether or not the -specified agent is installed and running: - -``` -> .^(? %gu /=btc-wallet=) -%.y -> .^(? %gu /=btc-provider=) -%.n -> .^(? %gu /=foobar=) -%.n -``` +A note on `care`s: Cares are most carefully implemented by Clay, where they +specify submodules and have tightly defined behaviors. For Gall agents, most of +these don't have any special behavior, and are just used to indicate the +general kind of data produced by the endpoint, with the exception of the `%x` +care: #### `%x` -A scry to Gall with a `%x` `care` will be passed to the agent for handling. Gall -handles `%x` specially, and expects an extra field at the end of the `path` that -specifies the `mark` to return. Gall will take the data produced by the -specified endpoint and try to convert it to the given mark, crashing if the mark -conversion fails. The extra field specifying the mark is not passed through to -the agent itself. Here's a couple of examples: +Gall handles `%x` specially, and expects an extra field at the end of the +`path` that specifies the `mark` to return. Gall will take the data produced by +the specified endpoint and try to convert it to the given mark, crashing if the +mark conversion fails. The extra field specifying the mark is not passed +through to the agent itself. Here's a couple of examples: ``` > =store -build-file /=landscape=/sur/graph-store/hoon @@ -338,8 +296,9 @@ crash! - Scries will fail if the scry endpoint does not exist, the requested data does not exist, or the data does not nest in the return type specified. - Scries can only be performed on the local ship, not on remote ships. -- Gall scries with an agent name in the `desk` field will be passed to that - agent's `on-peek` arm for handling. +- Gall scries with an agent name in the `desk` field and without an extra empty + element at the beginning of the path will be passed to that agent's `on-peek` + arm for handling. - Gall scries with a `%x` `care` take a `mark` at the end of the scry `path`, telling Gall to convert the data returned by the scry endpoint to the mark specified. @@ -352,6 +311,7 @@ crash! ## Exercises - Have a read through the [Scry Guide](/reference/arvo/concepts/scry). +- Have a look at Gall's [scry reference](/reference/arvo/gall/scry). - Have a read through the [dotket rune documentation](/reference/hoon/rune/dot#-dotket). - Run through the [Example](#example) yourself if you've not done so already. diff --git a/content/guides/core/environment.md b/content/guides/core/environment.md index b3782dc..c825147 100644 --- a/content/guides/core/environment.md +++ b/content/guides/core/environment.md @@ -115,7 +115,7 @@ Azimuth](https://developers.urbit.org/guides/core/hoon-school/C-azimuth#the-urbi While working with Hoon, you'll often want to delete an old fake ship and recreate a fresh one. Rather than having to wait a few minutes for the fresh -ship to be initialised, you can instead create a backup copy of a fake ship. +ship to be initialized, you can instead create a backup copy of a fake ship. That way you can just delete the current copy, replace it with the backup, and reboot in a matter of seconds. diff --git a/content/guides/core/hoon-school/B-syntax.md b/content/guides/core/hoon-school/B-syntax.md index dd1deb3..ba9227b 100644 --- a/content/guides/core/hoon-school/B-syntax.md +++ b/content/guides/core/hoon-school/B-syntax.md @@ -356,7 +356,7 @@ This syntax is a little bit strange in the Dojo because subsequent expressions, 38 > perfect-number -38 +28 ``` The difference is that the Dojo “pin” is permanent until deleted: diff --git a/content/guides/core/hoon-school/D-gates.md b/content/guides/core/hoon-school/D-gates.md index f00e5d2..32116b8 100644 --- a/content/guides/core/hoon-school/D-gates.md +++ b/content/guides/core/hoon-school/D-gates.md @@ -227,12 +227,12 @@ How can we control what kind of value a gate returns? Many programming language Remember `^-` kethep? We will use `^-` as a _fence_, a way of making sure only data matching the appropriate structure get passed on. ```hoon {% copy=true %} -:: Confirm whether a value is greater than one. +:: Confirm whether a value is greater than one by return 1 (if no) or 0 (if yes). |= a=@ud ^- @ud ?: (gth a 1) - %.n -%.y + 1 +0 ``` **This is the correct way to define a gate.** Frequent annotation of type with `^-` kethep fences is _essential_ to producing good Hoon code. From this point forward in Hoon School, we will hew to this standard. diff --git a/content/guides/core/hoon-school/E-types.md b/content/guides/core/hoon-school/E-types.md index 52c0988..996e6fb 100644 --- a/content/guides/core/hoon-school/E-types.md +++ b/content/guides/core/hoon-school/E-types.md @@ -106,9 +106,9 @@ Here's a non-exhaustive list of auras, along with examples of corresponding lite | `@sv` | signed base32 | `-0v1df64.49beg` | | `@sw` | signed base64 | `--0wbnC.8haTg` | | `@sx` | signed hexadecimal | `-0x5f5.e138` | -| `@t` | UTF-8 text (cord) | `'howdy'` | -| `@ta` | ASCII text (knot) | `~.howdy` | -| `@tas` | ASCII text symbol (term) | `%howdy` | +| `@t` | UTF-8 text (`cord`) | `'howdy'` | +| `@ta` | ASCII text (subset) (`knot`) | `~.howdy` | +| `@tas` | ASCII text symbol (subset) (`term`) | `%howdy` | | `@u` | unsigned integer | no literal | | `@ub` | unsigned binary | `0b11.1000` | | `@ud` | unsigned decimal | `1.000.056` | @@ -118,6 +118,8 @@ Here's a non-exhaustive list of auras, along with examples of corresponding lite Some of these auras nest under others. For example, `@u` is for all unsigned auras. But there are other, more specific auras; `@ub` for unsigned binary numbers, `@ux` for unsigned hexadecimal numbers, etc. (For a more complete list of auras, see [Auras](/reference/hoon/auras).) +`knot` and `term` values each use a URL-safe subset of ASCII, omitting characters like spaces. + ### Aura Inference in Hoon Let's work a few more examples in the Dojo using the `?` operator. We'll focus on just the unsigned auras for now: diff --git a/content/guides/core/hoon-school/F-cores.md b/content/guides/core/hoon-school/F-cores.md index 0904685..07cb6a1 100644 --- a/content/guides/core/hoon-school/F-cores.md +++ b/content/guides/core/hoon-school/F-cores.md @@ -242,7 +242,7 @@ Produce a gate (generator) which accepts a `tape` value and returns a `(list @ud The previous code simply modified a value by addition. You can generalize this to other arithmetic processes, like multiplication, but you can also grow a data structure like a list. -For example, given the `tape` `"hello"`, the generator should return the list `~[104 101 108 108 111]`. +For example, given the `tape` `"hello"`, the generator should return the list `[104 101 108 108 111 ~]`. (A list is structurally a null-terminated tuple, or rightwards-branching cell ending in `~` or `0`.) We can equivalently write `~[104 101 108 108 111]` which is a special syntax reducing to the same thing. Two tools that may help: @@ -779,12 +779,12 @@ and verify that our program correctly produces the sequence of numbers 1, 1, 2, =/ q 1 =/ r *(list @ud) |- ^- (list @ud) - ?: =(i n) r + ?: =(index n) r %= $ - i +(i) - p q - q (add p q) - r [q r] + index +(index) + p q + q (add p q) + r [q r] == ``` @@ -939,12 +939,4 @@ F_{n+1} (x, y+1) & = F_n (F_{n+1} (x, y), F_{n+1} (x, y) + y + 1) & \text{if } n \end{array} {% /math %} - - - Implement the Sudan function as a gate. diff --git a/content/guides/core/hoon-school/H-libraries.md b/content/guides/core/hoon-school/H-libraries.md index c35c369..f8d886e 100644 --- a/content/guides/core/hoon-school/H-libraries.md +++ b/content/guides/core/hoon-school/H-libraries.md @@ -284,22 +284,31 @@ Unfortunately `/` fas runes don't work in the Dojo right now, so we need to buil A [desk](/reference/glossary/desk) organizes a collection of files, including generators, libraries, agents, and system code, into one coherent bundle. A desk is similar to a file drive in a conventional computer, or a Git branch. Desks are supported by the Clay vane in Arvo, the Urbit OS. -At this point, you've likely only worked on the `%base` desk. You can see data about any particular desk using the `+vat` generator: +At this point, you've likely only worked on the `%base` desk. You can see data about any particular desk using the `+vats` generator: ```hoon -> +vat %base +> +vats %base %base - /sys/kelvin: [%zuse 417] - base hash: ~ - %cz hash: 0v2.r1lbp.i9jr2.hosbi.rvg16.pqe7u.i3hnp.j7k27.9jsgv.8k7rp.oi98q + /sys/kelvin: [%zuse 413] + base hash ends in: hih5c + %cz hash ends in: hih5c + app status: running + pending updates: ~ + +> +vats %base, =verb %.y +%base + /sys/kelvin: [%zuse 413] + base hash: 0v2.vhcjk.rj42q.e3la7.1679q.u2qs2.35vnn.9n1jm.mj66h.kgpe5.hih5c + %cz hash: 0v2.vhcjk.rj42q.e3la7.1679q.u2qs2.35vnn.9n1jm.mj66h.kgpe5.hih5c app status: running force on: ~ force off: ~ publishing ship: ~ - updates: local - source ship: ~ - source desk: ~ - source aeon: ~ + updates: remote + source ship: ~marnec-dozzod-marzod + source desk: %kids + source aeon: 43 + kids desk: %kids pending updates: ~ ``` diff --git a/content/guides/core/hoon-school/I-testing.md b/content/guides/core/hoon-school/I-testing.md index 4506439..56161d2 100644 --- a/content/guides/core/hoon-school/I-testing.md +++ b/content/guides/core/hoon-school/I-testing.md @@ -312,8 +312,6 @@ A `fish-loop` arises when using a recursive mold definition like `list`. (The r fish-loop ``` -although a promised `?#` wuthax rune should match it once implemented. - ### `generator-build-fail` A `generator-build-fail` most commonly results from composing code with mismatched runes (and thus the wrong children including hanging expected-but-empty slots). diff --git a/content/guides/core/hoon-school/K-doors.md b/content/guides/core/hoon-school/K-doors.md index 76a78a9..a6d7644 100644 --- a/content/guides/core/hoon-school/K-doors.md +++ b/content/guides/core/hoon-school/K-doors.md @@ -32,7 +32,7 @@ The second way of making a function call involves an expression that _produces_ 246 ``` -The difference is subtle: the first cast has an already-created gate in the subject when we called it, while the latter involves producing a gate that doesn't exist anywhere in the subject, and then calling it. +The difference is subtle: the first case has an already-created gate in the subject when we called it, while the latter involves producing a gate that doesn't exist anywhere in the subject, and then calling it. Are calls to `++add` and `++mul` of the Hoon standard library of the first kind, or the second? @@ -453,7 +453,7 @@ What is that cell? Wasn't the value stored as `0xff.8833`? Well, one fundament - What does `[~ ~]` mean when returned from a `map`? -`unit`s are common enough that they have their own syntax and set of operational functions. We'll look at them more in [the next module](/guides/core/hoon-school/K-doors). +`unit`s are common enough that they have their own syntax and set of operational functions. We'll look at them more in [the next module](/guides/core/hoon-school/L-struct). ```hoon > (~(get by colors) %brown) diff --git a/content/guides/core/hoon-school/M-typecheck.md b/content/guides/core/hoon-school/M-typecheck.md index 0cda14a..50ceafe 100644 --- a/content/guides/core/hoon-school/M-typecheck.md +++ b/content/guides/core/hoon-school/M-typecheck.md @@ -222,7 +222,7 @@ It's important to remember to include a cast rune with each gate and trap expres By now you've used the `|=` rune to define several gates. This rune is used to produce a _dry gate_, which has different type-checking and type-inference properties than a _wet gate_ does. We won't explain the distinction until [a later module](/guides/core/hoon-school/R-metals)—for now, just keep in mind that we're only dealing with one kind of gate (albeit the more common kind). -The first subexpression after the `|=` defines the sample type. Any faces used in this definition have the type declared for it in this definition. Consider an addition generator `/gen/add.hoon`: +The first subexpression after the `|=` defines the sample type. Any faces used in this definition have the type declared for it in this definition. Consider an addition generator `/gen/sum.hoon`: ```hoon {% copy=true %} |= [a=@ b=@] @@ -235,10 +235,10 @@ $(a +(a), b (dec b)) We run it in the Dojo using a cell to pass the two arguments: ```hoon -> +add 12 14 +> +sum [12 14] 26 -> +add 22 +> +sum 22 nest-fail -need.[a=@ b=@] -have.@ud diff --git a/content/guides/quickstart/chat-guide.md b/content/guides/quickstart/chat-guide.md index 8689ecc..ed9e57c 100644 --- a/content/guides/quickstart/chat-guide.md +++ b/content/guides/quickstart/chat-guide.md @@ -28,7 +28,7 @@ has three folders inside: 1. `bare-desk`: just the hoon files created here without any dependencies. 2. `full-desk`: `bare-desk` plus all dependencies. Note some files are symlinked, so if you're copying them you'll need to do `cp -rL`. -3. `react-frontend`: the React front-end files. +3. `ui`: the React front-end files. Let's get started. @@ -53,7 +53,7 @@ curl -L https://urbit.org/install/linux-aarch64/latest | tar xzk --transform='s/ #### macOS (`x86_64`) ```shell {% copy=true %} -curl -L https://urbit.org/install/macos-x86_64/latest | tar xzk -s '/.*/urbit/' +curl -L https://urbit.org/install/macos-x86_64/latest | tar xzk -s '/.*/urbit/' ``` #### macOS (`aarch64`) @@ -241,7 +241,7 @@ a hut by poking our agent with a `%post` action. Likewise, we'll be able to subscribe to huts for groups on other ships and poke them to post messages. Remember, all Urbit ships are both clients and servers. -There's three main agent arms we use for this: +There are three main agent arms we use for this: 1. `on-poke`: This arm handles one-off actions/requests, such as posting a message to a hut. @@ -368,7 +368,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in |= old-vase=vase ^- (quip card _this) [~ this(state !<(state-0 old-vase))] -:: on-poke handles "pokes", one-off requests/actions intiated either +:: on-poke handles "pokes", one-off requests/actions initiated either :: by our local ship, the front-end or other ships on the network. :: ++ on-poke @@ -570,7 +570,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in :: the subscribe succeeded or failed :: %watch-ack - :: if there's no error message it succceeded, + :: if there's no error message it succeeded, :: do nothing further :: ?~ p.sign `this @@ -784,7 +784,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in :: switch on the kind of event :: ?+ -.sign (on-agent:def wire sign) - :: if it was a response to a subscriiption request... + :: if it was a response to a subscription request... %watch-ack :: if there's no error message the subscription succeeded, :: no nothing @@ -1117,7 +1117,7 @@ in `hut/mar/hut/do.hoon` and `hut/mar/hut/did.hoon` respectively. :: ++ grow |% - :: this mark is primarily used inbound from the + :: this mark is primarily used inbound from the :: front-end, so we only need a simple %noun :: conversion method here :: @@ -1252,7 +1252,7 @@ in `hut/mar/hut/do.hoon` and `hut/mar/hut/did.hoon` respectively. ['joined' (en-joined joined.u)] == == - :: this function creates an array of the the members of a the + :: this function creates an array of the members of a the :: huts for a squad :: ++ en-joined @@ -1333,248 +1333,256 @@ in `hut/mar/hut/do.hoon` and `hut/mar/hut/did.hoon` respectively. Our back-end is complete, so we can now work on our React front-end. We'll just look at the basic setup process here, but you can get the full React app by cloning [this repo on Github](https://github.com/urbit/docs-examples) and run -`npm i` in `chat-app/react-frontend`. Additional commentary on the code is in -the [additional commentary](#additional-commentary) section below. +`npm i` in `chat-app/ui`. Additional commentary on the code is in the +[additional commentary](#additional-commentary) section below. #### Basic setup process -When creating it from scratch, we can first run `create-react-app` like usual: +When creating it from scratch, first make sure you have Node.js installed on +your computer (you can download it from their +[website](https://nodejs.org/en/download)) and then run `create-landscape-app`: -```shell {% copy=true %} -npx create-react-app hut-ui -cd hut-ui +```shell +npx @urbit/create-landscape-app +✔ What should we call your application? … hut +✔ What URL do you use to access Urbit? … http://127.0.0.1:8080 ``` -To make talking to our ship easy, we'll install the `@urbit/http-api` module: +This will generate a React project in the `hut/ui` directory with all the +basic necessities for Urbit front-end development. Next, run the following +commands to install the project's dependencies: -``` -npm i @urbit/http-api +```shell +cd hut/ui +npm i ``` -`http-api` handles most of the tricky parts of communicating with our ship for -us, and has a simple set of methods for doing things like pokes, subscriptions, -receiving updates, etc. - -The next thing we need to do is edit `package.json`. We'll change the name of -the app, and we'll also add an additional `"homepage"` entry. Front-ends are -serve at `/apps/`, so we need to set that as the root for when we build -it: - -```json -"name": "hut", -"homepage": "/apps/hut/", -``` - -Next, we need to edit `public/index.html` and add a script import to the -`` section. `http-api` needs to know the name of our ship in order to talk -to it, so our ship serves a simple script at `/session.js` that just does -`window.ship = "sampel-palnet";`. - -```html - -``` - -We can now open `src/App.js`, wipe its contents, and start writing our own app. -The first thing is to import the `Urbit` class from `@urbit/http-api`: +We can now open `src/app.jsx`, wipe its contents, and start writing our own +app. The first thing is to import the `Urbit` class from `@urbit/http-api`: ```javascript -import React, {Component} from "react"; +import React, {useEffect, useState} from "react"; import Urbit from "@urbit/http-api"; -// ..... ``` -In our App class, we'll create a new `Urbit` instance and tell it our ship name. -We'll also add some connection state callbacks. Our app is simple and will just -display the connection status in the top-right corner. +We'll create an `App` component that will create a new `Urbit` instance on load +to monitor our front-end's connection with our ship. Our app is simple and will +just display the connection status in the top-left corner: ```javascript -constructor(props) { - super(props); - window.urbit = new Urbit(""); - window.urbit.ship = window.ship; - // ...... - window.urbit.onOpen = () => this.setState({conn: "ok"}); - window.urbit.onRetry = () => this.setState({conn: "try"}); - window.urbit.onError = () => this.setState({conn: "err"}); - // ...... -}; +export function App() { + const [status, setStatus] = useState("try"); + + useEffect(() => { + window.urbit = new Urbit(""); + window.urbit.ship = window.ship; + + window.urbit.onOpen = () => setStatus("con"); + window.urbit.onRetry = () => setStatus("try"); + window.urbit.onError = () => setStatus("err"); + + const subscription = window.urbit.subscribe({ + app: "hut", + path: "/all", + event: (e) => console.log(e), + }); + + return () => window.urbit.unsubscribe(subscription); + }, []); + + return (

{status}

); +} ``` -```javascript -constructor(props) { - super(props); - window.urbit = new Urbit(""); - window.urbit.ship = window.ship; - // ...... - window.urbit.onOpen = () => this.setState({conn: "ok"}); - window.urbit.onRetry = () => this.setState({conn: "try"}); - window.urbit.onError = () => this.setState({conn: "err"}); - // ...... -}; -``` +After we've finished writing our React app, we can build it and view the +resulting files in the `dist` directory: -After we've finished writing our React app, we can build it: - -```shell {% copy=true %} +```shell npm run build +ls dist ``` #### Additional commentary -There are a fair few functions our front-end uses, so we'll just look at a -handful. The first is `doPoke`, which (as the name suggests) sends a poke to a -ship. It takes the poke in JSON form. It then calls the `poke` method of our -`Urbit` object to perform the poke. +There are a fair few functions in the +[complete front-end source for `%hut`](https://github.com/urbit/docs-examples); +we'll just look at a handful to cover the basics. The first is the `appPoke` +in `src/lib.js`, which (as the name suggests) sends a poke to a ship. It takes +the poke in JSON form and calls the `poke` method of our `Urbit` object to +perform the poke: ```javascript -doPoke = jon => { - window.urbit.poke({ +export function appPoke(jon) { + return api.poke({ app: "hut", mark: "hut-do", json: jon, - }) -}; + }); +} ``` -Here's an example of a `%join`-type `act` in JSON form: +An example of sending a `poke` with a `%join`-type `act` in JSON form can be +found in the `src/components/SelectGid.jsx` source file: ```javascript -joinGid = () => { - const joinSelect = this.state.joinSelect - if (joinSelect === "def") return; - const [host, name] = joinSelect.split("/"); - this.doPoke( - {"join": { - "gid" : {"host": host, "name": name}, - "who" : this.our - }} - ); - this.setState({joinSelect: "def"}) +const handleJoin = () => { + if (joinSelect !== "def") { + const [host, name] = joinSelect.split("/"); + appPoke({ + "join": { + "gid" : {"host": host, "name": name}, + "who" : OUR + } + }); + } }; ``` Our front-end will subscribe to updates for all groups our `%hut` agent is currently tracking. To do so, it calls the `subscribe` method of the `Urbit` -object with the `path` to subscribe to and an `event` callback to handle each -update it receives. Our agent publishes all updates on the local-only `/all` -path. +object (aliased to `api` in our example) with the `path` to subscribe to and an +`event` callback to handle each update it receives. Our agent publishes all +updates on the local-only `/all` path. Here's the source in the `src/app.jsx` +file: ```javascript -subscribe = () => { - window.urbit.subscribe({ - app: "hut", - path: "/all", - event: this.handleUpdate - }); -}; +const subscription = api.subscribe({ + app: "hut", + path: "/all", + event: setSubEvent, +}); ``` -Here's the `handleUpdate` function we gave as a callback. The update will be one -of our `hut-upd` types in JSON form, so we just switch on the type and handle it -as appropriate. +Notice that the above call to `subscribe` passes the `setSubEvent` function. +This is part of a common pattern for Urbit React applications wherein a state +variable is used to track new events and cause component re-rendering. The +broad outline for this workflow is as follows: + +1. Create a component subscription event variable with: + ```javascript + const [subEvent, setSubEvent] = useState(); + ``` +2. Call the `subscribe` function, passing `setSubEvent` as the `event` keyword + argument: + ```javascript + urbit.subscribe({ /* ... */, event: setSubEvent }); + ``` +3. Create a subscription handler function that updates when new events are + available with: + ```javascript + useEffect(() => {/* handler goes here */}, [subEvent]); + ``` + +The source for the final `useEffect` portion of this workflow (found in the +`src/app.jsx` file) can be found below: ```javascript {% mode="collapse" %} -handleUpdate = upd => { - const {huts, msgJar, joined, currentGid, currentHut} = this.state; - if ("initAll" in upd) { - upd.initAll.huts.forEach(obj => - huts.set(this.gidToStr(obj.gid), new Set(obj.names)) - ); - this.setState({ - huts: huts, - msgJar: new Map( - upd.initAll.msgJar.map(obj => [this.hutToStr(obj.hut), obj.msgs]) - ), - joined: new Map( - upd.initAll.joined.map(obj => - [this.gidToStr(obj.gid), new Set(obj.ppl)] - ) - ) - }) - } else if ("init" in upd) { - upd.init.msgJar.forEach(obj => - msgJar.set(this.hutToStr(obj.hut), obj.msgs) - ); - this.setState({ - msgJar: msgJar, - huts: huts.set( - this.gidToStr(upd.init.huts[0].gid), - new Set(upd.init.huts[0].names) - ), - joined: joined.set( - this.gidToStr(upd.init.joined[0].gid), - new Set(upd.init.joined[0].ppl) - ) - }) - } else if ("new" in upd) { - const gidStr = this.gidToStr(upd.new.hut.gid); - const hutStr = this.hutToStr(upd.new.hut); - (huts.has(gidStr)) - ? huts.get(gidStr).add(upd.new.hut.name) - : huts.set(gidStr, new Set(upd.new.hut.name)); - this.setState({ - huts: huts, - msgJar: msgJar.set(hutStr, upd.new.msgs) - }) - } else if ("post" in upd) { - const hutStr = this.hutToStr(upd.post.hut); - (msgJar.has(hutStr)) - ? msgJar.get(hutStr).push(upd.post.msg) - : msgJar.set(hutStr, [upd.post.msg]); - this.setState( - {msgJar: msgJar}, - () => { - (hutStr === this.state.currentHut) - && this.scrollToBottom(); +useEffect(() => { + const updateFuns = { + "initAll": (update) => { + update.huts.forEach(obj => + huts.set(gidToStr(obj.gid), new Set(obj.names)) + ); + + setHuts(new Map(huts)); + setChatContents(new Map( + update.msgJar.map(o => [hutToStr(o.hut), o.msgs]) + )); + setChatMembers(new Map( + update.joined.map(o => [gidToStr(o.gid), new Set(o.ppl)]) + )); + }, "init": (update) => { + setChatContents(new Map(update.msgJar.reduce( + (a, n) => a.set(hutToStr(n.hut), n.msgs) + , chatContents))); + setHuts(new Map(huts.set( + gidToStr(update.huts[0].gid), + new Set(update.huts[0].names) + ))); + setChatMembers(new Map(chatMembers.set( + gidToStr(update.joined[0].gid), + new Set(update.joined[0].ppl) + ))); + }, "new": (update) => { + const gidStr = gidToStr(update.hut.gid); + const hutStr = hutToStr(update.hut); + if (huts.has(gidStr)) { + huts.get(gidStr).add(update.hut.name); + } else { + huts.set(gidStr, new Set(update.hut.name)); } - ) - } else if ("join" in upd) { - const gidStr = this.gidToStr(upd.join.gid); - (joined.has(gidStr)) - ? joined.get(gidStr).add(upd.join.who) - : joined.set(gidStr, new Set([upd.join.who])); - this.setState({joined: joined}) - } else if ("quit" in upd) { - const gidStr = this.gidToStr(upd.quit.gid); - if ("~" + window.ship === upd.quit.who) { - (huts.has(gidStr)) && - huts.get(gidStr).forEach(name => - msgJar.delete(gidStr + "/" + name) + + setHuts(new Map(huts)); + setChatMembers(new Map(chatMembers.set(hutStr, update.msgs))); + }, "post": (update) => { + const newHut = hutToStr(update.hut); + if (chatContents.has(newHut)) { + chatContents.set(newHut, [...chatContents.get(newHut), update.msg]); + } else { + chatContents.set(newHut, [update.msg]); + } + + setChatContents(new Map(chatContents)); + }, "join": (update) => { + const gidStr = gidToStr(update.gid); + if (chatMembers.has(gidStr)) { + chatMembers.get(gidStr).add(update.who) + } else { + chatMembers.set(gidStr, new Set([update.who])); + } + + setChatMembers(new Map(chatMembers)); + setJoinSelect("def"); + }, "quit": (update) => { + const gidStr = gidToStr(update.gid); + if (update.who === OUR) { + huts.delete(gidStr); + chatMembers.delete(gidStr); + if(huts.has(gidStr)) { + huts.get(gidStr).forEach(name => + chatContents.delete(gidStr + "/" + name) + ); + } + + setHuts(new Map(huts)); + setChatMembers(new Map(chatMembers)); + setChatContents(new Map(chatContents)); + setCurrGid((currGid === gidStr) ? null : currGid); + setCurrHut((currHut === null) + ? null + : (`${currHut.split("/")[0]}/${currHut.split("/")[1]}` === gidStr) + ? null + : currHut ); - huts.delete(gidStr); - joined.delete(gidStr); - this.setState({ - msgJar: msgJar, - huts: huts, - joined: joined, - currentGid: (currentGid === gidStr) - ? null : currentGid, - currentHut: (currentHut === null) ? null : - ( - currentHut.split("/")[0] + "/" + currentHut.split("/")[1] - === gidStr - ) - ? null : currentHut, - make: (currentGid === gidStr) ? "" : this.state.make - }) - } else { - (joined.has(gidStr)) && - joined.get(gidStr).delete(upd.quit.who); - this.setState({joined: joined}) - } - } else if ("del" in upd) { - const gidStr = this.gidToStr(upd.del.hut.gid); - const hutStr = this.hutToStr(upd.del.hut); - (huts.has(gidStr)) && - huts.get(gidStr).delete(upd.del.hut.name); - msgJar.delete(hutStr); - this.setState({ - huts: huts, - msgJar: msgJar, - currentHut: (currentHut === hutStr) ? null : currentHut - }) + setViewSelect("def"); + setHutInput((currGid === gidStr) ? "" : hutInput); + } else { + if (chatMembers.has(gidStr)) { + chatMembers.get(gidStr).delete(update.who); + } + + setChatMembers(new Map(chatMembers)); + } + }, "del": (update) => { + const gidStr = gidToStr(update.hut.gid); + const hutStr = hutToStr(update.hut); + if (huts.has(gidStr)) { + huts.get(gidStr).delete(update.hut.name); + } + chatContents.delete(hutStr); + + setHuts(new Map(huts)); + setChatContents(new Map(chatContents)); + setCurrHut((currHut === hutStr) ? null : currHut); + }, + }; + + const eventTypes = Object.keys(subEvent); + if (eventTypes.length > 0) { + const eventType = eventTypes[0]; + updateFuns[eventType](subEvent[eventType]); } -}; +}, [subEvent]); ``` ## Desk config @@ -1587,7 +1595,7 @@ this by adding a `sys.kelvin` file to the root of our `hut` directory: ```shell {% copy=true %} cd hut -echo "[%zuse 417]" > sys.kelvin +echo "[%zuse 414]" > sys.kelvin ``` We also need to specify which agents to start when our desk is installed. We do @@ -1627,10 +1635,10 @@ the moment. ## Put it together Our app is now complete, so let's try it out. In the Dojo of our comet, -we'll create a new desk by forking from an existing one: +we'll create a new desk with the `|new-desk` generator: ``` {% copy=true %} -|merge %hut our %webterm +|new-desk %hut ``` Next, we'll mount the desk so we can access it from the host OS: @@ -1639,12 +1647,12 @@ Next, we'll mount the desk so we can access it from the host OS: |mount %hut ``` -Currently its contents are the same as the `%webterm` desk, so we'll need to -delete those files and copy in our own instead. In the normal shell, do the +It'll have a handful of skeleton files in it, but we can just delete those and +add our own instead. In the normal shell, do the following: ```shell {% copy=true %} -rm -r dev-comet/hut/* +rm -rI dev-comet/hut/* cp -r hut/* dev-comet/hut/ ``` @@ -1660,7 +1668,7 @@ to `localhost:8080` (or just `localhost` on a Mac). Login with the comet's web code, which you can get by running `+code` in the Dojo. Next, go to `localhost:8080/docket/upload` (or `localhost/docket/upload` on a Mac) and it'll bring up the Docket Globulator tool. Select the `hut` desk from the -drop-down menu, then navigate to `hut-ui/build` and select the whole folder. +drop-down menu, then navigate to `hut/ui/dist` and select the whole folder. Finally, hit `glob!` and it'll upload our React app. If we return to `localhost:8080` (or `localhost` on a Mac), we should see a diff --git a/content/guides/quickstart/groups-guide.md b/content/guides/quickstart/groups-guide.md index a898df2..33bbaaa 100644 --- a/content/guides/quickstart/groups-guide.md +++ b/content/guides/quickstart/groups-guide.md @@ -139,7 +139,7 @@ For the actions/requests our app will accept, we'll need the following: 6. Leave a squad. 7. Make a private squad public. 8. Make a public squad private. -9. Change the title of a squad . +9. Change the title of a squad. These actions will only be allowed to be taken by the host ship. @@ -256,7 +256,7 @@ host and receive updates such as access control list changes or members joining and leaving. Likewise, we'll be able to subscribe to squads on other ships and receive their updates. Remember, all Urbit ships are both clients and servers. -There's three main agent arms we use for this: +There are three main agent arms we use for this: 1. `on-poke`: This arm handles one-off actions/requests (our `act` structure). It will also handle requests from the front-end, which we'll create in the @@ -1103,7 +1103,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in == :: :: if it's a kick alert, it may or may not be intentional, - :: so we just automaticall try to resubscribe + :: so we just automatically try to resubscribe :: %kick ?. (~(has by squads) gid) `this @@ -1208,7 +1208,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in :: check whether it's US, and do nothing if so :: ?: =(our.bol ship.upd) `this - :: otherwise, update the member list and and let local + :: otherwise, update the member list and let local :: subscribers know :: :- ~[(fact:io cage.sign ~[/local/all])] @@ -1399,7 +1399,7 @@ mark in `squad/mar/squad/did.hoon`. :: first we import our /sur/squad.hoon type defs and expose them directly :: /- *squad -:: the mark door takes an $act action in in the outbound case +:: the mark door takes an $act action in the outbound case :: |_ a=act :: the grow arm converts from an $act to other things @@ -1656,7 +1656,7 @@ Save the code below in `squad/app/squad/index.hoon`. ?:(success.page "success" "failed") success.page == -:: This component lets a group host change whther a squad is private +:: This component lets a group host change whether a squad is private :: or public. It's a form that POSTs the new state to either the /squad/private :: or /squad/public URL path, and our Gall agent processes the request. :: @@ -1916,7 +1916,7 @@ this by adding a `sys.kelvin` file to the root of our `squad` directory: ```shell {% copy=true %} cd squad -echo "[%zuse 417]" > sys.kelvin +echo "[%zuse 414]" > sys.kelvin ``` We also need to specify which agents to start when our desk is installed. We do @@ -1967,10 +1967,10 @@ squad ``` Let's now try it out. In the Dojo of our comet, -we'll create a new desk by forking from an existing one: +we'll create a new desk with the `|new-desk` generator: ``` {% copy=true %} -|merge %squad our %webterm +|new-desk %squad ``` Next, we'll mount the desk so we can access it from the host OS: @@ -1979,9 +1979,8 @@ Next, we'll mount the desk so we can access it from the host OS: |mount %squad ``` -Currently its contents are the same as the `%webterm` desk, so we'll need to -delete those files and copy in our own instead. In the normal shell, do the -following: +Currently it just contains some skeleton files, but we can just delete those +and add our own instead. In the normal shell, do the following: ```shell {% copy=true %} rm -r dev-comet/squad/* diff --git a/content/guides/quickstart/voting-guide.md b/content/guides/quickstart/voting-guide.md index d092b3d..0c83e1b 100644 --- a/content/guides/quickstart/voting-guide.md +++ b/content/guides/quickstart/voting-guide.md @@ -116,7 +116,7 @@ across that our app will depend on: mkdir -p tally/{app,sur,mar,lib} cp -r dev-comet/squad/mar/{bill*,hoon*,json*,kelvin*,mime*,noun*,ship*,txt*,docket-0*} tally/mar/ cp -r dev-comet/squad/lib/{agentio*,dbug*,default-agent*,skeleton*,docket*} tally/lib/ -cp -r dev-comet/squad/sur/{docket*, squad*} tally/sur/ +cp -r dev-comet/squad/sur/{docket*,squad*} tally/sur/ cp -r dev-comet/base/sur/ring.hoon tally/sur/ cp -r dev-comet/garden/lib/mip.hoon tally/lib/ ``` @@ -146,10 +146,10 @@ be sent or received in *pokes* (one-off messages): - `%withdraw`: delete a poll. -We'll also define an `update` structure, which wil be the kinds of events that +We'll also define an `update` structure, which will be the kinds of events that subscribers may be notified about: -- `%init`: given the intial polls and their state to a new subscriber. +- `%init`: given the initial polls and their state to a new subscriber. - `%vote`: someone has voted on a poll. - `%new`: someone has created a new poll. - `%withdraw`: someone has withdrawn an existing poll. @@ -317,12 +317,12 @@ groups we're a member of. It additionally handles group updates from the `%squad` agent. In the former case, the events we'll receive will contain `update`s, which we'll process in a similar manner to the `action`s in `on-poke`. All incoming votes will be validated here also. In the latter case, -we'll receive `%squad` `upd` updates, such as members joining or leave groups, +we'll receive `%squad` `upd` updates, such as members joining or leaving groups, and we'll handle them as appropriate. We differentiate between these two cases by testing the `wire`, which is a message tag we set when we initially subscribed. For `%squad` updates it will be -`/squad`, and for the rest it'll be the group ID we've subscribe to. +`/squad`, and for the rest it'll be the group ID we've subscribed to. #### The code @@ -428,7 +428,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in %1 `this(state old) %0 `this(state [%1 by-group.old voted.old withdrawn.old %subs]) == -:: on-poke handles "pokes", which are one-off requests/actions intiated +:: on-poke handles "pokes", which are one-off requests/actions initiated :: locally or by remote ships. If it's a %tally-action we call :: a handle-action function, and if it's a %handle-http-request from the :: front-end we call the handle-http function @@ -454,7 +454,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in ?. authenticated.req :_ state(section %subs) (give-http:hc rid [307 ['Location' '/~/login?redirect='] ~] ~) - :: otherwise, switch on the method, prodicing a 405 response for + :: otherwise, switch on the method, producing a 405 response for :: unhandled methods :: ?+ method.request.req @@ -616,7 +616,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in :: calculate the expiry data :: =/ expiry=@da (add now.bol (yule days.act 0 0 0 ~)) - :: retreive all polls from state for this gid + :: retrieve all polls from state for this gid :: =/ polls=(map pid [=poll =votes]) (fall (~(get by by-group) gid.act) *(map pid [=poll =votes])) @@ -647,7 +647,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in :: vote on a poll :: %vote - :: retreive the target poll from state + :: retrieve the target poll from state :: =/ [=poll =votes] (~(got bi by-group) gid.act pid.act) :: make sure it hasn't expired @@ -737,7 +737,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in ?> ?| =(our.bol src.bol) &(=(src.bol creator.poll) (gte expiry.poll now.bol)) == - :: update state and notify subscribers of the withdrawl + :: update state and notify subscribers of the withdrawal :: :_ %= state by-group (~(del bi by-group) gid.act pid.act) @@ -777,7 +777,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in (fall (~(get by by-group) gid) *(map pid [=poll =votes])) == :: on-agent handles either responses to requests we've -:: initated or subscription updates from people or agents +:: initiated or subscription updates from people or agents :: to which we've previously subscribed :: ++ on-agent @@ -1329,7 +1329,7 @@ Save the code below in `tally/app/tally/index.hoon`. == == == -:: we retreive a list of all squads and sort them +:: we retrieve a list of all squads and sort them :: alphabetically by title :: =/ all-squads=(list (pair gid squad)) @@ -1902,7 +1902,7 @@ this by adding a `sys.kelvin` file to the root of our `tally` directory: ```shell {% copy=true %} cd tally -echo "[%zuse 417]" > sys.kelvin +echo "[%zuse 414]" > sys.kelvin ``` We also need to specify which agents to start when our desk is installed. We do @@ -1933,10 +1933,10 @@ following: ## Put it together Our app is now complete, so let's try it out. In the Dojo of our comet, we'll -create a new desk (filesystem repo) by forking from an existing one: +create a new desk with the `|new-desk` generator: ```{% copy=true %} -|merge %tally our %webterm +|new-desk %tally ``` Next, we'll mount the desk so we can access it from the host OS: @@ -1945,13 +1945,12 @@ Next, we'll mount the desk so we can access it from the host OS: |mount %tally ``` -Currently its contents are the same as the `%webterm` desk, so we'll need to -delete those files and copy in our own instead. In the normal shell, do the -following: +Currently it just contains some skeleton files, but we can delete those and add +our own instead. In the normal shell, do the following: ```shell {% copy=true %} rm -r dev-comet/tally/* -cp -r tally/* dev-comet/tally/* +cp -r tally/* dev-comet/tally/ ``` Back in the Dojo again, we can now commit those files and install the app: diff --git a/content/highlights/highlight-1.md b/content/highlights/highlight-1.md index 253135f..c32e8b9 100644 --- a/content/highlights/highlight-1.md +++ b/content/highlights/highlight-1.md @@ -1,6 +1,6 @@ +++ -title = "Hoon School Live Spring '23" -image = "https://storage.googleapis.com/media.urbit.org/site/featured/hoon-school-march-23.png" -url="https://developers.urbit.org/courses/hsl" -description = "The next session of Hoon School Live begins in March. Sign up to learn the fundamentals of programming on Urbit." +title = "Assembly Hackathon 2023" +image = "https://storage.googleapis.com/media.urbit.org/developers/hackathon/hackathon_logo.svg" +url="/hackathon" +description = "This year’s Assembly Hackathon has the highest prize pool on Urbit ever. Are you prepared to rise to the challenge, showcase your skills, and claim victory?" +++ \ No newline at end of file diff --git a/content/overview/hoon.md b/content/overview/hoon.md index 95ffc46..f6b9118 100644 --- a/content/overview/hoon.md +++ b/content/overview/hoon.md @@ -20,7 +20,7 @@ about 3000 lines of Hoon. to build Urbit in. * [Hoon School](/guides/core/hoon-school/): A collection of tutorials designed to teach you the Hoon language. -* [Guides](/guides/additional/hoon/): Guides to specific Hoon tasks, +* [Guides](/guides/additional/): Guides to specific Hoon tasks, including testing, command-line interface apps, and parsing. * [Reference](/reference/hoon/): Reference material primarily intended for Hoon developers with some experience. diff --git a/content/reference/additional/cryptography.md b/content/reference/additional/cryptography.md index 0731fa1..6742907 100644 --- a/content/reference/additional/cryptography.md +++ b/content/reference/additional/cryptography.md @@ -11,13 +11,13 @@ cryptography on Urbit. We first summarize the two categories of keys and how they are utilized by each ship type, then cover how different parts of Urbit are involved in cryptography. -### Types of keys +## Types of keys The two categories of keys are your Azimuth/Ethereum keys and your networking keys. In both cases, these are public/private key pairs utilized for public key cryptography. -#### Azimuth keys +### Azimuth keys Your Urbit ID exists as an ERC-721 non-fungible token on the Ethereum blockchain, and as such is contained in a wallet whose private key you possess. @@ -44,7 +44,7 @@ private keys there is no way to retrieve them somehow from your ship. For more information on the usage of these keys and the associated proxies, see the [Azimuth documentation](/reference/azimuth/azimuth). -#### Networking keys +### Networking keys All communications in Urbit over the [Ames](/reference/glossary/ames) network are end-to-end encrypted, and thus your ship stores its own public/private pair @@ -83,7 +83,7 @@ other method. This is merely a technical limitation imposed by the design of the system, not an intentional handicapping of comet abilities. A workaround to this limitation is slated to be implemented as of May 2021. -### System components +## System components [Ames](/reference/arvo/ames/ames) is Arvo's networking vane. All packets sent by Ames are encrypted utilizing a cryptosuite found in `zuse`. The only exception @@ -113,7 +113,7 @@ infrastructure utilized by Urbit. `azimuth-tracker` obtains networking public keys for planets, stars, and galaxies from this store, which are then stored in Jael and utilized by Ames for end-to-end encrypted communication. -### Additional documentation +## Additional documentation The following pages contained more detailed information about the cryptography utilized by each of the system components. diff --git a/content/reference/additional/dist/glob.md b/content/reference/additional/dist/glob.md index 44b9570..649041c 100644 --- a/content/reference/additional/dist/glob.md +++ b/content/reference/additional/dist/glob.md @@ -85,7 +85,7 @@ There's a different process for globs to be distributed over HTTP from a webserv To begin, you'll need to spin up a ship (typically a fake ship) and `|mount` a desk for which to add the files. In order for Clay to add the files, the desk must contain `mark` files in its `/mar` directory for all file extensions your folder contains. The `%garden` desk is a good bet because it includes `mark` files for `.js`, `.html`, `.png`, `.svg`, `.woff2` and a couple of others. If there's no desk with a mark for a particular file type you want included in your glob, you may need to add a new mark file. A very rudimentary mark file like the `png.hoon` mark will suffice. -With the desk mounted, add the folder to be globbed to the root of the desk in Unix. It's imporant it's in the root because the `%make-glob` thread will only strip the first level of the folder heirarchy. +With the desk mounted, add the folder to be globbed to the root of the desk in Unix. It's imporant it's in the root because the `%make-glob` thread will only strip the first level of the folder heirarchy. Additionally ensure that all file names in the folder you're globbing are lowercase, otherwise this next step will not work correctly. Next, `|commit` the files to the desk, then run `-garden!make-glob %the-desk /folder-name`, where `%the-desk` is the desk containing the folder to be globbed and `/folder-name` is its name. diff --git a/content/reference/arvo/ames/data-types.md b/content/reference/arvo/ames/data-types.md index c19503d..a36ce8f 100644 --- a/content/reference/arvo/ames/data-types.md +++ b/content/reference/arvo/ames/data-types.md @@ -65,6 +65,15 @@ Application-level message, as a `%pass`. - `path` - Internal route on the receiving ship. - `payload` - Semantic message contents. +## `$spar` + +```hoon ++$ spar [=ship =path] +``` + +Instead of a fully qualifying scry path, Ames infers rift and life based on the +ship. + ## `$bone` ```hoon @@ -167,6 +176,7 @@ All Ames knows about a peer. $: messages=(list [=duct =plea]) packets=(set =blob) heeds=(set duct) + keens=(jug path duct) == ``` @@ -175,6 +185,7 @@ What to do when Ames learns a peer's life and keys. - `messages` - [$plea](#plea)s local vanes have asked Ames to send. - `packets` - Packets we've tried to send. - `heeds` - Local tracking requests; passed through into [$peer-state](#peer-state). +- `keens` - Subscribers to remote scry paths. ## `$peer-state` @@ -182,6 +193,7 @@ What to do when Ames learns a peer's life and keys. +$ peer-state $: $: =symmetric-key =life + =rift =public-key sponsor=ship == @@ -192,6 +204,9 @@ What to do when Ames learns a peer's life and keys. rcv=(map bone message-sink-state) nax=(set [=bone =message-num]) heeds=(set duct) + closing=(set bone) + corked=(set bone) + keens=(map path keen-state) == ``` @@ -204,6 +219,137 @@ State for a peer with known life and keys. - `rcv` - Per-`bone` message sinks to assemble messages from fragments. - `nax` - Unprocessed nacks (negative acknowledgments). - `heeds` - Listeners for `%clog` notifications. +- `closing`: Bones closed on the sender side. +- `corked`: Bones closed on both sender and receiver. +- `keens`: Remote scry state. + +## `$keen-state` + +```hoon ++$ keen-state + $: wan=((mop @ud want) lte) :: request packets, sent + nex=(list want) :: request packets, unsent + hav=(list have) :: response packets, backward + num-fragments=@ud + num-received=@ud + next-wake=(unit @da) + listeners=(set duct) + metrics=pump-metrics + == +``` + +Remote scry state for a peer. + +- `wan`: Request packets, sent. +- `nex`: Request packets, unsent. +- `hav`: Response packets, backwards. +- `num-fragments`: Total fragment count. +- `num-received`: Fragments received. +- `next-wake`: Retry timing. +- `listeners`: Ducts waiting for a response. +- `metrics`: Stats. + +## `$want` + +```hoon ++$ want + $: fra=@ud + =hoot + packet-state + == +``` + +Remote scry request fragment. + +## `$have` + +```hoon ++$ have + $: fra=@ud + meow + == +``` + +Remote scry response fragment. + +## `$meow` + +```hoon ++$ meow + $: sig=@ux + num=@ud + dat=@ux + == +``` + +Remote scry response fragment data. + +- `sig`: signature. +- `num`: number of fragments. +- `dat`: contents. + +## `$peep` + +```hoon ++$ peep + $: =path + num=@ud + == +``` + +Remote scry fragment request. + +## `$wail` + +```hoon ++$ wail + $% [%0 peep] + == +``` + +Tagged remote scry request fragment. + +## `$roar` + +```hoon ++$ roar + (tale:pki:jael (pair path (unit (cask)))) +``` + +Remote scry response message. + +A `tale:pki:jael` is a: + +```hoon +++ tale :: urbit-signed * + |$ [typ] :: payload mold + $: dat=typ :: data + syg=(map ship (pair life oath)) :: signatures + == :: +``` + +Therefore, a `$roar` looks like: + +``` +> *roar:ames +[dat=[p=/ q=~] syg=~] +``` + +In `dat`, for the `(pair path (unit (cask)))`, the `path` is the remote scry +path and the `(unit (cask))` contains the value, or is null if there's no value +at this path and will never be one (equivalent to the `[~ ~]` case of a local +scry). + +## `$purr` + +```hoon ++$ purr + $: peep + meow + == +``` + +Response packet payload. ## `$qos` diff --git a/content/reference/arvo/ames/scry.md b/content/reference/arvo/ames/scry.md index 518134f..b402fca 100644 --- a/content/reference/arvo/ames/scry.md +++ b/content/reference/arvo/ames/scry.md @@ -123,3 +123,21 @@ A scry with a `%x` `care` and a `path` of `/snd-bones/[ship]/[bone]`, where `[sh ] ] ``` + +## /snubbed + +A scry with a `%x` `care` and a `path` of `/snubbed` will return Ames' current +ship whitelist/blacklist. The type is a `[form=?(%allow %deny) ships=(list +ship)]`, where `form` says whether it's a whitelist or blacklist and `ships` +are the ships on that list. + +#### Example + + +``` +> |ames-snub %deny ~wet ~sampel +>= + +> .^([form=?(%allow %deny) ships=(list ship)] %ax /=//=/snubbed) +[form=%deny ships=[i=~sampel t=[i=~wet t=~]]] +``` diff --git a/content/reference/arvo/ames/tasks.md b/content/reference/arvo/ames/tasks.md index 646fb8f..497afc4 100644 --- a/content/reference/arvo/ames/tasks.md +++ b/content/reference/arvo/ames/tasks.md @@ -10,8 +10,8 @@ Some `task`s appear to have more than one arm associated to them, e.g. there are four `+on-hear` arms. We denote this where it occurs, but always refer to the `+on-hear:event-core` arm. -Ames `task`s can be naturally divided into two categories: messaging tasks and -system/lifecycle tasks. +Ames `task`s can be naturally divided into three categories: messaging tasks, +system/lifecycle tasks, and remote scry tasks. ## Messaging Tasks @@ -33,6 +33,8 @@ There are multiple `+on-hear` arms in `ames.hoon`. Here we refer to `+on-hear:ev `%hear` can trigger a number of possible returns. It can trigger the release of zero or more additional packets via `%send` `gift`s. It may also trigger a `%boon` or `%plea` `gift` (collectively referred to as a `%memo` within Ames) to a local vane in the case of a completed message. +--- + ### `%heed` ```hoon @@ -51,6 +53,8 @@ If the `ship` is indeed being unresponsive, as measured by backed up `%boon`s, Ames will `give` a `%clog` `gift` to the requesting vane containing the unresponsive peer's urbit address. +--- + ### `%jilt` ```hoon @@ -68,6 +72,8 @@ The `ship` field specifies the peer we want to stop tracking. This `task` returns no `gift`s. +--- + ### `%plea` ```hoon @@ -93,6 +99,8 @@ A `%plea` `task` takes in the `ship` the `plea` is addressed to, and a [$plea](/ This `task` returns no `gift`s. +--- + ## System Tasks ### `%born` @@ -111,6 +119,8 @@ The `duct` along which `%born` comes is Ames' only duct to Unix, so `%send` `gift`s (which are instructions for Unix to send a packet) are also returned in response to `%born`. +--- + ### `%init` ```hoon @@ -134,6 +144,8 @@ contained by Jael. `%init` sends two moves that subscribe to `%turf` and `%private-keys` in Jael. +--- + ### `%sift` ```hoon @@ -148,6 +160,41 @@ The `ships` field specifies the ships for which debug output is desired. This `task` returns no `gift`s. +--- + +### `%snub` + +```hoon +[%snub form=?(%allow %deny) ships=(list ship)] +``` + +This `task` blacklists/whitelists ships in Ames. + +The `form` field specifies whether the given ships should be blacklisted or whitelisted. The `ships` field are the ships to blacklist/whitelist. + +The Ames `snub` settings can only have one form at a time: an `%allow` list or +`%deny` list. If an `%allow` form is set, packets from **all ships not on the +list will be blocked**. If a `%deny` form is set, packets from **any ship on +the list will be blocked, and all others allowed**. + +{% callout %} + +Note: a `%snub` `task` overrides the existing snub list and form entirely, +it does not merely add/remove ships from the existing list. + +If you just want to add/remove a ship from an existing blacklist/whitelist, +you'll need to first [scry out the existing snub +settings](/reference/arvo/ames/scry#snubbed), make your changes, and send the +whole modified list and form in a new `%snub` `task`. + +{% /callout %} + +#### Returns + +This `task` returns no `gift`s. + +--- + ### `%spew` ```hoon @@ -164,6 +211,8 @@ Sets verbosity toggles on debug output. This `task` is used internally when the This `task` returns no `gift`s. +--- + ### `%stir` ```hoon @@ -178,6 +227,8 @@ The `arg` field is unused. This `task` returns no `gift`s. +--- + ### `%vega` ```hoon @@ -190,3 +241,67 @@ anything in response to this. #### Returns This `task` returns no `gift`s. + +--- + +## Remote scry tasks + +### `%keen` + +```hoon +[%keen =ship =path] +``` + +A `%keen` `task` asks Ames to perform a remote scry, retrieving the value of +`path` on the given `ship`. The `path` has the general format of +`/[vane-letter]/[care]/[revision]/[rest-of-path]`. For a regular read into +Gall, it's `/g/x/[revision]/[agent]//[rest-of-path]`. Note the empty element in +between the agent and the rest of the path. + +#### Returns + +A `%tune` gift. A `%tune` gift looks like: + +```hoon +[%tune spar roar=(unit roar)] +``` + +It represents a *result*. The `roar` field is null if Ames doesn't have a +response, but may have one in the future. The +[`$roar`](/reference/arvo/ames/data-types#roar) contains a signature and the +data. The data in the `$roar` will be null if there is no value at the path in +question and will never be. These two cases are equivalent to `~` and `[~ ~]` of +a local scry. + +--- + +### `%yawn` + +```hoon +[%keen =ship =path] +``` + +A `%yawn` task asks Ames to cancel an existing remote scry request to the given +`path` on the given `ship`. + +#### Returns + +This `task` returns no `gift`s. + +--- + +### `%wham` + +```hoon +[%wham =ship =path] +``` + +A `%wham` task asks Ames to cancel all existing remote scry requests from all +vanes on all ducts for the given `path` on the given `ship`. + +#### Returns + +A `%tune` gift with a null `data` is given to all listeners. See the +[`%keen`](#keen) entry for more details of the `%tune` gift. + +--- diff --git a/content/reference/arvo/clay/architecture.md b/content/reference/arvo/clay/architecture.md index 518c630..ece66ea 100644 --- a/content/reference/arvo/clay/architecture.md +++ b/content/reference/arvo/clay/architecture.md @@ -43,7 +43,7 @@ DVCSes, and more. Clay has two other unique properties that we'll cover later on: it supports typed data and is referentially transparent. -### Revision Control +## Revision Control Every urbit has one or more desks, which are independently revision-controlled branches. Each desk contains its own `mark` @@ -113,7 +113,7 @@ from revision 5 of the `%base` desk on `~sampel-sipnym`, we refer to `/~sampel-sipnym/base/5/try/readme/md`. Clay's namespace is thus global and referentially transparent. -### A Typed Filesystem +## A Typed Filesystem Since Clay is a general filesystem for storing data of arbitrary types, in order to revision control correctly it needs to be @@ -169,7 +169,7 @@ As far as we are aware, Clay is the first generalized, type-aware revision control system. We'll go into the workings of this system in some detail. -### Marks +## Marks Central to a typed filesystem is the idea of file types. In Clay, we call these `mark`s. See the [Marks](/reference/arvo/clay/marks/marks) diff --git a/content/reference/arvo/clay/clay.md b/content/reference/arvo/clay/clay.md index ee3197b..869d4b8 100644 --- a/content/reference/arvo/clay/clay.md +++ b/content/reference/arvo/clay/clay.md @@ -20,14 +20,14 @@ zuse. Second is the write, query, and subscription logic. Finally, there is the logic for communicating requests to, and receiving requests from, foreign ships. -### User documentation +## User documentation [Filesystem](https://urbit.org/using/os/filesystem) How to interact with the Clay filesystem via Dojo. This includes basics such as mounting to Unix, changing directory, merging, and listing files. -### Developer Documentation +## Developer Documentation [Architecture](/reference/arvo/clay/architecture) diff --git a/content/reference/arvo/clay/data-types.md b/content/reference/arvo/clay/data-types.md index 2a68b20..e4a062c 100644 --- a/content/reference/arvo/clay/data-types.md +++ b/content/reference/arvo/clay/data-types.md @@ -11,7 +11,7 @@ filesystem. These types are only used inside of Clay. These are only relevant if you're working directly on Clay itself, or trying to understand its inner workings. -### `$cane` +### `cane` The set of changes between the mergebase and one of the desks being merged. @@ -33,26 +33,12 @@ The set of changes between the mergebase and one of the desks being merged. --- -### `$cult` - -Subscriptions - -```hoon -+$ cult (jug wove duct) -``` - -`cult`s keep track of subscribers. [`$wove`](#wove)s are associated to requests, -and each `wove` is mapped to a set of `duct`s associated to subscribers who -should be notified when the request is filled/updated. - ---- - -### `$melt` +### `melt` State for ongoing `%fuse` merges. ```hoon -+$ melt [bas=beak con=(list [beak germ]) sto=(map beak (unit dome:clay))] ++$ melt [bas=beak con=(list [beak germ]) sto=(map beak (unit domo))] ``` - `con` maintains the ordering. @@ -61,7 +47,7 @@ State for ongoing `%fuse` merges. --- -### `$dojo` +### `dojo` Domestic desk state @@ -86,167 +72,7 @@ ship. --- -### `$dome` - -Desk data - -```hoon -+$ dome - $: let=aeon :: top id - hit=(map aeon tako) :: versions by id - lab=(map @tas aeon) :: labels - tom=(map tako norm) :: tomb policies - nor=norm :: default policy - mim=(map path mime) :: mime cache - fod=flue :: ford cache - wic=(map weft yoki) :: commit-in-waiting - liv=zest :: running agents - ren=rein :: force agents on/off - == :: -``` - -A `dome` is the state of a `desk` and associated data. - -- `let` is the number of the most recently numbered commit. This is also the - total number of numbered commits. -- `hit` is a map of numerical IDs to commit hashes. These hashes are mapped into - their associated commits in the [`$rang`](#rang) of the the [`$raft`](#raft) - of Clay. In general, the keys of this map are exactly the numbers from 1 to - `let`, with no gaps. Of course, when there are no numbered commits, `let` is - 0, so `hit` is null. Additionally, each of the commits is an ancestor of every - commit numbered greater than this one. Thus, each is a descendant of every - commit numbered less than this one. Since it is true that the date in each - commit (`t.yaki`) is no earlier than that of each of its parents, the numbered - commits are totally ordered in the same way by both pedigree and date. If that - sounds too complicated to you, don't worry about it. It basically behaves - exactly as you would expect. -- `lab` is a map of textual labels to numbered commits. Labels must be unique - across a desk. -- `tom` contains the tombstoning policies for all files in the desk. -- `nor` is the default tombstoning policy. -- `mim` is a cache of the content in the directories that are mounted to Unix. -- `fod` is the Ford cache, which keeps a cache of the results of builds - performed at this `desk`'s current revision, including a full transitive - closure of dependencies for each completed build. -- `wic` contains commits waiting for future kernel versions. -- `liv` says whether agents on the desk are running or suspended. -- `ren` records which agents have been forced on or off, differing from the - desk's `desk.bill` manifest. - ---- - -### `$flow` - -Global Ford cache - -```hoon -+$ flow (map leak [refs=@ud =soak]) -``` - -Refcount includes references from other items in the cache, and from `spill`s in -each desk. - -This is optimized for minimizing the number of rebuilds, and given that, -minimizing the amount of memory used. It is relatively slow to lookup, because -generating a cache key can be fairly slow (for files, it requires parsing; for -`tube`s, it even requires building the marks). - ---- - -### `$flue` - -Per-desk build cache - -```hoon -+$ flue [spill=(set leak) sprig=(map mist [=leak =soak])] -``` - -- `spill` is the set of "roots" we have into the [global ford cache](#flow). We - add a root for everything referenced directly or indirectly on a desk, then - invalidate them on commit only if their dependencies change. -- `sprig` is a fast-lookup index over the global ford cache. The only goal is to - make cache hits fast. - ---- - -### `$mist` - -Ford build without content - -```hoon -+$ mist - $% [%file =path] - [%nave =mark] - [%dais =mark] - [%cast =mars] - [%tube =mars] - [%vale =path] - [%arch =path] - == -``` - -This is used at the index of `sprig`s in [`$flue`](#flue)s. - ---- - -### `$pour` - -Ford build with content. - -```hoon -+$ pour - $% [%file =path] - [%nave =mark] - [%dais =mark] - [%cast =mars] - [%tube =mars] - :: leafs - :: - [%vale =path =lobe] - [%arch =path =(map path lobe)] - == -``` - -Like a [`$mist`](#mist) except the leaf nodes (files and directories) contain -the [`$lobe:clay`](#lobeclay) (content hash). - ---- - -### `$soak` - -Ford result - -```hoon -+$ soak - $% [%cage =cage] - [%vase =vase] - [%arch dir=(map @ta vase)] - [%dais =dais] - [%tube =tube] - == -``` - -The actual data in the Ford cache. - ---- - -### `$leak` - -Ford cache key - -```hoon -+$ leak - $~ [*pour ~] - $: =pour - deps=(set leak) - == -``` - -This includes all build inputs, including transitive dependencies, recursively. - ---- - -### `$nako` +### `nako` New desk data @@ -266,7 +92,7 @@ any data you don't have yet. --- -### `$raft` +### `raft` Formal state @@ -308,7 +134,7 @@ calls to Clay is stored in this state. --- -### `$rand` +### `rand` Unvalidated response to a request. @@ -324,7 +150,7 @@ Like a [`$rant`](#rant), but with a page of data rather than a cage of it. --- -### `$rede` +### `rede` Generic desk state @@ -361,7 +187,7 @@ domestic. --- -### `$rind` +### `rind` Foreign request manager @@ -383,7 +209,7 @@ When we send a request to a foreign ship, we keep track of it in here. --- -### `$bill` +### `bill` The list of agents that should be automatically started on a desk @@ -391,7 +217,7 @@ The list of agents that should be automatically started on a desk +$ bill (list dude:gall) ``` -### `$update-state` +### `update-state` State of outstanding foreign request @@ -416,7 +242,7 @@ foreign `desk`. --- -### `$room` +### `room` Filesystem per domestic ship @@ -436,7 +262,7 @@ This is the representation of the filesystem of a ship on our pier. --- -### `$cach` +### `cach` Cached result of a request @@ -446,7 +272,7 @@ Cached result of a request --- -### `$wove` +### `wove` Stored source and request @@ -456,7 +282,7 @@ Stored source and request --- -### `$rove` +### `rove` Stored request @@ -474,12 +300,12 @@ Stored request == :: ``` -Like a [`$rave:clay`](#raveclay) but with caches of current versions for `%next` +Like a [`$rave`](#rave) but with caches of current versions for `%next` and `%many`. Generally used when we store a request in our state somewhere. --- -### `$rung` +### `rung` Foreign desk data @@ -499,60 +325,63 @@ the `desk`s we know about on their ship. These types are defined in `lull.hoon`, and are used in Clay's external interface. -### `$aeon:clay` +### `aeon` Desk revision number ```hoon -+$ aeon @ud :: version number ++$ aeon @ud ``` --- -### `$beam:clay` +### `beam` Global name ```hoon -+$ beam [[p=ship q=desk r=case] s=path] :: global name ++$ beam [[p=ship q=desk r=case] s=path] ``` The full path to a file or directory. --- -### `$beak:clay` +### `beak` Path prefix ```hoon -+$ beak [p=ship q=desk r=case] :: path prefix ++$ beak [p=ship q=desk r=case] ``` -A [`$beam:clay`](#beamclay) sans the specific file path. +A [`$beam`](#beam) sans the specific file path. --- -### `$cable:clay` +### `cable` `/lib`, `/sur` or `mark` reference ```hoon -+$ cable :: lib/sur/mark ref - $: face=(unit term) :: - file-path=term :: - == :: ++$ cable + $: face=(unit term) + file-path=term + == ``` --- -### `$care:clay` +### `care` Clay submodule ```hoon - +$ care :: clay submode - ?(%a %b %c %d %e %f %p %r %s %t %u %v %w %x %y %z) :: ++$ care + $? %a %b %c %d %e %f + %p %q %r %s %t %u + %v %w %x %y %z + == ``` This specifies what type of information is requested in a subscription @@ -567,8 +396,8 @@ or a scry. - `%e`: builds a statically typed `mark` by name (a `$nave` mark-interface core). - `%f`: builds a statically typed mark converstion gate. -- `%p`: produces the permissions for a directory, returned as a `[dict:clay - dict:clay]`. +- `%p`: produces the permissions for a directory, returned as a `[dict + dict]`. - `%r`: requests the file in the same fashion as `%x`, but wraps the result in a `vase`. - `%s`: has miscellaneous debug endpoints. @@ -580,7 +409,7 @@ or a scry. `aeon`. When used on a foreign `desk`, this get us up-to-date to the requested version. - `%w`: requests the revision number and date of the specified path, returned as - a `cass:clay`. + a `cass`. - `%x`: requests the file at a specified path at the specified commit, returned as an `@`. If there is no node at that path or if the node has no contents (that is, if `fil:ankh` is null), then this crashes. @@ -593,129 +422,144 @@ See the [scry reference](/reference/arvo/clay/scry) for more details. --- -### `$case:clay` - -Revision reference - -```hoon -+$ case :: ship desk case spur - $% [%da p=@da] :: date - [%tas p=@tas] :: label - [%ud p=@ud] :: number - == :: -``` - -A commit can be referred to in up to three ways: - -- `%da`: commit date -- `%tas`: label (seldom used currently) -- `%ud`: sequential revision number - ---- - -### `$cash:clay` +### `cash` `case` or `tako` ```hoon -+$ cash :: case or tako - $% [%tako p=tako] :: - case :: - == :: ++$ cash + $% [%tako p=tako] + case + == ``` --- -### `$cass:clay` +### `cass` Cases for revision ```hoon -+$ cass [ud=@ud da=@da] :: cases for revision ++$ cass [ud=@ud da=@da] ``` This is returned by a `%w` read. --- -### `$clue:clay` +### `clue` Tombstone target ```hoon -+$ clue :: murder weapon - $% [%lobe =lobe] :: specific lobe - [%all ~] :: all safe targets - [%pick ~] :: collect garbage - [%norm =ship =desk =norm] :: set default norm - [%worn =ship =desk =tako =norm] :: set commit norm - [%seek =ship =desk =cash] :: fetch source blobs - == :: ++$ clue + $% [%lobe =lobe] + [%all ~] + [%pick ~] + [%norm =ship =desk =norm] + [%worn =ship =desk =tako =norm] + [%seek =ship =desk =cash] + == ``` +- `%lobe`: specific [`lobe`](#lobe). +- `%all`: all safe tombstone targets. +- `%pick`: collect garbage. +- `%norm`: set default [`norm`](#norm). +- `%worn`: set [`norm`](#norm) for a specific commit. +- `%seek`: backfill tombstoned commit. + --- -### `$cone:clay` +### `cone` Domes ```hoon -+$ cone (map [ship desk] foam) :: domes ++$ cone (map [ship desk] dome) ``` --- -### `$foam:clay` +### `dome` -Desk state with additional metadata +Desk data ```hoon -+$ foam - $: dome ++$ dome + $: let=aeon + hit=(map aeon tako) + lab=(map @tas aeon) tom=(map tako norm) nor=norm + mim=(map path mime) + fod=flue + wic=(map weft yoki) liv=zest - ren=(map dude:gall ?) + ren=rein == ``` -- `dome`: desk state -- `tom`: specific tombstone policies -- `nor`: default tombstone policy -- `liv`: agent activation status -- `ren`: `%rein`'d agents (forced on/off) +A `dome` is the state of a `desk` and associated data. +- `let` is the number of the most recently numbered commit. This is also the + total number of numbered commits. +- `hit` is a map of numerical IDs to commit hashes. These hashes are mapped into + their associated commits in the [`$rang`](#rang) of the the [`$raft`](#raft) + of Clay. In general, the keys of this map are exactly the numbers from 1 to + `let`, with no gaps. Of course, when there are no numbered commits, `let` is + 0, so `hit` is null. Additionally, each of the commits is an ancestor of every + commit numbered greater than this one. Thus, each is a descendant of every + commit numbered less than this one. Since it is true that the date in each + commit (`t.yaki`) is no earlier than that of each of its parents, the numbered + commits are totally ordered in the same way by both pedigree and date. If that + sounds too complicated to you, don't worry about it. It basically behaves + exactly as you would expect. +- `lab` is a map of textual labels to numbered commits. Labels must be unique + across a desk. +- `tom` contains the tombstoning policies for all files in the desk. +- `nor` is the default tombstoning policy. +- `mim` is a cache of the content in the directories that are mounted to Unix. +- `fod` is the Ford cache, which keeps a cache of the results of builds + performed at this `desk`'s current revision, including a full transitive + closure of dependencies for each completed build. +- `wic` contains commits waiting for future kernel versions. +- `liv` says whether agents on the desk are running or suspended. +- `ren` records which agents have been forced on or off, differing from the + desk's `desk.bill` manifest. + --- -### `$crew:clay` +### `crew` Permission group ```hoon -+$ crew (set ship) :: permissions group ++$ crew (set ship) ``` --- -### `$dict:clay` +### `dict` Effective permission ```hoon - +$ dict [src=path rul=real] :: effective permission - ++$ dict [src=path rul=real] ``` --- -### `$dome:clay` +### `domo` + +Project state ```hoon -+$ dome :: project state - $: let=@ud :: top id - hit=(map @ud tako) :: changes by id - lab=(map @tas @ud) :: labels - == :: ++$ dome + $: let=@ud + hit=(map @ud tako) + lab=(map @tas @ud) + == ``` - `let`: current revision number @@ -724,24 +568,24 @@ Effective permission --- -### `$germ:clay` +### `germ` Merge strategy ```hoon -+$ germ :: merge style - $? %init :: new desk - %fine :: fast forward - %meet :: orthogonal files - %mate :: orthogonal changes - %meld :: force merge - %only-this :: ours with parents - %only-that :: hers with parents - %take-this :: ours unless absent - %take-that :: hers unless absent - %meet-this :: ours if conflict - %meet-that :: hers if conflict - == :: ++$ germ :: merge style + $? %init :: new desk + %fine :: fast forward + %meet :: orthogonal files + %mate :: orthogonal changes + %meld :: force merge + %only-this :: ours with parents + %only-that :: hers with parents + %take-this :: ours unless absent + %take-that :: hers unless absent + %meet-this :: ours if conflict + %meet-that :: hers if conflict + == :: ``` See the [Strategies](/reference/arvo/clay/using#strategies) section of "Using @@ -749,32 +593,32 @@ Clay" for further details of their meaning. --- -### `$lobe:clay` +### `lobe` File reference ```hoon -+$ lobe @uvI :: blob ref ++$ lobe @uvI ``` -This is a hash of a [`page`](#pageclay). These are most notably used in -[`lat.rang`](#rangclay), where they are associated with the actual `page`, and -as the values in [`q.yaki`](#yakiclay), where `path`s are associated with their +This is a hash of a [`page`](#page). These are most notably used in +[`lat.rang`](#rang), where they are associated with the actual `page`, and +as the values in [`q.yaki`](#yaki), where `path`s are associated with their content hashes in a commit. --- -### `$miso:clay` +### `miso` File delta ```hoon -+$ miso :: file delta - $% [%del ~] :: delete - [%ins p=cage] :: insert - [%dif p=cage] :: mutate from diff - [%mut p=cage] :: mutate from raw - == :: ++$ miso + $% [%del ~] + [%ins p=cage] + [%dif p=cage] + [%mut p=cage] + == ``` There are four kinds of changes that may be made to a node in a `desk`. @@ -790,48 +634,76 @@ There are four kinds of changes that may be made to a node in a `desk`. --- -### `$moar:clay` +### `misu` + +Computed delta + +```hoon ++$ misu + $% [%del ~] + [%ins p=cage] + [%dif p=lobe q=cage] + == +``` + +- `%del`: deletes the node. +- `%ins`: inserts file `p`. +- `%dif`: currently unimplemented. + +--- + +### `mizu` + +New state + +```hoon ++$ mizu [p=@u q=(map @ud tako) r=rang] +``` + +--- + +### `moar` Normal change range ```hoon -+$ moar [p=@ud q=@ud] :: normal change range ++$ moar [p=@ud q=@ud] ``` --- -### `$moat:clay` +### `moat` Range subscription request ```hoon -+$ moat [from=case to=case =path] :: change range ++$ moat [from=case to=case =path] ``` -This represents a request for all changes between `from` and `to` on `path`. You -will be notified when a change is made to the node referenced by the `path` or -to any of its children. +This represents a request for all changes between `from` and `to` on +`path`. You will be notified when a change is made to the node +referenced by the `path` or to any of its children. --- -### `$mode:clay` +### `mode` External files ```hoon -+$ mode (list [path (unit mime)]) :: external files ++$ mode (list [path (unit mime)]) ``` This is used when there's a commit from the host system. --- -### `$mood:clay` +### `mood` Single subscription request ```hoon -+$ mood [=care =case =path] :: request in desk ++$ mood [=care =case =path] ``` This represents a request for data related to the state of the `desk` at a @@ -840,15 +712,28 @@ information is desired, and the `path` specifies the path we are requesting. --- -### `$nori:clay` +### `mool` + +Requests in desk + +```hoon ++$ mool [=case paths=(set (pair care path))] +``` + +This is used in a `%mult` [`rave`](#rave) to specify the next +version of multiple files with multiple cares. + +--- + +### `nori` Repository action ```hoon -+$ nori :: repository action - $% [%& p=soba] :: delta - [%| p=@tas q=(unit aeon)] :: label - == :: ++$ nori + $% [%& p=soba] + [%| p=@tas q=(unit aeon)] + == ``` This describes a change that we are asking Clay to make to the `desk`. There are @@ -860,7 +745,23 @@ the given label to the commit specified in `q`, or the current one if it's null. --- -### `$norm:clay` +### `nuri` + +Repository action + +```hoon ++$ nuri + $% [%& p=suba] + [%| p=@tas] + == +``` + +Same as a [`nori`](#nori) but a [`suba`](#suba) rather +than [`soba`](#soba). + +--- + +### `norm` Tombstone policy. @@ -873,53 +774,88 @@ it should be tombstoned or not. --- -### `$page:clay` +### `open` + +Get prelude + +```hoon ++$ open $-(path vase) +``` + +This defines the type of a function for retrieving imported files. + +--- + +### `page` A raw, unvalidated file. ```hoon -+$ page ^page :: export for compat ++$ page ^page ``` This is just the `page` defined in `arvo.hoon`: a pair of a mark and a noun. --- -### `$rang:clay` +### `pour` + +Ford build with content. + +```hoon ++$ pour + $% [%file =path] + [%nave =mark] + [%dais =mark] + [%cast =mars] + [%tube =mars] + :: leafs + :: + [%vale =path =lobe] + [%arch =path =(map path lobe)] + == +``` + +Like a [`$mist`](#mist) except the leaf nodes (files and directories) contain +the [`$lobe`](#lobe) (content hash). + +--- + +### `rang` Data repository ```hoon -+$ rang :: repository - $: hut=(map tako yaki) :: changes - lat=(map lobe page) :: data - == :: ++$ rang + $: hut=(map tako yaki) + lat=(map lobe page) + == ``` This is a data repository keyed by hash. Thus, this is where the "real" data is stored, but it is only meaningful if we know the hash of what we're looking for. -`hut` is a `map` from commit hashes ([`tako`](#takoclay)s) to commits -([`yaki`](#yakiclay)s). We often get the hashes from [`hit.dome`](#domeclay), +`hut` is a `map` from commit hashes ([`tako`](#tako)s) to commits +([`yaki`](#yaki)s). We often get the hashes from [`hit.dome`](#dome), which keys them by numerical id. -`lat` is a `map` from content hashes ([`lobe`](#lobeclay)s) to the actual -content ([`page`](#pageclay)s). We often get the hashes from a -[`yaki`](#yakiclay), which references this `map` to get the data. There is no -`page` in `yaki:clay`. They are only accessible through `lat`. +`lat` is a `map` from content hashes ([`lobe`](#lobe)s) to the actual +content ([`page`](#page)s). We often get the hashes from a +[`yaki`](#yaki), which references this `map` to get the data. There is no +`page` in `yaki`. They are only accessible through `lat`. --- -### `$rant:clay` +### `rant` Response data ```hoon -+$ rant :: response to request - $: p=[p=care q=case r=desk] :: clade release book - q=path :: spur - r=cage :: data - == :: ++$ rant + $: p=[p=care q=case r=desk] + q=path + r=cage + == ``` This is the data associated to the response to a request. @@ -933,23 +869,22 @@ This is the data associated to the response to a request. --- -### `$rave:clay` +### `rave` General subscription request ```hoon -+$ rave :: general request - $% [%sing =mood] :: single request - [%next =mood] :: await next version - [%mult =mool] :: next version of any - [%many track=? =moat] :: track range - == :: ++$ rave + $% [%sing =mood] + [%next =mood] + [%mult =mool] + [%many track=? =moat] + == ``` This represents a subscription request for a `desk`. - `%sing`: asks for data at single revision. - - `%next`: asks to be notified the next time there’s a change to the specified file. - `%mult`: asks to be notified the next time there's a change to a specified set @@ -959,41 +894,41 @@ This represents a subscription request for a `desk`. --- -### `$real:clay` +### `real` Resolved permissions ```hoon -+$ real :: resolved permissions - $: mod=?(%black %white) :: - who=(pair (set ship) (map @ta crew)) :: - == :: ++$ real + $: mod=?(%black %white) + who=(pair (set ship) (map @ta crew)) + == ``` - `mod`: whether it's a blacklist or whitelist. - `who`: the ships who are blacklisted/whitelisted. It can have both individual - ships as well as [`crew`](#crewclay) (permission groups). + ships as well as [`crew`](#crew) (permission groups). --- -### `$regs:clay` +### `regs` Permission rules for paths ```hoon -+$ regs (map path rule) :: rules for paths ++$ regs (map path rule) ``` -A map from file/directory paths to permission [`rule`](#ruleclay)s. +A map from file/directory paths to permission [`rule`](#rule)s. --- -### `$rein:clay` +### `rein` Forced on/off apps ```hoon -+$ rein (map dude:gall ?) :: extra apps ++$ rein (map dude:gall ?) ``` A `dude:gall` is the name of a Gall agent and the `?` is whether it's forced on @@ -1002,12 +937,12 @@ manifest or stopped when it *is* on the manifest. --- -### `$riff:clay` +### `riff` Request/desist ```hoon -+$ riff [p=desk q=(unit rave)] :: request+desist ++$ riff [p=desk q=(unit rave)] ``` This represents a request for data about a particular `desk`. If `q` contains a @@ -1016,16 +951,30 @@ null, then this tells Clay to cancel the subscription along this duct. --- -### `$rite:clay` +### `riff-any` + +Versioned [`riff`](#riff) + +```hoon + +$ riff-any + $% [%1 =riff] + == +``` + +Currently there's only one version. + +--- + +### `rite` New permissions ```hoon -+$ rite :: new permissions - $% [%r red=(unit rule)] :: for read - [%w wit=(unit rule)] :: for write - [%rw red=(unit rule) wit=(unit rule)] :: for read and write - == :: ++$ rite + $% [%r red=(unit rule)] + [%w wit=(unit rule)] + [%rw red=(unit rule) wit=(unit rule)] + == ``` - `%r`: read permissions. @@ -1034,12 +983,12 @@ New permissions --- -### `$riot:clay` +### `riot` Response ```hoon -+$ riot (unit rant) :: response+complete ++$ riot (unit rant) ``` A `riot` is a response to a subscription. If null, the subscription has been @@ -1048,12 +997,12 @@ produced data. --- -### `$rule:clay` +### `rule` Node permission ```hoon -+$ rule [mod=?(%black %white) who=(set whom)] :: node permission ++$ rule [mod=?(%black %white) who=(set whom)] ``` - `mod`: whether it's a blacklist or whitelist. @@ -1061,12 +1010,50 @@ Node permission --- -### `$soba:clay` +### `rump` + +Relative path + +```hoon ++$ rump [p=care q=case r=@tas s=path] +``` + +--- + +### `saba` + +Patch plus merge + +```hoon ++$ saba [p=ship q=@tas r=moar s=dome] +``` + +--- + +### `soak` + +Ford result + +```hoon ++$ soak + $% [%cage =cage] + [%vase =vase] + [%arch dir=(map @ta vase)] + [%dais =dais] + [%tube =tube] + == +``` + +The actual data in the Ford cache. + +--- + +### `soba` Delta ```hoon -+$ soba (list [p=path q=miso]) :: delta ++$ soba (list [p=path q=miso]) ``` This describes a `list` of changes to make to a `desk`. The `path`s are `path`s @@ -1075,31 +1062,55 @@ the change itself. --- -### `$tako:clay` +### `suba` + +Delta + +```hoon ++$ suba (list [p=path q=misu]) +``` + +Same as a [`soba`](#soba) but with a [`misu`](misu) +rather than [`miso`](miso). + +--- + +### `tako` Commit reference ```hoon -+$ tako @uvI :: yaki ref ++$ tako @uvI ``` -This is a hash of a [`yaki`](#yakiclay), a commit. These are most notably used -as the keys in [`hut.rang`](#rangclay), where they are associated with the -actual `yaki`, and as the values in [`hit.dome`](#domeclay), where sequential -numerical ids are associated with these. +This is a hash of a [`yaki`](#yaki), a commit. These are most +notably used as the keys in [`hut.rang`](#rang), where they are +associated with the actual `yaki`, and as the values in +[`hit.dome`](#dome), where sequential numerical ids are associated +with these. --- -### `+unce:clay` +### `toro` + +General change + +```hoon ++$ toro [p=@ta q=nori] +``` + +--- + +### `unce` Change part of a list. ```hoon -++ unce :: change part - |* a=mold :: - $% [%& p=@ud] :: skip[copy] - [%| p=(list a) q=(list a)] :: p -> q[chunk] - == :: +++ unce + |* a=mold + $% [%& p=@ud] + [%| p=(list a) q=(list a)] + == ``` This is a single change in a list of elements of type `a`. For example, @@ -1110,12 +1121,12 @@ This is a single change in a list of elements of type `a`. For example, --- -### `+urge:clay` +### `urge` List change ```hoon -++ urge |*(a=mold (list (unce a))) :: list change +++ urge |*(a=mold (list (unce a))) ``` This is a parametrized type for list changes. For example, `(urge @t)` is a list @@ -1123,14 +1134,14 @@ change for lines of text. --- -### `$waft:clay` +### `waft` Kelvin range ```hoon -+$ waft :: kelvin range - $^ [[%1 ~] p=(set weft)] :: - weft :: ++$ waft + $^ [[%1 ~] p=(set weft)] + weft ``` A `waft` is the result of reading a `sys.kelvin` file in a desk. It lists all @@ -1143,84 +1154,84 @@ either be a single `weft` like `[%zuse 417]`, or a range like: --- -### `$whom:clay` +### `whom` Ship or named crew ```hoon -+$ whom (each ship @ta) :: ship or named crew ++$ whom (each ship @ta) ``` -Either a single ship or a set of ships in a [`crew`](#crewclay) (permission +Either a single ship or a set of ships in a [`crew`](#crew) (permission group). This is used for read/write permissions. --- -### `$yoki:clay` +### `yoki` Commit ```hoon - +$ yoki (each yuki yaki) :: commit ++$ yoki (each yuki yaki) ``` -Either a [`yuki`](#yukiclay) or a [`yaki`](#yakiclay). A `yuki` is a -proto-commit, a `yaki` is a final commit whose data is entirely in the general -object store. +Either a [`yuki`](#yuki) or a [`yaki`](#yaki). A `yuki` is a +proto-commit, a `yaki` is a final commit whose data is entirely in the +general object store. --- -### `$yuki:clay` +### `yuki` Proto-commit ```hoon -+$ yuki :: proto-commit - $: p=(list tako) :: parents - q=(map path (each page lobe)) :: namespace - == :: ++$ yuki + $: p=(list tako) + q=(map path (each page lobe)) + == ``` A `yuki` is a proto-commit: a new, proposed commit that has not yet been -finalized. This is in contrast to a [`yaki`](#yakiclay). The main difference is +finalized. This is in contrast to a [`yaki`](#yaki). The main difference is that a `yuki` may contain actual data, while a `yaki` only contains -[`lobe`](#lobeclay)s ( content hashes used as references to data in the general +[`lobe`](#lobe)s ( content hashes used as references to data in the general object store). - `p`: commit references of any parents. -- `q`: a `map` from file paths to either [`page`](#page:clay) data or `lobe`s. +- `q`: a `map` from file paths to either [`page`](#page) data or `lobe`s. --- -### `$yaki:clay` +### `yaki` Finalized commit ```hoon -+$ yaki :: commit - $: p=(list tako) :: parents - q=(map path lobe) :: namespace - r=tako :: self-reference - t=@da :: date - == :: ++$ yaki + $: p=(list tako) + q=(map path lobe) + r=tako + t=@da + == ``` - `p`: a `list` of the hashes of the parents of this commit. In most cases, this will be a single commit, but in a merge there may be more parents. - `q`: is a `map` of the `path`s on a desk to the content hashes at that - location. If you understand what a [`lobe`](#lobeclay) and a - [`page`](#pageclay) is, then the type signature here tells the whole story. + location. If you understand what a [`lobe`](#lobe) and a + [`page`](#page) is, then the type signature here tells the whole story. - `r`: is the hash associated with this commit. - `t`: is the date at which this commit was made. --- -### `$zest:clay` +### `zest` How live ```hoon -+$ zest $~(%dead ?(%dead %live %held)) :: how live ++$ zest $~(%dead ?(%dead %live %held)) ``` This represents the state of apps on the desk. @@ -1230,3 +1241,171 @@ This represents the state of apps on the desk. - `%live`: running. --- + +### `rock:tire` + +App states + +```hoon ++$ rock (map desk [=zest wic=(set weft)]) +``` + +- [`zest`](#zest) says whether the desk is running or + suspended. +- `wic` is the set of kernel versions ([`weft`](#weft)s) for + which the desk has queued commits awaiting kernel updates. + +--- + +### `wave:tire` + +App state changes + +```hoon ++$ wave + $% [%wait =desk =weft] + [%warp =desk =weft] + [%zest =desk =zest] + == +``` + +- `%wait`: blocked commit by [`weft`](#weft). +- `%warp`: unblocked commit by `weft`. +- `%zest`: desk is now running/suspended/held. + +--- + +### `leak` + +Ford cache key + +```hoon ++$ leak + $~ [*pour ~] + $: =pour + deps=(set leak) + == +``` + +This includes all build inputs, including transitive dependencies, +recursively. + +--- + +### `flow` + +Global Ford cache + +```hoon ++$ flow (map leak [refs=@ud =soak]) +``` + +Refcount includes references from other items in the cache, and from `spill`s in +each desk. + +This is optimized for minimizing the number of rebuilds, and given that, +minimizing the amount of memory used. It is relatively slow to lookup, because +generating a cache key can be fairly slow (for files, it requires parsing; for +`tube`s, it even requires building the marks). + +--- + +### `flue` + +Per-desk build cache + +```hoon ++$ flue [spill=(set leak) sprig=(map mist [=leak =soak])] +``` + +- `spill` is the set of "roots" we have into the [global ford cache](#flow). We + add a root for everything referenced directly or indirectly on a desk, then + invalidate them on commit only if their dependencies change. +- `sprig` is a fast-lookup index over the global ford cache. The only goal is to + make cache hits fast. + +--- + +### `mist` + +Ford build without content + +```hoon ++$ mist + $% [%file =path] + [%nave =mark] + [%dais =mark] + [%cast =mars] + [%tube =mars] + [%vale =path] + [%arch =path] + == +``` + +This is used at the index of `sprig`s in [`$flue`](#flue)s. + +--- + +### `pile` + +Preprocessed hoon source file + +```hoon ++$ pile + $: sur=(list taut) + lib=(list taut) + raw=(list [face=term =path]) + raz=(list [face=term =spec =path]) + maz=(list [face=term =mark]) + caz=(list [face=term =mars]) + bar=(list [face=term =mark =path]) + =hoon + == +``` + +- `sur`: surface imports from `/sur` (`/-`). +- `lib`: library imports from `/lib` (`/+`). +- `raw`: imports built hoon file at path (`/=`). +- `raz`: imports built hoon files from directory (`/~`). +- `maz`: imports mark definition from `/mar` (`/%`). +- `caz`: imports mark converter from `/mar` (`/$`). +- `bar`: unbuilt file imports, as mark (`/*`). +- `hoon`: the rest of the hoon file. + +--- + +### `taut` + +File import from `/lib` or `/sur` + +```hoon ++$ taut [face=(unit term) pax=term] +``` + +--- + +### `mars` + +Mark conversion request + +```hoon ++$ mars [a=mark b=mark] +``` + +From `a` to `b`. + +--- + +### `tube` + +Mark conversion gate + +```hoon ++$ tube $-(vase vase) +``` + +A gate that takes a `vase` and produces a `vase`. This is the type of +mark convertion gate returned by `%c`-care scries and read requests. + +--- + diff --git a/content/reference/arvo/clay/marks/examples.md b/content/reference/arvo/clay/marks/examples.md index 1ac45b5..0d926c6 100644 --- a/content/reference/arvo/clay/marks/examples.md +++ b/content/reference/arvo/clay/marks/examples.md @@ -5,9 +5,9 @@ weight = 4 These are the files used in the [Writing Marks](/reference/arvo/clay/marks/writing-marks) guide. -## `/lib/csv/hoon` +## `/lib/csv-utils/hoon` -```hoon +```hoon {% mode="collapse" copy="true" %} |% ++ validate :: All rows same length? |= csv=(list (list @t)) @@ -177,8 +177,8 @@ These are the files used in the [Writing Marks](/reference/arvo/clay/marks/writi ## `/mar/csv/hoon` -```hoon -/+ *csv +```hoon {% mode="collapse" copy="true" %} +/+ *csv-utils |_ csv=(list (list @t)) ++ grab |% @@ -232,7 +232,7 @@ These are the files used in the [Writing Marks](/reference/arvo/clay/marks/writi ## `/mar/csv-diff/hoon` -```hoon +```hoon {% copy="true" %} |_ dif=(urge:clay (list @t)) ++ grab |% diff --git a/content/reference/arvo/clay/marks/writing-marks.md b/content/reference/arvo/clay/marks/writing-marks.md index dd1a850..4b4a0a9 100644 --- a/content/reference/arvo/clay/marks/writing-marks.md +++ b/content/reference/arvo/clay/marks/writing-marks.md @@ -5,7 +5,7 @@ weight = 2 Here we'll walk through a practical example of writing a `mark` file. -We'll create a `mark` for CSV (comma separated values) files, a simple format for storing tabular data in a text file. Note that there's already a `csv.hoon` `mark` and library, but we'll create new ones for demonstrative purposes. It shouldn't be an issue to overwrite the existing ones on a fakezod. +We'll create a `mark` for CSV (comma separated values) files, a simple format for storing tabular data in a text file. CSV files separate fields with commas and rows with line breaks. They look something like: @@ -69,7 +69,7 @@ The `%mime` `mark` is used by Clay to store and convert `$mime` data. It's an im So with the nature of the `%mime` `mark` hopefully now clear, the reason we want conversion methods to and from `%mime` in our `%csv` `mark` is so we can import CSV files from Unix and vice versa. -Since a CSV file on Unix will just be a long string with ASCII or UTF-8 encoding, we can treat `q.q` in the `$mime` as a `cord`, and thus write a parser to convert it to a `(list (list @t))`. For this purpose, here's a library: `csv.hoon`, which you can view in full on the [Examples](/reference/arvo/clay/marks/examples#libcsvhoon) page. +Since a CSV file on Unix will just be a long string with ASCII or UTF-8 encoding, we can treat `q.q` in the `$mime` as a `cord`, and thus write a parser to convert it to a `(list (list @t))`. For this purpose, here's a library: `csv-utils.hoon`, which you can view in full on the [Examples](/reference/arvo/clay/marks/examples#libcsv-utilshoon) page. The library contains four functions: @@ -83,27 +83,27 @@ The decoding and encoding arms use parsing functions from the Hoon standard libr Let's try the library in the dojo. After we've added it to `/lib` and run `|commit`, we can build the file: ``` -> =csv -build-file %/lib/csv/hoon +> =csv -build-file %/lib/csv-utils/hoon ``` ...try decode a CSV-format `@t`: ``` -> (de-csv:csv 'foo,bar,baz\0ablah,blah,blah\0a1,2,3') +> (de-csv:csv-utils 'foo,bar,baz\0ablah,blah,blah\0a1,2,3') ~[<|foo bar baz|> <|blah blah blah|> <|1 2 3|>] ``` ...and try encode a `(list (list @t))` as a CSV-format `@t`: ``` -> (en-csv:csv [['foo' 'bar' 'baz' ~] ['blah' 'blah' 'blah' ~] ['1' '2' '3' ~] ~]) +> (en-csv:csv-utils [['foo' 'bar' 'baz' ~] ['blah' 'blah' 'blah' ~] ['1' '2' '3' ~] ~]) 'foo,bar,baz\0ablah,blah,blah\0a1,2,3\0a' ``` With that working, we can add an import for our library to our `%csv` `mark` defintion and add a `+mime` arm to both our `+grab` and `+grow` arms: ```hoon -/+ *csv +/+ *csv-utils |_ csv=(list (list @t)) ++ grab |% @@ -184,12 +184,12 @@ For demonstrative purposes, we can just poach the algorithms used in the `+grad` Our diff format will be a `(urge:clay (list @t))`, and we'll use some `differ` functions from `zuse.hoon` like `+loss`, `+lusk` and `+lurk` to produce diffs and apply patches. -The [csv.hoon library](/reference/arvo/clay/marks/examples#libcsvhoon) we imported also contains a `+csv-join` function which we'll use in the `+join` arm, just to save space here. +The [csv.hoon library](/reference/arvo/clay/marks/examples#libcsv-utilshoon) we imported also contains a `+csv-join` function which we'll use in the `+join` arm, just to save space here. Here's the new `%csv` `mark` defintion: ```hoon -/+ *csv +/+ *csv-utils |_ csv=(list (list @t)) ++ grab |% diff --git a/content/reference/arvo/clay/scry.md b/content/reference/arvo/clay/scry.md index 4e7f3ab..c891a10 100644 --- a/content/reference/arvo/clay/scry.md +++ b/content/reference/arvo/clay/scry.md @@ -49,7 +49,7 @@ Example: ### `/rang` - Get `rang` A buc scry with a path of `/rang` will return the full -[`rang`](/reference/arvo/clay/data-types#rangclay) from Clay's state. +[`rang`](/reference/arvo/clay/data-types#rang) from Clay's state. Example: @@ -79,10 +79,50 @@ Example: --- +### `/cult/[desk]` - Subscribers + +A buc scry with a path of `/cult/[desk]` will return the current subscriptions +for the specified `desk`. + +The type returned is: + +```hoon +(set [@p rave:clay]) +``` + +See the [`$rave:clay`](/reference/arvo/clay/data-types#rave) data +type entry for more details of the `$rave` structure. + +Example: + +``` +> .^((set [@p rave:clay]) %cx /=//=/cult/kids) +{ [~zod [%next mood=[care=%z case=[%da p=~2023.7.27..13.41.30..0536] path=/]]] + [~zod [%next mood=[care=%z case=[%da p=~2023.7.27..13.41.30..0536] path=/desk/docket-0]]] +} +``` + +--- + +### `/flow` - Build cache + +A buc scry with a path of `/flow` will return the global build cache. +The type returned is a +[`$flow:clay`](/reference/arvo/clay/data-types#flow). + +Example: + +``` +> ~(wyt by .^(flow:clay %cx /=//=/flow)) +960 +``` + +--- + ### `/domes` - All domes A buc scry with a path of `/domes` will return a -[`cone:clay`](/reference/arvo/clay/data-types#coneclay) containing the `dome`s +[`cone`](/reference/arvo/clay/data-types#cone) containing the `dome`s and associated metadata for all desks, foreign and local. Example: @@ -109,7 +149,7 @@ Example: A buc scry with a path of `/tire` will return the `rock:tire:clay` for all domestic desks, which is a `(map desk [=zest wic=(set weft)])`. The -[`zest`](/reference/arvo/clay/data-types#zestclay) specifies whether apps on the +[`zest`](/reference/arvo/clay/data-types#zest) specifies whether apps on the desk are running or suspended. The `wic` set contains the `weft`s (kernel versions) of any queued updates. @@ -158,7 +198,7 @@ Example: ## `%b` - Dyn. mark core -A scry with a `care` of `%b` will produce a `dais:clay` processed `mark` core +A scry with a `care` of `%b` will produce a `dais` processed `mark` core for the specified `mark`. The `path` in the scry is a `mark`. Example: @@ -169,7 +209,7 @@ Example: ## %c - Dyn. mark convert -A scry with a `care` of `%c` will produce a `tube:clay` dynamically typed `mark` +A scry with a `care` of `%c` will produce a `tube` dynamically typed `mark` conversion gate. The `path` specifies two `mark`s - _from_ and _to_, like `/txt/noun`. @@ -188,10 +228,14 @@ Example: A scry with a `care` of `%d` will return a `(set desk)` of the `desk`s that exist on your ship. +Note this scry should be performed with an empty `desk` field (`%$`) in +the `beak` (e.g. `/=//=`). If it's not empty, it'll work but Clay will +complain in the terminal. + Example: ``` -> .^((set desk) %cd %) +> .^((set desk) %cd /=//=) {%bitcoin %base %landscape %webterm %garden %kids} ``` @@ -199,7 +243,7 @@ Example: ## `%e` - Static mark core -A scry with a `care` of `%e` will return a statically typed `nave:clay` `mark` +A scry with a `care` of `%e` will return a statically typed `nave` `mark` core. The `path` in the scry specifies the `mark`. The type returned is a `(nave:clay [type] [diff])`, where `[type]` is the type the `mark` takes and `[diff]` is the type taken by the `mark` specified in `+form:grad`. @@ -229,7 +273,7 @@ A scry with a `care` of `%f` will return a static `mark` conversion gate. The A scry with a `care` of `%p` will return the permissions of the file or directory in question. The type returned is a [`[dict:clay -dict:clay]`](/reference/arvo/clay/data-types#dictclay) where the head is read +dict:clay]`](/reference/arvo/clay/data-types#dict) where the head is read permissions and the tail is write permissions. If the specified file or directory has no permissions set, it will default to @@ -285,14 +329,14 @@ you use. We'll look at each in turn. ### `%yaki` - Commit -This will return the [yaki:clay](/reference/arvo/clay/data-types#yakiclay) of +This will return the [yaki:clay](/reference/arvo/clay/data-types#yaki) of the specified commit. It takes a -[tako:clay](/reference/arvo/clay/data-types#takoclay). +[tako:clay](/reference/arvo/clay/data-types#tako). Example: -Here we scry the [dome:clay](/reference/arvo/clay/data-types#domeclay) for `%`, -get the latest `tako:clay` and the do a `%s` scry for the `yaki:clay` in +Here we scry the [dome:clay](/reference/arvo/clay/data-types#dome) for `%`, +get the latest `tako` and the do a `%s` scry for the `yaki` in question. ``` @@ -316,13 +360,13 @@ question. ### `%blob` - File blob -This will return the [page:clay](/reference/arvo/clay/data-types#pageclay) of -some file. It takes a [lobe:clay](/reference/arvo/clay/data-types#lobeclay). +This will return the [page:clay](/reference/arvo/clay/data-types#page) of +some file. It takes a [lobe:clay](/reference/arvo/clay/data-types#lobe). Example: -Here we grab the `lobe:clay` of `/gen/hood/hi/hoon` with a `%y` scry, then use -it to do a `%s` scry for the `blob:clay` of the file. +Here we grab the `lobe` of `/gen/hood/hi/hoon` with a `%y` scry, then use +it to do a `%s` scry for the `blob` of the file. ``` > =/ =arch .^(arch %cy %/gen/hood/hi/hoon) @@ -340,13 +384,13 @@ it to do a `%s` scry for the `blob:clay` of the file. ### `%hash` - Commit hash This will return the `@uvI` (256-bit) content hash of the specified commit. It -takes a [`tako:clay`](/reference/arvo/clay/data-types#takoclay). +takes a [`tako`](/reference/arvo/clay/data-types#tako). Example: -Here we grab the [`dome:clay`](/reference/arvo/clay/data-types#domeclay) for `%` +Here we grab the [`dome`](/reference/arvo/clay/data-types#dome) for `%` with a `%v` scry, get the latest -[`tako:clay`](/reference/arvo/clay/data-types#takoclay) and then do a `%s` +[`tako`](/reference/arvo/clay/data-types#tako) and then do a `%s` `%hash` scry for it. ``` @@ -360,11 +404,11 @@ with a `%v` scry, get the latest ### `%cage` - File as cage -This will return a `cage` of the data of some file. It takes a `lobe:clay`. +This will return a `cage` of the data of some file. It takes a `lobe`. Example: -Here we grab the `lobe:clay` of `/gen/hood/hi/hoon` with a `%y` scry, then use it to do a `%s` scry for the `cage` of the data. +Here we grab the `lobe` of `/gen/hood/hi/hoon` with a `%y` scry, then use it to do a `%s` scry for the `cage` of the data. ``` > =/ =arch .^(arch %cy %/gen/hood/hi/hoon) @@ -394,10 +438,10 @@ documentaton at a later date. This will return the most recent revision number of a `desk` that has been fully downloaded. The type it returns is a -[`cass:clay`](/reference/arvo/clay/data-types#cassclay). The `case` in the +[`cass`](/reference/arvo/clay/data-types#cass). The `case` in the `beak` must be a revision number rather than a date. You can just provide a case of `1` since it returns the latest regardless. If we have nothing for the -specified `desk`, this will just return the bunt of a `cass:clay` like +specified `desk`, this will just return the bunt of a `cass` like `cass=[ud=0 da=~2000.1.1]`. Example: @@ -498,7 +542,7 @@ Examples: ## `%v` - Desk state A scry with a care of `%v` will return the entire state of a `desk` as a -[`dome:clay`](/reference/arvo/clay/data-types#domeclay). +[`dome`](/reference/arvo/clay/data-types#dome). Example: @@ -516,7 +560,7 @@ Note: If you try printing this it will take forever and probably OOM your ship. A scry with a `care` of `%w` will return the revision number and date of a given `case`. The type returned is a -[`cass:clay`](/reference/arvo/clay/data-types#cassclay) like `[ud=@ud da=@da]` +[`cass`](/reference/arvo/clay/data-types#cass) like `[ud=@ud da=@da]` where `ud` is the revision number and `da` is the date. Example: @@ -530,19 +574,20 @@ Example: ## `%x` - Read file -A scry with a `care` of `%x` will return the raw data of a file as an `@` or -crash if it's a directory. +A scry with a `care` of `%x` will return the data of a file or crash if it's a +directory. The type returned will be whatever is defined by the `mark` of the +file. Examples: ``` -> .^(@ %cx %/gen/hood/hi/hoon) -3.548.750.706.400.251.607.252.023.288.575.526.190.856.734.474.077.821.289.791.377.301.707.878.697.553.411.219.689.905.949.957.893.633.811.025.757.107.990.477.902.858.170.125.439.223.250.551.937.540.468.638.902.955.378.837.954.792.031.592.462.617.422.136.386.332.469.076.584.061.249.923.938.374.214.925.312.954.606.277.212.923.859.309.330.556.730.410.200.952.056.760.727.611.447.500.996.168.035.027.753.417.869.213.425.113.257.514.474.700.810.203.348.784.547.006.707.150.406.298.809.062.567.217.447.347.357.039.994.339.342.906 +> .^(@t %cx %/gen/hood/hi/hoon) +':: Helm: send message to an urbit\0a::\0a:::: /hoon/hi/hood/gen\0a ::\0a/? 310\0a:- %say\0a|=([^ [who=ship mez=$@(~ [a=tape ~])] ~] helm-send-hi+[who ?~(mez ~ `a.mez)])\0a' ``` ``` -> .^(@t %cx %/gen/hood/hi/hoon) -':: Helm: send message to an urbit\0a::\0a:::: /hoon/hi/hood/gen\0a ::\0a/? 310\0a:- %say\0a|=([^ [who=ship mez=$@(~ [a=tape ~])] ~] helm-send-hi+[who ?~(mez ~ `a.mez)])\0a' +> .^(waft:clay %cx %/sys/kelvin) +[lal=%zuse num=413] ``` ``` @@ -557,7 +602,7 @@ Crash! A scry with a `care` of `%y` will return the `arch` of a file or directory. An `arch` is a `[fil=(unit lobe:clay) dir=(map @ta ~)]`. The `fil` will contain -the [`lobe:clay`](/reference/arvo/clay/data-types#lobeclay) hash if it's a file, +the [`lobe`](/reference/arvo/clay/data-types#lobe) hash if it's a file, otherwise it will be null. The `dir` will contain a map of the files and directories it contains, otherwise it will be null. diff --git a/content/reference/arvo/clay/tasks.md b/content/reference/arvo/clay/tasks.md index 2da03b2..6eab465 100644 --- a/content/reference/arvo/clay/tasks.md +++ b/content/reference/arvo/clay/tasks.md @@ -23,7 +23,10 @@ Clay from a kernel development perspective. A `%warp` `task` is for reading and subscribing to files and directories. -The `wer` field is the target ship. The `(unit rave)` of the [riff](/reference/arvo/clay/data-types#riff-clay-request-desist) is null to cancel an existing subscription, otherwise the [rave](/reference/arvo/clay/data-types#rave-clay-general-subscription-request) is tagged with one of: +The `wer` field is the target ship. The `(unit rave)` of the +[riff](/reference/arvo/clay/data-types#riff) is null to cancel an existing +subscription, otherwise the [rave](/reference/arvo/clay/data-types#rave) is +tagged with one of: - `%sing` - Read a single file or directory. - `%next` - Subscribe for the next change to a file or directory. @@ -48,7 +51,12 @@ A `%wris` `gift` looks like: [%writ p=riot] :: response ``` -The `unit` of the [riot](/reference/arvo/clay/data-types#riot-clay-response) will be null if the target file cannot be found or if a subscription has ended (depending on context). Otherwise it will have a [rant](/reference/arvo/clay/data-types#rant-clay-response-data) with a `cage` containing the data you requested. Its contents will vary depending on the kind of request and `care`. +The `unit` of the [riot](/reference/arvo/clay/data-types#riot) will be null +if the target file cannot be found or if a subscription has ended (depending on +context). Otherwise it will have a +[rant](/reference/arvo/clay/data-types#rant) with a `cage` containing the +data you requested. Its contents will vary depending on the kind of request and +`care`. Now we'll look at each of the `rave` request types in turn. @@ -60,9 +68,16 @@ Now we'll look at each of the `rave` request types in turn. This `rave` is for reading a single file or directory immediately. -The `care` of the [mood](/reference/arvo/clay/data-types#mood-clay-single-subscription-request) will determine what you can read and what type of data will be returned. See the [care](/reference/arvo/clay/data-types#care-clay-clay-submode) documentation and [scry](/reference/arvo/clay/scry) documentation for details on the various `care`s. +The `care` of the [mood](/reference/arvo/clay/data-types#mood) will +determine what you can read and what type of data will be returned. See the +[care](/reference/arvo/clay/data-types#care) documentation and +[scry](/reference/arvo/clay/scry) documentation for details on the various +`care`s. -The [case](/reference/arvo/clay/data-types#case-specifying-a-commit) specifies the `desk` revision and you can use whichever kind you prefer. The `path` will usually be a path to a file or directory like `/gen/hood/hi/hoon` but may be something else depending on the `care`. +The [case](/reference/arvo/clay/data-types#case) specifies the `desk` +revision and you can use whichever kind you prefer. The `path` will usually be +a path to a file or directory like `/gen/hood/hi/hoon` but may be something +else depending on the `care`. #### Example @@ -76,7 +91,9 @@ The [case](/reference/arvo/clay/data-types#case-specifying-a-commit) specifies t [%next =mood] :: await next version ``` -This subscribes to the next version of the specified file. See [here](/reference/arvo/clay/data-types#mood-clay-single-subscription-request) for details of the `mood` structure. +This subscribes to the next version of the specified file. See +[here](/reference/arvo/clay/data-types#mood) for details of the `mood` +structure. If you subscribe to the current `case` of the `desk`, Clay will not respond until the file changes. If you subscribe to a previous `case` of the `desk` and the file has changed in between then and now, it will immediately return the first change it comes across in that range. For example, if you're currently at `case` `100`, subscribe to case `50` and the file in question has been modified at both `60` and `80`, clay will immediately return the version of the file at `case` `60`. @@ -126,9 +143,17 @@ If the `track` is `%.y` it will just return a `%writ` like: ...that merely informs you of a change. If you want the actual data you'll have to request it separately. -If the `track` is `%.n`, the `cage` of the `%writ` will contain a [nako](/reference/arvo/clay/data-types#nako-subscription-response-data) with the relevant data for all changes to a desk between what you have and the `case` requested. It is very large and fairly complicated. The `nako` structure is defined in the `clay.hoon` source file itself rather than in `lull.hoon` or elsewhere since you're unlikely to work with it yourself. +If the `track` is `%.n`, the `cage` of the `%writ` will contain a +[nako](/reference/arvo/clay/data-types#nako) with the relevant data for all +changes to a desk between what you have and the `case` requested. It is very +large and fairly complicated. The `nako` structure is defined in the +`clay.hoon` source file itself rather than in `lull.hoon` or elsewhere since +you're unlikely to work with it yourself. -The `from` and `to` fields of the [moat](/reference/arvo/clay/data-types#moat-clay-range-subscription-request) specify the range of `case`s for which to subscribe. The range is _inclusive_. It can be specified by date or by revision number, whichever you prefer. +The `from` and `to` fields of the +[moat](/reference/arvo/clay/data-types#moat) specify the range of `case`s +for which to subscribe. The range is _inclusive_. It can be specified by date +or by revision number, whichever you prefer. The `path` in the `moat` is a path to a file or directory. If it's `~` it refers to the root of the `desk` in question. This lets you say "only inform me of changes to the `desk` if the specified file or directory exists". If it doesn't exist, Clay will not send you anything. @@ -164,7 +189,19 @@ To cancel a subscription, you just send a `%warp` with a null `(unit rave)` in t To write or modify a file, we send Clay a `%info` `task`. -The `%|` tag in the [nori](/reference/arvo/clay/data-types#nori-clay-repository-action) is not currently supported and will crash with a `%labelling-not-implemented` if used, so you can focus on the `%&` part. The [soba](/reference/arvo/clay/data-types#soba-clay-delta) in the `nori` is just a list of changes so you can make more than one change in one request. Its `path` is just the path to a file like `/gen/hood/hi/hoon` and the [miso](/reference/arvo/clay/data-types#miso-clay-ankh-delta) is one of these types of requests: +If the head of the [nori](/reference/arvo/clay/data-types#nori) `dit` is +`%|`, it's a request to add a label to a commit, and the `nori` looks like `[%| +p=@tas q=(unit aeon)]` where `p` is the label and `q` is the +[`aeon`](/reference/arvo/clay/data-types#aeon) (commit reference). If `q` +is null, the label is applied to the latest commit in the desk. + +If the head of the `nori` is `%&`, it's a request to add, delete or modify one +or more files in the given desk, and looks like `[%& p=soba]`. The +[soba](/reference/arvo/clay/data-types#soba) in the `nori` is just a list +of changes so you can make more than one change in one request. Its `path` is +just the path to a file like `/gen/hood/hi/hoon` and the +[miso](/reference/arvo/clay/data-types#miso) is one of these types of +requests: - `%del` - Delete a file. - `%ins` - Insert file. This will also replace an existing file. @@ -195,7 +232,7 @@ Here are examples of using each of these as well as making multiple changes in o ``` Force on/off apps on a desk. A -[`rein:clay`](/reference/arvo/clay/data-types#rein) is a `map` from Gall agent +[`rein`](/reference/arvo/clay/data-types#rein) is a `map` from Gall agent name to `?`, where `%.y` is *on* and `%.n` is *off*. By default, a live desk will run the agents defined in its `desk.bill` manifest, so this is used to either stop agents in its manifest or start agents which aren't in its manifest. @@ -228,7 +265,7 @@ A `rock:tire` is a: +$ rock (map desk [=zest wic=(set weft)]) ``` -The [`zest:clay`](/reference/arvo/clay/data-types#zestclay) says whether the +The [`zest`](/reference/arvo/clay/data-types#zest) says whether the desk is running (`%live`), suspended (`%dead`), or suspended pending a kernel-compatible update (`%held`). The `wic` set contains the `weft`s (kernel versions) of any queued updates. @@ -264,7 +301,7 @@ Try to apply a queued kernel update. ``` A `%zest` `task` suspends or unsuspends a desk. the -[`zest:clay`](/reference/arvo/clay/data-types#zestclay) in `liv` is one of: +[`zest`](/reference/arvo/clay/data-types#zest) in `liv` is one of: - `%live`: running. - `%dead`: suspended. @@ -279,7 +316,7 @@ A `%zest` `task` suspends or unsuspends a desk. the ``` Tombstoning is the deletion of data for old desk revisions. Clay has a single -`%tomb` `task`, but its [`clue:clay`](/reference/arvo/clay/data-types#clueclay) +`%tomb` `task`, but its [`clue`](/reference/arvo/clay/data-types#clue) has a number of different possible actions: ```hoon @@ -302,7 +339,7 @@ We'll look at each of these in turn. ``` A `%tomb` `task` with a `%lobe` `clue` will tombstone the `page` matching the -given [`lobe:clay`](/reference/arvo/clay/data-types#lobeclay). If the `page` in +given [`lobe`](/reference/arvo/clay/data-types#lobe). If the `page` in question is used in the current revision of any desks, it will fail. Otherwise, it will be tombstoned globally. @@ -327,7 +364,7 @@ by current desk revisions, globally. This should be used with caution. A `%tomb` `task` with a `%pick` `clue` will perform garbage collection, tombstoning any data that should be tombstoned according to current tombstoning -policy ([`norm`](/reference/arvo/clay/data-types#normclay)s). +policy ([`norm`](/reference/arvo/clay/data-types#norm)s). --- @@ -339,7 +376,7 @@ policy ([`norm`](/reference/arvo/clay/data-types#normclay)s). A `%tomb` `task` with a `%norm` `clue` will set the default tombstoning policy for the given `desk` and `ship`. A -[`norm:clay`](/referende/arvo/clay/data-types#normclay) is an `(axal ?)`. An +[`norm`](/referende/arvo/clay/data-types#norm) is an `(axal ?)`. An `axal` is like a recursive `arch`, and is defined in `arvo.hoon`. The `?` says whether to *keep* the given file or directory. You may want to look at the `+of` axal engine in `arvo.hoon` for constructing and manipulating the `norm`. @@ -358,7 +395,7 @@ make your changes. A `%tomb` `task` with a `%worn` `clue` is like [`%norm`](#norm---default-policy), except it only applies to a specific commit -for a ship/desk. The [`tako:clay`](/reference/arvo/clay/data-types#takoclay) +for a ship/desk. The [`tako`](/reference/arvo/clay/data-types#tako) denotes the commit to apply the policy. --- @@ -371,10 +408,10 @@ denotes the commit to apply the policy. A `%tomb` `task` with a `%seek` `clue` will attempt to retrieve missing, tombstoned data and integrate it into Clay's object store. The -[`cash:clay`](/reference/arvo/clay/data-types#cashclay) is a reference to a +[`cash`](/reference/arvo/clay/data-types#cash) is a reference to a commit on the given ship/desk as either a -[`tako:clay`](/reference/arvo/clay/data-types#takoclay) or a -[`case:clay`](/reference/arvo/clay/data-types#caseclay). +[`tako`](/reference/arvo/clay/data-types#tako) or a +[`case`](/reference/arvo/clay/data-types#case). --- @@ -421,7 +458,7 @@ The type it returns is a `%hill` `gift`, which looks like: A `%mont` `task` mounts the specified `beam` to the specified `term` mount point. -A `beam:clay` is the following structure: +A `beam` is the following structure: ```hoon +$ beam [[p=ship q=desk r=case] s=path] :: global name @@ -447,7 +484,7 @@ Clay does not return a `gift` in response to a `%mont` `%task`. A `%ogre` `task` unmounts the specified mount. -It's defined in `lull.hoon` as taking `$@(desk beam)` but in fact it will only unmount the target when specified as a `term` mount name. Passing it a `desk` will incidentally work if the mount is named the same as the `desk` but otherwise it won't work. Passing it a `beam:clay` will simply not work. +It's defined in `lull.hoon` as taking `$@(desk beam)` but in fact it will only unmount the target when specified as a `term` mount name. Passing it a `desk` will incidentally work if the mount is named the same as the `desk` but otherwise it won't work. Passing it a `beam` will simply not work. #### Returns @@ -532,9 +569,9 @@ If permissions are not set for a particular file, they will be inherited from th A group is called a `crew` and is just a `set` of ships with a `@ta` name. -The permissions for each file or directory are a pair of `dict:clay` where the head is read permissions and the tail is write permissions. +The permissions for each file or directory are a pair of `dict` where the head is read permissions and the tail is write permissions. -A `dict:clay` is this structure: +A `dict` is this structure: ```hoon +$ dict [src=path rul=real] :: effective permission @@ -703,7 +740,7 @@ To read files on a foreign `desk`, you just send Clay a `%warp` `task` (as you w Clay only allows a subset of `care`s to be used remotely. They are: - `%u` - Check for existence of file. -- `%v` - Get entire `dome:clay` state of a desk. +- `%v` - Get entire `dome` state of a desk. - `%w` - Get revision number. - `%x` - Get data of file. - `%y` - Get `arch` of file or directory. @@ -711,6 +748,11 @@ Clay only allows a subset of `care`s to be used remotely. They are: Any other `care` will crash with a `%clay-bad-foreign-request-care` error. +In addition, Clay only allows `%sing` and `%many` requests -- not `%next` or +`%mult`. One way to get the next revision is to first `%sing` the current +revision number using `%w` and case `da+now`, then `%sing` again with the next +revision number using case `ud+`. + The foreign ship will respond only if correct permissions have been set. See the [Permissions](#permissions) section for details. Note that if you're reading a whole `desk` or directory, all subfolders and files must also permit reading. If even a single file does not permit you reading it, the foreign ship will not respond to the request. diff --git a/content/reference/arvo/clay/using.md b/content/reference/arvo/clay/using.md index cf531e4..8926bb8 100644 --- a/content/reference/arvo/clay/using.md +++ b/content/reference/arvo/clay/using.md @@ -6,7 +6,7 @@ weight = 3 ## Reading and Subscribing When reading from Clay, there are three types of requests. A -`%sing` request asks for data at single revsion. A `%next` +`%sing` request asks for data at a single revision. A `%next` request asks to be notified the next time there's a change to given file. A `%many` request asks to be notified on every change in a `desk` for a range of changes. diff --git a/content/reference/arvo/concepts/scry.md b/content/reference/arvo/concepts/scry.md index fa85b47..a6ffe5c 100644 --- a/content/reference/arvo/concepts/scry.md +++ b/content/reference/arvo/concepts/scry.md @@ -3,17 +3,19 @@ title = "Scries" weight = 40 +++ -### What is a scry? +This document mostly covers local scries. Remote scries have recently been introduced, and are documented in a [separate guide](/guides/additional/remote-scry). + +## What is a scry? A scry is a read-only request to Arvo's global namespace. Vanes and agents define _scry endpoints_ which allow one to request data from their respective states. The endpoints can process the data in any way before returning it, but they cannot alter the actual state - scries can _only_ read, not modify. -### Why scry? +## Why scry? The subject available in something like a Gall agent or thread contains a great many functions and structures from the standard library as well as `zuse` and `lull`, but it does not include any of the actual data stored elsewhere in the ship. All it has is its own state, a `bowl` and any `card`s it has been passed. Ordinarily, in order to access such data, one would need to `%poke` or `%watch` other agents, or `%pass` `task`s to vanes, then wait for a response. Arvo's scry system is the one exception to this; it allows direct retrieval of data from other vanes or agents in situ, from any context, without any of the normal messaging rigmarole. -### How do I scry? +## How do I scry? Scries are performed exclusively with the dotket rune: `.^` @@ -25,11 +27,11 @@ One further note on `care`s (which can sometimes be confusing): While `care`s ar Most other vanes also make use of `care`s in their scry endpoints. While such vanes don't have corresponding submodules with strictly defined behaviour like Clay, the `care`s still confer the general nature of the endpoint. The most widely used `care` is `%x`, which implies reading data in a general sense. Gall has special handling of `%x` scries as described in the [Gall agents](#gall-agents) section below, but otherwise `care`s have no special behaviour for non-Clay vanes (though they must still be included if the endpoint specifies it). -### What can I scry? +## What can I scry? There are two places where scry endpoints are defined: -#### Vanes +### Vanes Each of Arvo's nine vanes (kernel modules) include a `+scry` arm which defines that vane's scry endpoints. The number of endpoints and extent of data available @@ -43,13 +45,22 @@ endpoints you'd not typically use in your applications). To explore what scry endpoints are available for vanes, you can refer to the Scry Reference section of each vane in the [Arvo](/reference/arvo/overview) section of the documents. -#### Gall agents +### Gall agents Gall has a single scry endpoint of its own to check for the existence of an agent, but otherwise all Gall scries are passed through to one of the agents it manages. The target agent to scry is specified in place of the `desk` as described in the diagram above. Each Gall agent includes a `+on-peek` arm that defines its own scry endpoints. For example, `%graph-store` has a number of scry endpoints to access the data it stores, such as chat messages and the like. Gall agents can expose scry endpoints with any `care`, but most commonly they'll take a `%x` `care`. Gall handles `%x` scries specially - it expects an extra field at the end of the `path` that specifies a `mark`. Gall will attempt to perform a `mark` conversion from the `mark` returned by the scry endpoint to the `mark` specified. Note the trailing `mark` in the `path` will not be passed through to the agent itself. -### What is an endpoint? +{% callout %} + +**Note:** you should not perform agent scries from within the +`++on-load` arm of your agent. All Gall agents are suspended during +kernel upgrade, and then reloaded one-by-one. If the agent you scry +wasn't reloaded before yours, the scry will fail. + +{% /callout %} + +## What is an endpoint? "Endpoint" refers to a specific scry path in a vane or agent. They will sometimes informally be noted in documentation or source comments like `/x/foo/bar/baz` or maybe just `/foo/bar/baz`. The first part of the former example is the `care`, then the rest is the `path` portion as noted in the diagram earlier. @@ -69,11 +80,11 @@ The case in the beginning says it takes a `%x` `care` and has a `path` of `/keys .^(json %gx /(scot %p our)/graph-store/(scot %da now)/keys/json) ``` -### Web scries +## Web scries The webserver vane Eyre has a system which allows clients like web browsers to perform scries over HTTP. For details, refer to the [Scry section of Eyre's External API Reference](/reference/arvo/eyre/external-api-ref#scry). -### Further reading +## Further reading [dotket](/reference/hoon/rune/dot#-dotket) - Documentation of the `.^` rune which performs scries. diff --git a/content/reference/arvo/dill/data-types.md b/content/reference/arvo/dill/data-types.md index 797d088..5271d46 100644 --- a/content/reference/arvo/dill/data-types.md +++ b/content/reference/arvo/dill/data-types.md @@ -7,180 +7,253 @@ Here are the data types used by Dill, as defined in `/sys/lull.hoon`. ## `$blew` +Terminal dimension. + ```hoon - +$ blew [p=@ud q=@ud] ++$ blew [p=@ud q=@ud] ``` -Terminal dimension; `p` is columns, `q` is rows. This structure is passed to Dill by the runtime in a [%blew](/reference/arvo/dill/tasks#blew) `task` whenever the dimensions of the terminal changes. +`p` is columns, `q` is rows. This structure is passed to Dill by the +runtime in a [%blew](/reference/arvo/dill/tasks#blew) `task` whenever +the dimensions of the terminal changes. + +--- ## `$belt` -```hoon -+$ belt - $? bolt - $% [%mod mod=?(%ctl %met %hyp) key=bolt] - [%txt p=(list @c)] - [%ctl p=@c] - [%met p=@c] - == == -``` - Terminal client input. -A `$belt` is passed to Dill in a [%belt](/reference/arvo/dill/tasks#belt) `task` by the runtime whenever there is input, such as a user typing in the console. This is only used between the terminal client and Dill, a [$dill-belt](#dill-belt) is used between Dill and Arvo. +```hoon ++$ belt :: client input + $? bolt :: simple input + [%mod mod=?(%ctl %met %hyp) key=bolt] :: w/ modifier + [%txt p=(list @c)] :: utf32 text + ::TODO consider moving %hey, %rez, %yow here :: + == :: +``` + +A `$belt` is passed to Dill in a +[%belt](/reference/arvo/dill/tasks#belt) `task` by the runtime whenever +there is input, such as a user typing in the console. This is only used +between the terminal client and Dill, a [$dill-belt](#dill-belt) is used +between Dill and Arvo. May either be a [$bolt](#bolt) or one of: -- `%mod` - Modifier (Ctrl, Meta or Hyper) plus [key]. -- `%txt` - A series of characters -- `%ctl` - Ctrl+[key], deprecated in favour of `%mod`. -- `%met` - Meta+[key], deprecated in favour of `%mod`. +- `%mod` - Modifier (Ctrl, Meta or Hyper) plus a key (see + [`$bolt`](#bolt). +- `%txt` - A series of UTF-32 characters. + +-- ## `$bolt` -```hoon -+$ bolt - $@ @c - $% [%aro p=?(%d %l %r %u)] - [%bac ~] - [%del ~] - [%hit r=@ud c=@ud] - [%ret ~] - == -``` - Simple input. -Either a single simple character or one of: +```hoon ++$ bolt :: simple input + $@ @c :: simple keystroke + $% [%aro p=?(%d %l %r %u)] :: arrow key + [%bac ~] :: true backspace + [%del ~] :: true delete + [%hit x=@ud y=@ud] :: mouse click + [%ret ~] :: return + == :: +``` + +Either a single UTF-32 character or one of: - `%aro` - Arrow keys. - `%bac` - Backspace key. - `%del` - Delete key. -- `%hit` - Mouse click - `r` is row and `c` is column. Note these are zero-indexed, with `[0 0]` being the _bottom left_ corner. +- `%hit` - Mouse click - `r` is row and `c` is column. Note these are + zero-indexed, with `[0 0]` being the _bottom left_ corner. - `%ret` - Return (Enter) key. +--- + ## `$blit` -```hoon -+$ blit - $% [%bel ~] - [%clr ~] - [%hop p=@ud] - [%klr p=stub] - [%lin p=(list @c)] - [%mor ~] - [%sag p=path q=*] - [%sav p=path q=@] - [%url p=@t] - == -``` - Terminal client output. -A `$blit` is given to the terminal client by Dill in a `%blit` `gift` when it wants to print some text, clear the screen, go _ding_ or what have you. +```hoon ++$ blit :: client output + $% [%bel ~] :: make a noise + [%clr ~] :: clear the screen + [%hop p=$@(@ud [x=@ud y=@ud])] :: set cursor col/pos + [%klr p=stub] :: put styled + [%mor p=(list blit)] :: multiple blits + [%nel ~] :: newline + [%put p=(list @c)] :: put text at cursor + [%sag p=path q=*] :: save to jamfile + [%sav p=path q=@] :: save to file + [%url p=@t] :: activate url + [%wyp ~] :: wipe cursor line + == :: +``` -This is only used between Dill and the terminal client, a [$dill-blit](#dill-blit) is used instead between Arvo and Dill. +A `$blit` is given to the terminal client by Dill in a `%blit` `gift` +when it wants to print some text, clear the screen, go _ding_ or what +have you. + +This is directly used between Dill and the terminal client, while a +[$dill-blit](#dill-blit) is used between Arvo and Dill. A `$dill-blit` +includes the `$blit` union as a subset. A `$blit` is one of: - `%bel` - Ring the terminal bell. - `%clr` - Clear the screen. -- `%hop` - Set cursor position, `p` specifies its position on the prompt line. +- `%hop` - Set cursor position. If `p` is an atom, it specifies the + horizontal position on the prompt line. If `p` is a cell, it + represents a 2D location where `x` is columns and `y` is + rows. - `%klr` - Set styled line, the `$stub` specifies the text and style. -- `%lin` - Set current line, `p` contains the text. -- `%mor` - Newline. -- `%sag` - Save to jamfile, typically in `/[pier]/.urb/put/`. `p` is `/[path]/[filename]/[extension]`. For example, `/foo/bar` will save it in `/[pier]/.urb/put/foo.bar`, `/a/b/c/foo/bar` will save it in `/[pier]/.urb/put/a/b/c/foo.bar`, and `/foo` will save it in `/[pier]/.urb/put.foo`. `q` is the `noun` to `jam` and save in the file. -- `%sav` - Save to file. Same behaviour as `%sag` except `q` is an `atom` rather than a `noun` and therefore doesn't need to be `jam`med. The `atom` is written to disk as if it were the bytestring in the tail of an `$octs`. That is, `%sav`ing the `cord` `'abcdef'`, whose `@ux` value is `0x6665.6463.6261`, results in a unix file whose hex dump renders as `61 62 63 64 65 66`. +- `%mor` - multiple `$blit`s. +- `%nel` - a newline. +- `%put` - put text (as a list of UTF-32 characters) at the current + cursor position. +- `%sag` - Save to jamfile, typically in `/[pier]/.urb/put/`. `p` is + `/[path]/[filename]/[extension]`. For example, `/foo/bar` will save it + in `/[pier]/.urb/put/foo.bar`, `/a/b/c/foo/bar` will save it in + `/[pier]/.urb/put/a/b/c/foo.bar`, and `/foo` will save it in + `/[pier]/.urb/put.foo`. `q` is the `noun` to `jam` and save in the + file. +- `%sav` - Save to file. Same behaviour as `%sag` except `q` is an + `atom` rather than a `noun` and therefore doesn't need to be `jam`med. + The `atom` is written to disk as if it were the bytestring in the tail + of an `$octs`. That is, `%sav`ing the `cord` `'abcdef'`, whose `@ux` + value is `0x6665.6463.6261`, results in a unix file whose hex dump + renders as `61 62 63 64 65 66`. - `%url` - Activate URL, `p` is the URL. +- `%wyp` - clear the cursor line. + +--- ## `$dill-belt` -```hoon -+$ dill-belt - $% [%aro p=?(%d %l %r %u)] - [%bac ~] - [%cru p=@tas q=(list tank)] - [%ctl p=@] - [%del ~] - [%hey ~] - [%met p=@] - [%ret ~] - [%rez p=@ud q=@ud] - [%txt p=(list @c)] - [%yow p=gill:gall] - == -``` - Terminal input for Arvo. -While [$belt](#belt) is used between the terminal client and Dill, `$dill-belt` is used between Dill and Arvo. +```hoon ++$ dill-belt :: arvo input + $% belt :: client input + [%cru p=@tas q=(list tank)] :: errmsg (deprecated) + [%hey ~] :: refresh + [%rez p=@ud q=@ud] :: resize, cols, rows + [%yow p=gill:gall] :: connect to app + == :: +``` -a `$dill-belt` is one of: +A [$belt](#belt) is used between the terminal client and Dill, while a +`$dill-belt` is used between Dill and Arvo. A `$dill-belt` includes the +`$belt` union as a subset. -- `%aro` - Arrow keys. -- `%bac` - Backspace key. -- `%cru` - Echo error, `p` is an error `@tas` and `q` is a traceback. -- `%ctl` - Ctrl+[key]. -- `%del` - Delete key. +a `$dill-belt` is either [`$belt`](#belt) or one of: + +- `%cru` - Echo error, `p` is some error tag and `q` is a stack trace. - `%hey` - Refresh. -- `%met` - Meta+[key]. -- `%ret` - Return key (Enter). - `%rez` - Terminal resized, `p` is columns and `q` is rows. -- `%txt` - Text input. - `%yow` - Connect to app. +--- + ## `$dill-blit` -```hoon -+$ dill-blit - $% [%bel ~] - [%clr ~] - [%hop p=@ud] - [%klr p=stub] - [%mor p=(list dill-blit)] - [%pom p=stub] - [%pro p=(list @c)] - [%qit ~] - [%out p=(list @c)] - [%sag p=path q=*] - [%sav p=path q=@] - [%url p=@t] - == -``` - Terminal output from Arvo. -While [$blit](#blit) is used between Dill and the terminal client, `$dill-blit` is used between Arvo and Dill. +```hoon ++$ dill-blit :: arvo output + $% blit :: client output + [%qit ~] :: close console + == :: +``` -A `$dill-blit` is one of: +While [$blit](#blit) is used between Dill and the terminal client, +`$dill-blit` is used between Arvo and Dill. A `$blit` is a subset of a +`$dill-blit`. + +A `$dill-blit` is either a [`$blit`](#blit) or a: -- `%bel` - Terminal bell. -- `%clr` - Clear screen. -- `%hop` - Set cursor position, `p` is its horizontal position on the prompt line. -- `%klr` - Styled text, the `$stub` specifies both the text and style. -- `%mor` - Multiple `$dill-blit`. -- `%pom` - Styled prompt, the `$stub` specifies both the text and style. -- `%pro` - Set prompt, `p` is the text for the prompt. - `%qit` - Close console. -- `%out` - Print text. -- `%sag` - Save `noun` to jamfile. See [$blit](#blit) section for further details. -- `%sav` - Save `atom` to file. See [$blit](#blit) section for further details. -- `%url` - Activate URL. + +--- ## `$flog` -```hoon -+$ flog - $% [%crop p=@ud] - [%crud p=@tas q=(list tank)] - [%heft ~] - [%meld ~] - [%pack ~] - [%text p=tape] - [%verb ~] - == -``` - Wrapped Dill `task`s. -These are a subset of Dill's `task`s which can be wrapped in a `%flog` `task`. See the [API Reference](/reference/arvo/dill/tasks) document for details of each of these `task`s. +```hoon ++$ flog :: sent to %dill + $% [%crop p=@ud] :: trim kernel state + $>(%crud told) :: + [%heft ~] :: + [%meld ~] :: unify memory + [%pack ~] :: compact memory + $>(%text told) :: + [%verb ~] :: verbose mode + == :: +``` + +These are a subset of Dill's `task`s which can be wrapped in a `%flog` +`task`. See the [API Reference](/reference/arvo/dill/tasks) document for +details of each of these `task`s. + +--- + +## `$poke` + +Dill to userspace. + +```hoon ++$ poke :: dill to userspace + $: ses=@tas :: target session + dill-belt :: input + == :: +``` + +A [`$dill-belt`](#dill-belt) (client input) for a particular session. + +--- + +## `$session-task` + +A subset of [Dill's `task`s](/reference/arvo/dill/tasks#session-tasks) +for interacting with a particular session. + +```hoon ++$ session-task :: session request + $% [%belt p=belt] :: terminal input + [%blew p=blew] :: terminal config + [%flee ~] :: unwatch session + [%hail ~] :: terminal refresh + [%open p=dude:gall q=(list gill:gall)] :: setup session + [%shut ~] :: close session + [%view ~] :: watch session blits + == :: +``` + +This type is used in the [`%shot`](/reference/arvo/dill/tasks#shot) +wrapper `task`. + +See the [Session Tasks](/reference/arvo/dill/tasks#session-tasks) entry +in the API reference for more details of these `task`s. + +--- + +## `$told` + +A subset of [Dill's `task`s](/reference/arvo/dill/tasks#session-tasks) +for basic text printing. + +```hoon ++$ told :: system output + $% [%crud p=@tas q=tang] :: error + [%talk p=(list tank)] :: tanks (in order) + [%text p=tape] :: tape + == :: +``` + +See the [Told Tasks](/reference/arvo/dill/tasks#told-tasks) entry +in the API reference for more details of these `task`s. + +--- diff --git a/content/reference/arvo/dill/dill.md b/content/reference/arvo/dill/dill.md index fd0ad59..89af9d6 100644 --- a/content/reference/arvo/dill/dill.md +++ b/content/reference/arvo/dill/dill.md @@ -5,35 +5,72 @@ weight = 1 The terminal driver vane. -Keyboard events and the like from Unix are received by Dill as [%belt](/reference/arvo/dill/tasks#belt) `task`s, and Dill sends `%blit` `gift`s containing [$blit](/reference/arvo/dill/data-types#blit)s back to the runtime to be displayed in the Unix terminal. The manner of interacting with Dill differs depending on whether you're in userspace or kernelspace, as we'll explore below. +Keyboard events and the like from Unix are received by Dill as +[%belt](/reference/arvo/dill/tasks#belt) `task`s, and Dill sends `%blit` +`gift`s containing [$blit](/reference/arvo/dill/data-types#blit)s back to the +runtime to be displayed in the Unix terminal. The manner of interacting with +Dill differs depending on whether you're in userspace or kernelspace, as we'll +explore below. ## Kernelspace -For technical reasons, Dill performs a handful of system tasks related to booting a ship and some memory operations. Aside from those, other Vanes mostly just pass Dill [tasks](/reference/arvo/dill/tasks) to print error messages and the like to the terminal. +For technical reasons, Dill performs a handful of system tasks related to +booting a ship and some memory operations. Aside from those, other Vanes mostly +just pass Dill [tasks](/reference/arvo/dill/tasks) to print error messages and +the like to the terminal. ## Userspace -Unlike in kernelspace, userspace applications are unlikely to `%pass` Dill `task`s directly. Instead, Dill looks at things in terms of sessions. A session is a pipeline between a client and a handler, where: +Unlike in kernelspace, userspace applications are unlikely to `%pass` Dill +`task`s directly. Instead, Dill looks at things in terms of sessions. A session +is a pipeline between a client and a handler, where: -- The client is an external input source and output sink; a terminal with with dimensions and so forth. -- The handler is an application in Urbit that interprets input, maybe does something with it, maybe produces output to be displayed in the client, etc. +- The client is an external input source and output sink; a terminal with + dimensions and so forth. +- The handler is an application in Urbit that interprets input, maybe does + something with it, maybe produces output to be displayed in the client, + etc. The handler may itself handle and multiplex terminal interfaces for + other applications, as is the case with the `%hood` module `%drum`, or it + may be a stand-alone application. -There are plans for Dill to support multiple independent sessions (i.e. independent terminal clients displaying and processing different information at the same time), but at this stage Dill only supports a single session. The client for this default session is just the Unix terminal via the Urbit runtime. The handler for the default session is the `%hood` app, whose `drum` module does the heavy lifting. +Currently, Dill supports multiple *sessions*, but Vere (the runtime) only +supports a single Unix terminal *client* for the default session (`%$`). This +means any non-default sessions will need to be linked through the default +session handler `%drum` (a module of the `%hood` app) if they are to work in +the Unix terminal. Alternatively, a client could be built that talks to Dill +via the HTTP server Eyre in a similar way to the `%webterm` app, and +interacts with sessions entirely separately from the Unix terminal and its +`%drum` handler. -`drum` is the CLI app manager. By default you'll have a couple of CLI applications running—`%dojo` and `%chat-cli`—which you can switch between with `ctrl+x`. You may also have additional CLI apps which you have attached with the `|link` command. It's `drum` that keeps track of which one is active, which one input should be routed to, which one should be displayed, what each prompt should look like, and so forth. Dill itself is oblivious to the distinction between these CLI apps. It only sees the default session with `drum`, so it just passes all input to `drum` and display whatever `drum` gives it. +`%drum` is Arvo's CLI app manager. By default you'll have one CLI application +running: the `%dojo`. You may also have additional CLI apps which you have +started or attached with the `|link` command. It's `%drum` that keeps track of +which one is active, which one input should be routed to, which one should be +displayed, what each prompt should look like, and so forth. Dill itself is +oblivious to the distinction between these CLI apps. It only sees the session +with `%drum`, so it just passes all input to `%drum` and display whatever +`%drum` gives it. -While `drum` talks with Dill in `$dill-belt`s and `$dill-blit`s, it talks to CLI apps with `$sole-action`s and `$sole-event`s, which are defined in the `sole` library. For more information on the `sole` library and the related `shoe` library, and for information on how to build CLI apps, you can refer to the [CLI app tutorial](/guides/additional/cli-tutorial). +While `%drum` talks with Dill in `$dill-belt`s and `$dill-blit`s, it talks to +CLI apps with `$sole-action`s and `$sole-event`s, which are defined in the +`sole` library. For more information on the `sole` library and the related +`shoe` library, and for information on how to build CLI apps, you can refer to +the [CLI app tutorial](/guides/additional/cli-tutorial). -To give a basic idea of how keyboard events flow through these systems and produce terminal output, here's a diagram showing the messages in pseudo-Hoon: +To give a basic idea of how keyboard events flow through these systems and +produce terminal output, here's a diagram showing the messages in pseudo-Hoon: ![Dill userspace diagram](https://media.urbit.org/docs/arvo/dill/dill-userspace.svg) -You can use a [move trace](/reference/arvo/tutorials/move-trace) to get a hands-on feel for this data flow. +You can use a [move trace](/reference/arvo/tutorials/move-trace) to get a +hands-on feel for this data flow. ## Sections -[API Reference](/reference/arvo/dill/tasks) - The `task`s Dill takes and the `gift`s it returns. +[API Reference](/reference/arvo/dill/tasks) - The `task`s Dill takes and the +`gift`s it returns. [Scry Reference](/reference/arvo/dill/scry) - The scry endpoints of Dill. -[Data Types](/reference/arvo/dill/data-types) - Reference documentation of the data types used by Dill. +[Data Types](/reference/arvo/dill/data-types) - Reference documentation of the +data types used by Dill. diff --git a/content/reference/arvo/dill/scry.md b/content/reference/arvo/dill/scry.md index b314b8c..3f71faf 100644 --- a/content/reference/arvo/dill/scry.md +++ b/content/reference/arvo/dill/scry.md @@ -3,42 +3,39 @@ title = "Scry Reference" weight = 3 +++ -Here are the scry endpoints of Dill. They take a `%x` `care` and require the `desk` in the path prefix be empty, so the general format is `.^([type] %dx /=//=/[some-path])`. +Here are the scry endpoints of Dill. They require the `desk` in the path prefix +be empty, so the general format is `.^([type] %d[care] /=//=/[some-path])`. -Dill only has a couple of scry endpoints, both of which begin with `/session//`. The empty `//` would be where the target session would be specified, but at this stage Dill can only return a result for the default session, so it's always empty. +## `%x` - `/sessions` -## /sessions//line +Get all sessions. -A scry with a `care` of `%x` and a `path` of `/sessions//line` returns the current text of the prompt line of the default session. The type returned is a [$blit](/reference/arvo/dill/data-types#blit). +A scy with a `care` of `%x` and a `path` of `/sessions` returns a `(set @tas)` +of the current sessions. The `%$` session is the default session. #### Example ``` -> .^(blit:dill %dx /=//=/sessions//line) -[ %lin - p - ~[ - ~-~~ - ~-z - ~-o - ~-d - ~-~3a. - ~-d - ~-o - ~-j - ~-o - ...truncated for brevity... - ] -] +> .^((set @tas) %dy /=//=/sessions) +{%$} ``` -## /sessions//cursor +--- -A scry with a `care` of `%x` and a `path` of `/sessions//cursor` returns the current horizontal position of the cursor in the default session. The type returned is a `@ud`. +## `%u` - `/sessions/[ses]` + +Does session exist? + +A scry with a `care` of `%x` and a `path` of `/sessions/[ses]` where `[ses]` is +a session name returns a `?` of whether `[ses]` exists. #### Example ``` -> .^(@ud %dx /=//=/sessions//cursor) -44 +> .^(? %du /=//=/sessions/$) +%.y +> .^(? %du /=//=/sessions/foo) +%.n ``` + +--- diff --git a/content/reference/arvo/dill/tasks.md b/content/reference/arvo/dill/tasks.md index 7433c2e..b25feeb 100644 --- a/content/reference/arvo/dill/tasks.md +++ b/content/reference/arvo/dill/tasks.md @@ -3,339 +3,478 @@ title = "API Reference" weight = 2 +++ -In this document we describe the public interface for Dill. Namely, we describe each `task` that Dill can be `pass`ed, and which `gift`(s) Dill can `give` in return. +In this document we describe the public interface for Dill. Namely, we +describe each `task` that Dill can be `pass`ed, and which `gift`(s) Dill +can `give` in return. -## `%belt` +Dill's `task`s are organized into three categories: [Session +`task`s](#session-tasks) for interacting with a particular session, +[Told `task`s](#told-tasks) for basic terminal printing, and +[system/miscellaneous `task`s](#system-misc-tasks) which are a combination of +general system `task`s and Dill `task`s which don't fit in the previous +two categories. -```hoon -[%belt p=belt] -``` +## System & Misc. Tasks -Terminal input. +These are the Dill `task`s not otherwise categorized as [session +tasks](#session-tasks) or [told tasks](#told-tasks). Most of them are +interfaces for Vere or Arvo and do thing like toggle verbose mode, +defragment state, etc. The most notable Dill-specific `task` is +[`%shot`](#shot), which is used as a wrapper for the [session +tasks](#session-tasks) to specify the session. The [`%logs`](#logs) +`task` is also useful, letting you subscribe to all system output. -The runtime sends a `%belt` `task` to Dill whenever there is input in the terminal, such as a keystroke. - -The [$belt](/reference/arvo/dill/data-types#belt) in `p` contains the input such as which key was pressed. - -Dill will convert the `$belt` into a [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke` the session handler (typically `drum`) with it. - -This `task` would not typically be used from userspace. - -#### Returns - -Dill returns no `gift` in response to a `%belt` `task`. - -## `%blew` - -```hoon -[%blew p=blew] -``` - -Terminal resized. - -The runtime passes Dill a `%blew` `task` whenever the terminal is resized. - -The [$blew](/reference/arvo/dill/data-types#blew) specifies the new dimensions. - -Dill will convert the `$blew` into a `%rez` [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the session handler (typically `drum`) with it. - -This `task` would not typically be used from userspace. - -#### Returns - -Dill returns no `gift` in response to a `%blew` `task`. - -## `%boot` +### `%boot` ```hoon [%boot lit=? p=*] ``` -This `task` is used only once, when Arvo first enters the [adult stage](/reference/arvo/overview#structural-interface-core). Dill is technically the first vane to be activated, via the `%boot` `task`, which then sends Jael (considered the "true" first vane) the `%dawn` or `%fake` `task` wrapped in the `%boot` `task`. Jael then goes on to call `%init` `task`s for other vanes (including Dill). +This `task` is used only once, when Arvo first enters the [adult +stage](/reference/arvo/overview#structural-interface-core). Dill is +technically the first vane to be activated, via the `%boot` `task`, +which then sends Jael (considered the "true" first vane) the `%dawn` or +`%fake` `task` wrapped in the `%boot` `task`. Jael then goes on to call +`%init` `task`s for other vanes (including Dill). -`lit` specifies whether to boot in lite mode. `p` is either a [%dawn](/reference/arvo/jael/data-types#dawn) or [%fake](/reference/arvo/jael/tasks#fake) `task:jael`. `%dawn` is for an ordinary boot and `%fake` is for booting a fake ship. +`lit` specifies whether to boot in lite mode. `p` is either a +[%dawn](/reference/arvo/jael/tasks#dawn) or +[%fake](/reference/arvo/jael/tasks#fake) `task:jael`. `%dawn` is for an +ordinary boot and `%fake` is for booting a fake ship. This `task` would not be used from userspace. #### Returns -Dill returns no `gift` in response to a `%boot` `task`, but it will `%pass` the wrapped `%dawn` or `%fake` `task` to Jael. +Dill returns no `gift` in response to a `%boot` `task`, but it will +`%pass` the wrapped `%dawn` or `%fake` `task` to Jael. -## `%crop` +--- + +### `%crop` + +Trim kernel state. ```hoon [%crop p=@ud] ``` -Trim kernel state. - -This `task` is the same as the [%trim](#trim) `task`. Like `%trim`, Dill does nothing with it. +This `task` is the same as the `%trim` vane `task`. Like `%trim`, Dill +does nothing with it. #### Returns Dill returns no `gift` in response to a `%crop` `task`. -## `%crud` +--- -```hoon -[%crud p=@tas q=(list tank)] -``` - -Print error. - -Dill prints the given error to the terminal. The verbosity for the particular `@tas` error tag specified in `p` is determined by the `level` set by a [%knob](#knob) `task` - either `%hush`, `%soft` or `%loud`. The default is `%loud`, where it will print the full `(list tank)` specified in `q`. See [%knob](#knob) for details of verbosity levels. - -#### Returns - -Dill does not return a `gift` in response to a `%crud` `task`. - -## `%flee` - -```hoon -[%flee session=~] -``` - -Unwatch a session to which you've previously subscribed with [%view](#view). - -The ability to specify a session is not yet implemented in Dill, so `session` is always `~`, the default session. - -#### Returns - -Dill does not return a `gift` in response to a `%flee` `task`. - -## `%flog` +### `%flog` ```hoon [%flog p=flog] ``` -A `%flog` `task` is a wrapper over a `task` sent by another vane. Dill removes the wrapper and sends the bare `task` to itself over the default `duct`. +A `%flog` `task` is a wrapper over a `task` sent by another vane. Dill +removes the wrapper and sends the bare `task` to itself over the default +`duct`. -A `%flog` `task` takes a [$flog](/reference/arvo/dill/data-types#flog) as its argument. A `$flog` is a subset of Dill's `task`s. +A `%flog` `task` takes a [$flog](/reference/arvo/dill/data-types#flog) +as its argument. A `$flog` is a subset of Dill's `task`s. #### Returns Dill does not return a `gift` in response to a `%flog` `task`. -## `%flow` +--- -```hoon -[%flow p=@tas q=(list gill:gall)] -``` +### `%heft` -Terminal config. - -This `task` is not used. - -## `%hail` - -```hoon -[%hail ~] -``` - -Refresh. - -Dill converts a `%hail` `task` into a `%hey` [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the session handler (typically `drum`) with it to handle the refresh. - -This `task` would not be used from userspace. - -#### Returns - -Dill returns no `gift` in response to a `%hail` `task`. - -## `%heft` +Produce memory report. ```hoon [%heft ~] ``` -Produce memory report. - -When Dill receives a `%heft` `task` it passes Arvo a `%whey` `waif` to obtain a memory report and prints it to the terminal. +When Dill receives a `%heft` `task` it passes Arvo a `%whey` `waif` to +obtain a memory report and prints it to the terminal. #### Returns Dill does not return a `gift` in response to a `%heft` `task`. -## `%hook` +--- + +### `%logs` + +Watch system output. ```hoon -[%hook ~] +[%logs p=(unit ~)] ``` -This terminal hung up. - -This task is not used. - -## `%harm` - -```hoon -[%harm ~] -``` - -All terminals hung up. - -This `task` is not used. - -## `%init` - -```hoon -[%init ~] -``` - -This `task` is called only once, when Arvo first enters the [adult stage](/reference/arvo/overview#structural-interface-core). It performs initial setup for Dill, such as setting the width of the console. - -Note that this is not actually the first `task` passed to Dill - see [%boot](#%boot). - -This `task` would not be used from userspace. +A non-null `p` subscribes to system output, and a null `p` unsubscribes. +While subscribed, you'll receive each piece of output in a `%logs` gift +as it occurs. #### Returns -Dill does not return a `gift` in response to a `%init` `task`. +Dill does not immediately return anything, but will give you a `%logs` +gifts each time system output occurs. A `%logs` gift looks like: -## `%meld` +```hoon +[%logs =told] +``` -``` -[%meld ~] -``` +A `$told` is either a [`%crud`](#crud), [`%talk`](#talk) or +[`%text`](#text) task. See the +[`$told`](/reference/arvo/dill/data-types#dill-belt) entry in the data +types reference for more details. + +--- + +### `%meld` Deduplicate persistent state. +```hoon +[%meld ~] +``` + Dill asks the runtime to perform the memory deduplication. #### Returns Dill does not return a `gift` in response to a `%meld` `task`. -## `%noop` +--- -```hoon -[%noop ~] -``` +### `%pack` -No operation. - -A `%noop` `task` does nothing, as the name implies. - -#### Returns - -Dill does not return a `gift` in response to a `%noop` `task`. - -## `%pack` +Defragment persistent state. ```hoon [%pack ~] ``` -Defragment persistent state. - Dill asks the runtime to perform the defragmentation. #### Returns Dill does not return a `gift` in response to a `%meld` `task`. -## `%talk` +--- + +### `%seat` + +Install desk. ```hoon -[%talk p=tank] +[%seat =desk] ``` -This `task` is not used. - -## `%text` - -```hoon -[%text p=tape] -``` - -Print `tape` to terminal. - -Upon receiving a `%text` `task`, Dill will convert the `tape` given in `p` to a `(list @c)` and give it to the runtime in a `%blit` `gift` including a `%lin` [$blit](/reference/arvo/dill/data-types#blit). +This indirectly pokes `%hood` with a `%kiln-install` `mark` to install +the specified `desk`. You should just poke `%hood` directly rather than +using this. #### Returns -Dill does not return a `gift` in response to a `%text` `task`, but it does give a `%blit` `gift` to the runtime which looks like: +Dill does not return a `gift` in response to a `%seat` `task`. + +--- + +### `%shot` + +Task for session. ```hoon -[%blit p=(list blit)] +[%shot ses=@tas task=session-task] ``` -## `%view` +A `$session-task` is one of these `task`s: [`%belt`](#belt), +[`%blew`](#blew), [`%flee`](#flee), [`%hail`](#hail), [`%open`](#open), +[`%shut`](#shut), [`%view`](#view). See the +[`$session-task`](/reference/arvo/dill/data-types#session-task) entry in +the data types reference. -```hoon -[%view session=~] -``` +--- -Watch session. +### `%verb` -A `%view` `task` subscribes for a copy of all `%blit` `gift`s which Dill `%give`s for the default session. This `task` is used by the `%herm` app so it can convert the `$blit`s to JSON and render them in the web terminal. - -The ability to specify a session is not yet implemented in Dill, so `session` is always `~`, the default session. - -#### Returns - -Dill will `%give` a copy of all `%blit`s for the default session. A `%blit` `gift` is: - -```hoon -[%blit p=(list blit)] -``` - -## `%trim` - -```hoon -[%trim p=@ud] -``` - -`%trim` is a common vane `task` used to reduce memory usage. It does nothing for Dill because Dill only keeps a minimal necessary state. - -#### Returns - -Dill does not return a `gift` in response to a `%trim` `task`. - -## `%vega` - -```hoon -[%vega ~] -``` - -This is a common vane `task` used to inform the vane that the kernel has been -upgraded. Dill does not do anything in response to this. - -This `task` would not be used from userspace. - -#### Returns - -Dill returns no `gift` in response to a `%vega` `task`. - -## `%verb` +Toggle Arvo verbose mode. ```hoon [%verb ~] ``` -This `task` toggles verbose mode for all of Arvo, which is located here since -Dill is the vane that prints errors. To be precise, `%verb` toggles the laconic -bit `lac` in the [Arvo state](/reference/arvo/overview#the-state) by passing a `%verb` `waif` to Arvo. +This `task` toggles verbose mode for all of Arvo, which is located here +since Dill is the vane that prints errors. To be precise, `%verb` +toggles the laconic bit `lac` in the [Arvo +state](/reference/arvo/overview#the-state) by passing a `%verb` `waif` +to Arvo. #### Returns Dill does not return a `gift` in response to a `%verb` `task`. -## `%knob` +--- + +## Session Tasks + +These `task`s are for interacting with a particular session. These all +would normally be wrapped in a [`%shot`](#shot) `task` to specify the +session, rather than sent directly. + +This subset of Dill's `task`s are defined in the [`$session-task` +type](/reference/arvo/dill/data-types#session-task). + +### `%belt` + +Terminal input. ```hoon -[%knob tag=term level=?(%hush %soft %loud)] +[%belt p=belt] ``` -`%knob` sets the verbosity level for each error tag. The error `tag` can be any `@tas`. The given `tag` and `level` will be added to Dill's `veb` which maps tags to levels. Subsequent [%crud](#crud) `task`s will then print with the specified verbosity. +The [$belt](/reference/arvo/dill/data-types#belt) in `p` contains the +input such as which key was pressed. Dill will convert the `$belt` into +a [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke` it +into the session handler agent for the session in question. -The `level`s behave like so: - -- `%hush` - Completely silent, print nothing. -- `%soft` - Just print `crud: %error-tag event failed`, ignore any `tang` given in the `%crud`. -- `%loud` - Print the `%soft` message as well as the full `tang` given in the `%crud` `task`. +This `task` should be wrapped in a [`%shot`](#shot) `task` to specify +the session. Without the `%shot` wrapper, it will use the default +session (`%$`). #### Returns -Dill does not return a `gift` in response to a `%knob` `task`. +Dill returns no `gift` in response to a `%belt` `task`. + +--- + +### `%blew` + +Terminal resized. + +```hoon +[%blew p=blew] +``` + +The [$blew](/reference/arvo/dill/data-types#blew) specifies the new +dimensions. + +Dill will convert the `$blew` into a `%rez` +[$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the +session handler (typically `drum`) with it. + +This `task` would not typically be used from userspace. Instead, it +would come in from the runtime for the default session (`%$`) when the +actual terminal were resized. If in an odd scenario it were used from +userspace, it would need to be wrapped in a [`%shot`](#shot) `task` to +specify a session other than `%$`. + +#### Returns + +Dill returns no `gift` in response to a `%blew` `task`. + +--- + +### `%flee` + +Unwatch a session to which you've previously subscribed with +[%view](#view). + +```hoon +[%flee ~] +``` + +This `task` must be wrapped in a [`%shot`](#shot) `task` in order to +specify the session. Without that, it will default to the default +session (`%$`). The subscription to end will be determined implicitly by +the `duct`. + +#### Returns + +Dill does not return a `gift` in response to a `%flee` `task`. + +--- + +### `%hail` + +Refresh. + +```hoon +[%hail ~] +``` + +Dill converts a `%hail` `task` into a `%hey` +[$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the +session handler (typically `%drum`) with it. + +This `task` would not typically be used from userspace. If in an odd +scenario it were used from userspace, it would need to be wrapped in a +[`%shot`](#shot) `task` to specify a session other than `%$`. + +#### Returns + +Dill returns no `gift` in response to a `%hail` `task`. + +--- + +### `%open` + +Setup session. + +```hoon +[%open p=dude:gall q=(list gill:gall)] +``` + +This `task` is always wrapped in a [`%shot`](#shot) `task`, and creates +the new session specified in that wrapper. If it's not wrapped in a +`%shot` task, it will default to the default session (`%$`) and fail +because it already exists. This `task` is designed to be used by a +userspace session handler like `%drum` that multiplexes terminal +interfaces for multiple userspace applications, but could also be used +by a stand-alone application that talks to Dill directly. + +The Gall agent specified in `p` is the session handler or stand-alone +application. The `q` field contains a list of `gill:gall`, which are +pairs of `[ship term]`, representing an app on a ship. The `gill`s in +`q` are the list of apps being managed by the session handler `q` that +should be notified of being connected to this session. If `p` were a +stand-alone application, `q` could be empty, or else just contain that +one app. + +Dill will poke every agent listed in `q` (local or remote) with a +[`%yow` `$dill-belt`](/reference/arvo/dill/data-types#dill-belt), to let +it know it's been connected. It will also `%watch` the agent `p` for +[`$dill-blit`](/reference/arvo/dill/data-types#dill-blit)s in `%fact`s +with a `%dill-blit` `mark` on the `path` `/dill/[ses]` where `ses` is +the session specified in the [`%shot`](#shot) wrapper. + +Additionally, the source of the `%open` request (as determined by the +`duct`, typically the agent in `p`) will begin receiving terminal output +gifts for the session in question. Essentially, it behaves as though you +also passed it a [`%view`](#view) task. + +#### Returns + +Dill does not give a `gift` in direct response to an `%open` `task`. It +will, however, start giving terminal output `%blit` `gift`s to the +requester as the occur for the session. A `%blit` `gift` looks like: + +```hoon +[%blit p=(list blit)] +``` + +See the [`$blit`](/reference/arvo/dill/data-types#blit) entry in the +data type reference for details of what it can contain. + +This subscription for `$blit`s can be stopped with a [`%flee`](#flee) +`task` at any time. + +--- + +### `%shut` + +Close session. + +```hoon +[%shut ~] +``` + +This `task` needs to be wrapped in [`%shot`](#shot) `task` to specify +the session to close. Otherwise, it will default to the default session +(`%$`). + +The session handler will be passed a `%leave`. Subscribers for `$blit`s +will not be notified, they'll just stop receiving `$blit`s. + +#### Returns + +Dill does not give a `gift` in response to a `%shut` `task`. It will, +however, pass a `%leave` to the session handler agent. + +--- + +### `%view` + +Watch session. + +```hoon +[%view ~] +``` + +A `%view` `task` subscribes for a copy of all `%blit` `gift`s which Dill +`%give`s for the session in question. This `task` must be wrapped in a +[`%shot`](#shot) `task` which specifies the session if you want to +subscribe to anything other than the default session (`%$`). + +#### Returns + +Dill will begin giving a copy of all `%blit`s for the session specified +in the [`%shot`](#shot) wrapper, or the default session (`%$`) if a +`%shot` wrapper is not used. A `%blit` `gift` is: + +```hoon +[%blit p=(list blit)] +``` + +See the [`$blit`](/reference/arvo/dill/data-types#blit) entry in the +data type reference for more details. + +The subscription can be ended with a [`%flee`](#flee) `task`. + +--- + +## Told Tasks + +This subset of Dill `task`s are for printing things. They are defined in +the [`$told` type](/reference/arvo/dill/data-types#told). + +### `%crud` + +Print error. + +```hoon +[%crud p=@tas q=tang] +``` + +Dill prints the given error to the terminal. + +#### Returns + +Dill does not return a `gift` in response to a `%crud` `task`. + +--- + +### `%talk` + +Print `tang` to terminal. + +```hoon +[%talk p=(list tank)] +``` + +The `tank`s in `p` will be printed to the terminal, first to last. + +#### Returns + +Dill does not return a `gift` in response to a `%talk` `task`. + +--- + +### `%text` + +Print `tape` to terminal. + +```hoon +[%text p=tape] +``` + +The `tape` in `p` will be printed to the terminal. + +#### Returns + +Dill does not return a `gift` in response to a `%text` `task`. + +--- + diff --git a/content/reference/arvo/eyre/data-types.md b/content/reference/arvo/eyre/data-types.md index 25d5ba3..eaf2795 100644 --- a/content/reference/arvo/eyre/data-types.md +++ b/content/reference/arvo/eyre/data-types.md @@ -11,6 +11,24 @@ This document describes the data types used by Eyre as defined in `/sys/lull.hoo ## Eyre +### `$cache-entry` + +```hoon ++$ cache-entry + $: auth=? + $= body + $% [%payload =simple-payload:http] + == == +``` + +- `auth`: Whether the request must include a valid session cookie or otherwise + be authenticated. If this is false, the entry will be publicly accessible. +- `body`: The HTTP response to give. This contains a `[%payload + =simple-payload:http]`. See the [`$simple-payload:http`](#simple-payloadhttp) + for more details of the data. + +--- + ### `$origin` ```hoon @@ -19,6 +37,8 @@ This document describes the data types used by Eyre as defined in `/sys/lull.hoo A single CORS origin as used in an HTTP Origin header and the [$cors-registry](#cors-registry). +--- + ### `$cors-registry` ```hoon @@ -31,6 +51,8 @@ A single CORS origin as used in an HTTP Origin header and the [$cors-registry](# CORS origins categorised by approval status. The `requests` `set` contains all [$origin](#origin)s Eyre has received in the headers of HTTP requests that have not been explicitly approved or rejected. The `approved` and `rejected` `set`s are those that have been explicitly approved or rejected. +--- + ### `$outstanding-connection` ```hoon @@ -44,6 +66,8 @@ CORS origins categorised by approval status. The `requests` `set` contains all [ An HTTP connection that is currently open. The [$action](#action) is how it's being handled (e.g. by a Gall app, the channel system, etc). The [$inbound-request](#inbound-request) is the original request which opened the connection. The `response-header` contains the status code and headers. The `bytes-sent` is the total bytes sent so far in response. +--- + ### `$authentication-state` ```hoon @@ -52,6 +76,8 @@ An HTTP connection that is currently open. The [$action](#action) is how it's be This represents the authentication state of all sessions. It maps session cookies (without the `urbauth-{SHIP}=` prefix) to [$session](#session)s. +--- + ### `$session` ```hoon @@ -63,6 +89,8 @@ This represents the authentication state of all sessions. It maps session cookie This represents server-side data about a session. The `expiry-time` is when the `session` expires and `channels` is the `set` of [$channel](#channel) names opened by the session. +--- + ### `$channel-state` ```hoon @@ -74,6 +102,8 @@ This represents server-side data about a session. The `expiry-time` is when the The state used by the channel system. The `session` is a `map` between channel names and [$channel](#channel)s and the `duct-to-key` `map`s `duct`s to `channel` names. +--- + ### `$timer` ```hoon @@ -85,6 +115,8 @@ The state used by the channel system. The `session` is a `map` between channel n A reference to a timer so it can be cancelled or updated. The `date` is when it will fire and the `duct` is what set the timer. +--- + ### `$channel-event` ```hoon @@ -98,21 +130,42 @@ A reference to a timer so it can be cancelled or updated. The `date` is when it An unacknowledged event in the channel system. +--- + ### `$channel` ```hoon -+$ channel - $: state=(each timer duct) - next-id=@ud - last-ack=@da - events=(qeu [id=@ud request-id=@ud =channel-event]) - unacked=(map @ud @ud) - subscriptions=(map @ud [ship=@p app=term =path duc=duct]) - heartbeat=(unit timer) - == + +$ channel + $: mode=?(%json %jam) + state=(each timer duct) + next-id=@ud + last-ack=@da + events=(qeu [id=@ud request-id=@ud =channel-event]) + unacked=(map @ud @ud) + subscriptions=(map @ud [ship=@p app=term =path duc=duct]) + heartbeat=(unit timer) + == + ``` -This is the state of a particular channel in the channel system. The `state` is either the expiration time or the duct currently listening. The `next-id` is the next event ID to be used in the event stream. The `last-ack` is the date of the last client ack and is used for clog calculations in combination with `unacked`. The `events` queue contains all unacked events - `id` is the server-set event ID, `request-id` is the client-set request ID and the [$channel-event](#channel-event) is the event itself. The `unacked` `map` contains the unacked event count per `request-id` and is used for clog calculations. The `subscriptions` `map` contains gall subscriptions by `request-id`. The `heartbeat` is the SSE heartbeat [$timer](#timer). +This is the state of a particular channel in the channel system. + +- `mode` says whether the channel sends/received JSON or + [nouns](/guides/additional/noun-channels). +- `state` is either the expiration time or the duct currently listening. +- `next-id` is the next event ID to be used in the event stream. +- `last-ack` is the date of the last client ack and is used for clog + calculations in combination with `unacked`. +- `events` queue contains all unacked events: + - `id` is the server-set event ID. + - `request-id` is the client-set request ID. + - [$channel-event](#channel-event) is the event itself. +- `unacked` `map` contains the unacked event count per `request-id` and is used + for clog calculations. +- `subscriptions` `map` contains gall subscriptions by `request-id`. +- `heartbeat` is the SSE heartbeat [$timer](#timer). + +--- ### `$binding` @@ -125,22 +178,27 @@ This is the state of a particular channel in the channel system. The `state` is A `binding` is a rule to match a URL `path` and optional `site` domain which can then be tied to an [$action](#action). A `path` of `/foo` will also match `/foo/bar`, `/foo/bar/baz`, etc. If the `site` is `~` it will be determined implicitly. A binding must be unique. +--- + ### `$action` ```hoon -+$ action - $% [%gen =generator] :: dispatch to a generator - [%app app=term] :: dispatch to an application - [%authentication ~] :: internal authentication page - [%logout ~] :: internal logout page - [%channel ~] :: gall channel system - [%scry ~] :: gall scry endpoint - [%four-oh-four ~] :: respond with the default file not found page - == + +$ action + $% [%gen =generator] + [%app app=term] + [%authentication ~] + [%logout ~] + [%channel ~] + [%scry ~] + [%name ~] + [%four-oh-four ~] + == ``` The action to take when a [$binding](#binding) matches an incoming HTTP request. +--- + ### `$generator` ```hoon @@ -153,6 +211,8 @@ The action to take when a [$binding](#binding) matches an incoming HTTP request. This refers to a generator on a local ship that can handle requests. Note that serving generators via Eyre is not fully implmented and should not be used. +--- + ### `$http-config` ```hoon @@ -166,6 +226,8 @@ This refers to a generator on a local ship that can handle requests. Note that s The configuration of the runtime HTTP server itself. The `secure` field contains the PEM-encoded RSA private key and certificate or certificate chain when using HTTPS, and otherwise is `~` when using plain HTTP. The `proxy` field is not currently used. The `log` field turns on HTTP(S) access logs but is not currently implemented. The `redirect` field turns on 301 redirects to upgrade HTTP to HTTPS if the `key` and `cert` are set in `secure`. +--- + ### `$http-rule` ```hoon @@ -177,6 +239,8 @@ The configuration of the runtime HTTP server itself. The `secure` field contains This is for updating the server configuration. In the case of `%cert`, a `cert` of `~` clears the HTTPS cert & key, otherwise `cert` contains the PEM-encoded RSA private key and certificate or certificate chain. In the case of `%turf`, a `%put` `action` sets a domain name and a `%del` `action` removes it. The [$turf](#turf) contains the domain name. +--- + ### `$address` ```hoon @@ -188,6 +252,8 @@ This is for updating the server configuration. In the case of `%cert`, a `cert` A client IP address. +--- + ### `$inbound-request` ```hoon @@ -201,6 +267,8 @@ A client IP address. An inbound HTTP request and metadata. The `authenticated` field says whether the request was made with a valid session cookie. The `secure` field says whether it was made with HTTPS. The [$address](#address) is the IP address from which the request originated, except if it came from localhost and included a `Forwarded` header, in which case it's the address specified in that header. The [$request:http](#requesthttp) contains the HTTP request itself. +--- + ## HTTP ### `$header-list:http` @@ -211,6 +279,8 @@ An inbound HTTP request and metadata. The `authenticated` field says whether the An ordered list of HTTP headers. The `key` is the header name e.g `'Content-Type'` and the `value` is the value e.g. `text/html`. +--- + ### `$method:http` ```hoon @@ -228,6 +298,8 @@ An ordered list of HTTP headers. The `key` is the header name e.g `'Content-Type An HTTP method. +--- + ### `$request:http` ```hoon @@ -241,6 +313,8 @@ An HTTP method. A single HTTP request. The [$method:http](#methodhttp) is the HTTP method, the `url` is the unescaped URL, the [$header-list:http](#header-listhttp) contains the HTTP headers of the request and the `body` is the actual data. An `octs` is just `[p=@ud q=@]` where `p` is the byte-length of `q`, the data. +--- + ### `$response-header:http` ```hoon @@ -252,6 +326,8 @@ A single HTTP request. The [$method:http](#methodhttp) is the HTTP method, the ` The status code and [$header-list:http](#header-listhttp) of an HTTP response. +--- + ### `$http-event:http` ```hoon @@ -275,6 +351,8 @@ Urbit treats Earth's HTTP servers as pipes, where Urbit sends or receives one or Calculation of control headers such as `'Content-Length'` or `'Transfer-Encoding'` should be performed at a higher level; this structure is merely for what gets sent to or received from Earth. +--- + ### `$simple-payload:http` ```hoon @@ -286,3 +364,5 @@ Calculation of control headers such as `'Content-Length'` or `'Transfer-Encoding ``` A simple, one-event response used for generators. The [$reponse-header:http](#response-headerhttp) contains the status code and HTTP headers. The `octs` in the `data` contains the body of the response and is a `[p=@ud q=@]` where `p` is the byte-length of `q`, the data. + +--- diff --git a/content/reference/arvo/eyre/scry.md b/content/reference/arvo/eyre/scry.md index 4580e2b..ffadf77 100644 --- a/content/reference/arvo/eyre/scry.md +++ b/content/reference/arvo/eyre/scry.md @@ -3,9 +3,12 @@ title = "Scry Reference" weight = 4 +++ -Here are all of Eyre's scry endpoints. There's not too many and they mostly deal with either CORS settings or aspects of the state of connections. +Here are all of Eyre's scry endpoints. There's not too many and they mostly +deal with either CORS settings or aspects of the state of connections. -The first few have a `care` of `x` and are a scry like `.^({TYPE} %ex /=//=/{SOME-PATH})` (note the empty `desk`). The rest have no `care` and the tag replaces the `desk` like `.^({TYPE} %e /={SOMETHING}=)`. +The first few have a `care` of `x` and are a scry like `.^([type] %ex +/=//=/[some-path])` (note the empty `desk`). The rest have no `care` and the +tag replaces the `desk` like `.^([type] %e /=[something]=)`. All examples are run from the dojo. @@ -13,7 +16,7 @@ All examples are run from the dojo. An `x` scry with a `path` of `/cors` will return Eyre's CORS origin registry. The type returned is a [cors-registry](/reference/arvo/eyre/data-types#cors-registry) which contains the `set`s of approved, rejected and requested origins. -### Example {% #example-1 %} +#### Example {% #example-1 %} ``` > .^(cors-registry:eyre %ex /=//=/cors) @@ -23,35 +26,48 @@ An `x` scry with a `path` of `/cors` will return Eyre's CORS origin registry. Th ] ``` +--- + ## `/cors/requests` -An `x` scry with a `path` of `/cors/requests` will return the `set` of pending origin requests. These are origins that were in an `Origin: ...` HTTP header but weren't in the existing approved or rejected sets. The type returned is a `(set origin:eyre)`. +An `x` scry with a `path` of `/cors/requests` will return the `set` of pending +origin requests. These are origins that were in an `Origin: ...` HTTP header +but weren't in the existing approved or rejected sets. The type returned is a +`(set origin:eyre)`. -### Example {% #example-2 %} +#### Example {% #example-2 %} ``` > .^(requests=(set origin:eyre) %ex /=//=/cors/requests) requests={~~http~3a.~2f.~2f.baz~.example} ``` +--- + ## `/cors/approved` An `x` scry with a `path` of `/cors/approved` will return the `set` of approved CORS origins. The type returned is a `(set origin:eyre)`. -### Example {% #example-3 %} +#### Example {% #example-3 %} ``` > .^(approved=(set origin:eyre) %ex /=//=/cors/approved) approved={~~http~3a.~2f.~2f.foo~.example} ``` -## `/cors/approved/{ORIGIN}` +--- -An `x` scry whose `path` is `/cors/approved/{ORIGIN}` tests whether the given origin URL is in the `approved` set of the CORS registry. The type returned is a simple `?`. +## `/cors/approved/[origin]` -The origin URL is a `@t`, but since `@t` may not be valid in a path, it must be encoded in a `@ta` using `+scot` like `(scot %t 'foo')` rather than just `'foo'`. +An `x` scry whose `path` is `/cors/approved/[origin]` tests whether the given +origin URL is in the `approved` set of the CORS registry. The type returned is +a simple `?`. -### Examples {% #examples-1 %} +The origin URL is a `@t`, but since `@t` may not be valid in a path, it must be +encoded in a `@ta` using `+scot` like `(scot %t 'foo')` rather than just +`'foo'`. + +#### Examples {% #examples-1 %} ``` > .^(? %ex /=//=/cors/approved/(scot %t 'http://foo.example')) @@ -63,24 +79,32 @@ The origin URL is a `@t`, but since `@t` may not be valid in a path, it must be %.n ``` +--- + ## `/cors/rejected` -An `x` scry with a `path` of `/cors/rejected` will return the `set` of rejected CORS origins. The type returned is a `(set origin:eyre)`. +An `x` scry with a `path` of `/cors/rejected` will return the `set` of rejected +CORS origins. The type returned is a `(set origin:eyre)`. -### Example {% #example-4 %} +#### Example {% #example-4 %} ``` > .^(rejected=(set origin:eyre) %ex /=//=/cors/rejected) rejected={~~http~3a.~2f.~2f.bar~.example} ``` -## `/cors/rejected/{ORIGIN}` +--- -An `x` scry whose `path` is `/cors/rejected/{ORIGIN}` tests whether the given origin URL is in the `rejected` set of the CORS registry. The type returned is a simple `?`. +## `/cors/rejected/[origin]` -The origin URL must be a cord-encoded `@t` rather than just the plain `@t`, so you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`. +An `x` scry whose `path` is `/cors/rejected/[origin]` tests whether the given +origin URL is in the `rejected` set of the CORS registry. The type returned is +a simple `?`. -### Examples {% #examples-2 %} +The origin URL must be a cord-encoded `@t` rather than just the plain `@t`, so +you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`. + +#### Examples {% #examples-2 %} ``` > .^(? %ex /=//=/cors/rejected/(scot %t 'http://bar.example')) @@ -92,13 +116,18 @@ The origin URL must be a cord-encoded `@t` rather than just the plain `@t`, so y %.n ``` +--- + ## `/authenticated/cookie` -An `x` scry whose `path` is `/authenticated/cookie/{COOKIE}` tests whether the given cookie is currently valid. The type returned is a `?`. +An `x` scry whose `path` is `/authenticated/cookie/[cookie]` tests whether the +given cookie is currently valid. The type returned is a `?`. -The cookie must be the full cookie including the `urbauth-{SHIP}=` part. The cookie must be a cord-encoded `@t` rather than just a plain `@t`, so you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`. +The cookie must be the full cookie including the `urbauth-{SHIP}=` part. The +cookie must be a knot-encoded `@t` rather than just a plain `@t`, so you'll +have to do something like `(scot %t 'foo')` rather than just `'foo'`. -### Examples {% #examples-3 %} +#### Examples {% #examples-3 %} ``` > .^(? %ex /=//=/authenticated/cookie/(scot %t 'urbauth-~zod=0vvndn8.bfsjj.j3614.k40ha.8fomi')) @@ -110,11 +139,27 @@ The cookie must be the full cookie including the `urbauth-{SHIP}=` part. The coo %.n ``` +--- + +## `/cache/[aeon]/[url]` + +An `%x` `/cache` scry will return the cached value for the given `[url]` at the +given `[aeon]` if it exists. + +The `[url]` must be a knot-encoded `@t` rather than just a plain `@t`, so +you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`. + +--- + ## `%bindings` -A scry with `bindings` in place of the `desk` in the `beak` will return Eyre's URL path bindings. The type returned is a `(list [binding:eyre duct action:eyre])` (see the [$binding](/reference/arvo/eyre/data-types#binding) & [$action](/reference/arvo/eyre/data-types#action) sections of the Data Types document for details). +A scry with `bindings` in place of the `desk` in the `beak` will return Eyre's +URL path bindings. The type returned is a `(list [binding:eyre duct +action:eyre])` (see the [$binding](/reference/arvo/eyre/data-types#binding) & +[$action](/reference/arvo/eyre/data-types#action) sections of the Data Types +document for details). -### Example {% #example-5 %} +#### Example {% #example-5 %} ``` > .^((list [binding:eyre duct action:eyre]) %e /=bindings=) @@ -133,20 +178,29 @@ A scry with `bindings` in place of the `desk` in the `beak` will return Eyre's U ## `%connections` -A scry with `bindings` in place of the `desk` in the `beak` will return all open HTTP connections that aren't fully complete. The type returned is a `(map duct outstanding-connection:eyre)` (see the [$outstanding-connection](/reference/arvo/eyre/data-types#outstanding-connection) section of the Data Types document for details). +A scry with `connections` in place of the `desk` in the `beak` will return all +open HTTP connections that aren't fully complete. The type returned is a `(map +duct outstanding-connection:eyre)` (see the +[$outstanding-connection](/reference/arvo/eyre/data-types#outstanding-connection) +section of the Data Types document for details). -### Example {% #example-6 %} +#### Example {% #example-6 %} ``` > .^((map duct outstanding-connection:eyre) %e /=connections=) {} ``` +--- + ## `%authentication-state` -A scry with `authentication-state` in place of the `desk` in the `beak` will return authentication details of all current sessions. The type returned is a [$authentication-state](/reference/arvo/eyre/data-types#authentication-state). The `p` field is the cookie sans the `urbauth-{SHIP}=` part. +A scry with `authentication-state` in place of the `desk` in the `beak` will +return authentication details of all current sessions. The type returned is a +[$authentication-state](/reference/arvo/eyre/data-types#authentication-state). +The `p` field is the cookie sans the `urbauth-[ship]=` part. -### Example {% #example-7 %} +#### Example {% #example-7 %} ``` > .^(authentication-state:eyre %e /=authentication-state=) @@ -157,11 +211,15 @@ A scry with `authentication-state` in place of the `desk` in the `beak` will ret } ``` +--- + ## `%channel-state` -A scry with `channel-state` in place of the `desk` in the `beak` will return details of the state of each channel. The type returned is a [channel-state](/reference/arvo/eyre/data-types#channel-state). +A scry with `channel-state` in place of the `desk` in the `beak` will return +details of the state of each channel. The type returned is a +[channel-state](/reference/arvo/eyre/data-types#channel-state). -### Example {% #example-8 %} +#### Example {% #example-8 %} ``` > .^(channel-state:eyre %e /=channel-state=) @@ -182,13 +240,18 @@ A scry with `channel-state` in place of the `desk` in the `beak` will return det ] ``` +--- + ## `%host` -A scry with `host` in place of the `desk` in the `beak` will return host details of the ship. The type returned is a `hart:eyre`. +A scry with `host` in place of the `desk` in the `beak` will return host +details of the ship. The type returned is a `hart:eyre`. -### Example {% #example-9 %} +#### Example {% #example-9 %} ``` > .^(hart:eyre %e /=host=) [p=%.n q=[~ 8.080] r=[%.y p=<|localhost|>]] ``` + +--- diff --git a/content/reference/arvo/eyre/tasks.md b/content/reference/arvo/eyre/tasks.md index eb82071..6215ea2 100644 --- a/content/reference/arvo/eyre/tasks.md +++ b/content/reference/arvo/eyre/tasks.md @@ -23,6 +23,8 @@ The `insecure` field is the HTTP port and `secure` is the optional HTTPS port. Eyre returns no `gift` in response to a `%live` `task`. +--- + ## `%rule` ```hoon @@ -37,6 +39,8 @@ The [$http-rule](/reference/arvo/eyre/data-types#http-rule) is either tagged wit Eyre returns no `gift` in response to a `%rule` `task`. +--- + ## `%request` ```hoon @@ -51,6 +55,8 @@ The `secure` field says whether it's over HTTPS. The `address` is the IP address Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the contents of the `%request`, state of the connection, and other factors. +--- + ## `%request-local` ```hoon @@ -63,6 +69,8 @@ This `task` is how Eyre receives an inbound HTTP request over the local loopback Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the contents of the `%request`, state of the connection, and other factors. +--- + ## `%cancel-request` ```hoon @@ -77,6 +85,8 @@ This `task` takes no arguments. Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the state of the connection and other factors. +--- + ## `%connect` ```hoon @@ -111,6 +121,8 @@ The `accepted` field says whether the binding succeeded and the `binding` is the See the [Agents: Direct HTTP](/reference/arvo/eyre/guide#agents-direct-http) section of the [Guide](/reference/arvo/eyre/guide) document for an example. +--- + ## `%serve` ```hoon @@ -147,6 +159,8 @@ Eyre will return a `%bound` `gift` as described at the end of the [%connect](#co See the [Generators](/reference/arvo/eyre/guide#generators) section of the [Guide](/reference/arvo/eyre/guide) document for an example. +--- + ## `%disconnect` ```hoon @@ -161,6 +175,8 @@ The [$binding](/reference/arvo/eyre/data-types#binding) is the URL path and doma Eyre returns no `gift` in response to a `%disconnect` `task`. +--- + ## `%code-changed` ```hoon @@ -210,3 +226,47 @@ Eyre returns no `gift` in response to a `%reject-origin` `task`. #### Example See the [Managing CORS Origins](/reference/arvo/eyre/guide#managing-cors-origins) section of the [Guide](/reference/arvo/eyre/guide) document for an example. + +--- +## `%set-response` + +```hoon +[%set-response url=@t entry=(unit cache-entry)] +``` + +This `task` tells Eyre to set a cache entry for a URL path. Adding entries to +Eyre's cache will make them much faster to load, and more capable of handling +many connections. + +The `url` field is the URL path you want to bind with the cache entry. Note this +will just be the URL path as a cord like `'/foo/bar/baz'`, it does not include +the host, etc. + +The `entry` field is a +[`$cache-entry`](/reference/arvo/eyre/data-types#cache-entry) in a `unit`. If +the unit is null, the specified `url` will be unbound and the cache entry +removed. If non-null, the given `entry` will be added to the cache (or updated +if the binding already exists). + +Each time the entry for a URL path is changed, its revision number will be +incremented. + +See the [`$cache-entry`](/reference/arvo/eyre/data-types#cache-entry) entry in +Eyre's type reference for more details of the entry itself. + +#### Returns + +Eyre gives a `%grow` `gift` in response to a `%set-response` `task`. A `%grow` +`gift` looks like: + +```hoon +[%grow =path] +``` + +The `path` will be of the format `/cache/[revision]/[url]`, for example +`/cache/12/~~~2f.foo~2f.bar`. The revision number is incremented each time the +entry is updated, including if it's removed, and is in `@ud` format. The url +element uses `%t` [`++scot`](/reference/hoon/stdlib/4m#scot) encoding, so will +need to be decoded with `%t` [`++slav`](/reference/hoon/stdlib/4m#slav). + +--- diff --git a/content/reference/arvo/gall/_index.md b/content/reference/arvo/gall/_index.md index 9177d01..d3152c9 100644 --- a/content/reference/arvo/gall/_index.md +++ b/content/reference/arvo/gall/_index.md @@ -4,3 +4,20 @@ weight = 70 sort_by = "weight" insert_anchor_links = "right" +++ + +## [Overview](/reference/arvo/gall/gall) + +An overview of Eyre and its capabilities. + +## [API Reference](/reference/arvo/gall/gall-api) + +Reference documentation of both Gall's vane API and the API available to +agents. + +## [Scry Reference](/reference/arvo/gall/scry) + +Reference documentation of Gall scry endpoints. + +## [Data Types](/reference/arvo/gall/data-types) + +Reference documentation of Gall's types as defined in `lull.hoon`. diff --git a/content/reference/arvo/gall/data-types.md b/content/reference/arvo/gall/data-types.md index b85020f..8f0a5fe 100644 --- a/content/reference/arvo/gall/data-types.md +++ b/content/reference/arvo/gall/data-types.md @@ -5,59 +5,79 @@ weight = 5 This document describes the data types for Gall defined in `lull.hoon`. + ## `bitt` +Incoming subscriptions. + ```hoon +$ bitt (map duct (pair ship path)) ``` -Incoming subscriptions. +This is the structure Gall uses to keep track of incoming subscriptions +for a Gall agent. The `sup` field of a [`bowl`](#bowl) contains the +`bitt` for our agent. -This is the structure Gall uses to keep track of subscribers to our Gall agent. -The `sup` field of a [`bowl`](#bowl) contains the `bitt` for our agent. +--- ## `boat` -```hoon -+$ boat - %+ map [=wire =ship =term] - [acked=? =path] -``` - Outgoing subscriptions. -This is the structure Gall uses to keep track of subscriptions our agent has -initiated. The `wex` field of a [`bowl`](#bowl) contails the `boat` for that -agent. +```hoon ++$ boat (map [=wire =ship =term] [acked=? =path]) +``` -The `wire` field is the `wire` which [`sign:agent`](#signagent)s will come in on. -The `ship` and `term` fields are the ship and the name of the agent to which our -agent has subscribed. +This is the structure Gall uses to keep track of subscriptions our agent +has initiated. The `wex` field of a [`bowl`](#bowl) contails the `boat` +for that agent. -The `acked` field is `%.y` if they have acknowledged our subscription request, -and `%.n` if they have not. The `path` field is the `path` on the other agent to -which our agent has subscribed. +The `wire` field is the `wire` which [`sign:agent`](#signagent)s will +come in on. The `ship` and `term` fields are the ship and the name of +the agent to which our agent has subscribed. + +The `acked` field is `%.y` if they have acknowledged our subscription +request with a `%watch-ack`, and `%.n` if they have not. The `path` +field is the `path` on the other agent to which our agent has +subscribed. + +--- + +## `boar` + +Subscription nonces. + +```hoon ++$ boar (map [=wire =ship =term] nonce=@) +``` + +Gall uses this to keep track of nonces for subscriptions. + +--- ## `bowl` +Additional agent state. + ```hoon +$ bowl :: standard app state - $: $: our=ship :: host - src=ship :: guest - dap=term :: agent - == :: - $: wex=boat :: outgoing subs - sup=bitt :: incoming subs - == :: - $: act=@ud :: change number - eny=@uvJ :: entropy - now=@da :: current time - byk=beak :: load source - == == :: + $: $: our=ship :: host + src=ship :: guest + dap=term :: agent + == :: + $: wex=boat :: outgoing subs + sup=bitt :: incoming subs + $= sky :: scry bindings + %+ map path :: + ((mop @ud (pair @da (each page @uvI))) lte) :: + == :: + $: act=@ud :: change number + eny=@uvJ :: entropy + now=@da :: current time + byk=beak :: load source + == == :: ``` -Additional agent state. - A `bowl` is given to the agent core each time an event comes in. The fields are as follows: @@ -68,6 +88,9 @@ as follows: See the [`boat`](#boat) section for details of the type. - `sup`: Incoming subscriptions. That is, subscriptions others have made to our agent. See the [`bitt`](#bitt) section for details of the type. +- `sky`: Remote scry bindings. A map from binding paths to a + [`mop`](/reference/hoon/zuse/2m#mop) (ordered map) of files by revision + number. Tombstoned files have an `@uvI` hash rather than `page`. - `act`: The total number of [`move`](/reference/arvo/overview#move)s our agent has processed so far. - `eny`: 512 bits of entropy. @@ -76,24 +99,49 @@ as follows: `case` will be `[%da @da]` where the `@da` is the when the agent was loaded. A `beak` is a triple of `[ship desk case]`. +--- + ## `dude` +Agent name. + ```hoon +$ dude term ``` -Agent name. +--- ## `gill` +A general contact. + ```hoon +$ gill (pair ship term) ``` -A general contact: A pair of the ship and agent name. +A pair of the ship and agent name. + +--- + +## `load` + +Loadout. + +```hoon ++$ load (list [=dude =beak =agent]) +``` + +The [`dude`](#dude) is the agent name, the `beak` is the ship/desk/case +in which it resides, and the [`agent`](#agent) is the built agent +itself. Clay passes this to Gall when it builds or modifies the state of +running agents. + +--- ## `scar` +Opaque duct - used internally. + ```hoon +$ scar $: p=@ud @@ -102,40 +150,30 @@ A general contact: A pair of the ship and agent name. == ``` -Opaque duct - used internally. +--- ## `suss` +Configuration report. + ```hoon +$ suss (trel dude @tas @da) ``` -Configuration report. +--- ## `well` +Desk and agent. + ```hoon +$ well (pair desk term) ``` -Desk and agent. - -## `neat` - -```hoon -+$ neat - $% [%arvo =note-arvo] - [%agent [=ship name=term] =deal] - [%pyre =tang] - == -``` - -Like a [`note:agent`](#noteagent), except the `%agent` case has a [`deal`](#deal) instead -of just a [`task:agent`](#taskagent). This is used for messages that come in over the -network and would not be used manually. - ## `deal` +An agent task or raw poke. + ```hoon +$ deal $% [%raw-poke =mark =noun] @@ -143,12 +181,17 @@ network and would not be used manually. == ``` -Like a [`task:agent`](#taskagent) but with the additional case of a raw poke. -This is used for messages that come in over the network and would not be used -manually. +The additional `%raw-poke` is for pokes which haven't yet been converted +to an ordinary `%poke` by molding the `noun` with the specified `mark` +core. This structure is passed around on the kernel level, it would not +be used in userspace. + +--- ## `unto` +An agent gift or a raw fact. + ```hoon +$ unto $% [%raw-fact =mark =noun] @@ -156,9 +199,24 @@ manually. == ``` -Like a [`sign:agent`](#signagent) but with the additional case of a raw fact. -This is used for messages that come in over the network and would not be used -manually. +The additional `%raw-fact` is for facts which haven't yet been converted +to an ordinary `%fact` by molding the `noun` it with the specified +`mark` core. This structure is passed around on the kernel level, it +would not be used in userspace. + +--- + +## `verb` + +Verbosity flags. + +```hoon ++$ verb ?(%odd) +``` + +Flags to set Gall verbosity. Currently only `%odd` for unusual errors. + +--- ## `agent` @@ -169,10 +227,10 @@ manually. ``` Container for Gall agent types. The most significant arm is -[`form:agent`](#formagent), which specifies the structure of the agent itself. -There are also some additional structures defined here, mostly defining the -kinds of messages agents can send. The different arms of the core in `agent` -are considered separately below. +[`form:agent`](#formagent), which specifies the structure of the agent +itself. There are also some additional structures defined here, mostly +defining the kinds of messages agents can send. The different arms of +the core in `agent` are considered separately below. ### `step:agent` @@ -180,8 +238,12 @@ are considered separately below. +$ step (quip card form) ``` -A cell of [`card:agent`](#cardagent)s to be sent and a new agent state. This is the -type returned by most arms of an agent. A `(quip a b)` is the same as `[(list a) b]`, it's just a more convenient way to specify it. +A cell of [`card:agent`](#cardagent)s to be sent and a new agent state. +This is the type returned by most arms of an agent. A `(quip a b)` is +the same as `[(list a) b]`, it's just a more convenient way to specify +it. + +--- ### `card:agent` @@ -189,9 +251,9 @@ type returned by most arms of an agent. A `(quip a b)` is the same as `[(list a) +$ card (wind note gift) ``` -An effect - typically a message to be sent to another agent or vane. A list of -these are returned by most agent arms along with a new state in a -[`step:agent`](#stepagent). A `wind` is the following: +An effect - typically a message to be sent to another agent or vane. A +list of these are returned by most agent arms along with a new state in +a [`step:agent`](#stepagent). A `wind` is the following: ```hoon ++ wind @@ -207,9 +269,11 @@ Gall will not allow a `%slip`, so in practice a `card` will be one of: - `[%pass path note]` - `[%give gift]` -For `%pass`, `p` specifies the `wire` on which a response should be returned. -See [`note:agent`](#noteagent) and [`gift:agent`](#giftagent) below for details of their -types. +For `%pass`, `p` specifies the `wire` on which a response should be +returned. See [`note:agent`](#noteagent) and [`gift:agent`](#giftagent) +below for details of their types. + +--- ### `note:agent` @@ -218,17 +282,23 @@ types. $% [%agent [=ship name=term] =task] [%arvo note-arvo] [%pyre =tang] + :: + [%grow =spur =page] + [%tomb =case =spur] + [%cull =case =spur] == ``` The type for messages initiated by our agent. This is opposed to -[`gift:agent`](#giftagent), which is the type for responding to other agents or -vanes, or for sending out updates to subscribers. The three cases are: +[`gift:agent`](#giftagent), which is the type for responding to other +agents or vanes, or for sending out updates to subscribers. The three +cases are: - `%agent`: Poke another agent, subscribe to another agent, or cancel a - subscription to another agent. The `ship` and `name` fields are the ship and - agent to which the `task` should be sent. The `task` is the request itself, - see [`task:agent`](#taskagent) below for its possible types. + subscription to another agent. The `ship` and `name` fields are the + ship and agent to which the `task` should be sent. The `task` is the + request itself, see [`task:agent`](#taskagent) below for its possible + types. - `%arvo`: Pass a `task` to a vane. The type of a `note-arvo` is: ```hoon +$ note-arvo @@ -246,16 +316,23 @@ vanes, or for sending out updates to subscribers. The three cases are: [@tas %meta vase] == ``` - You can refer to the `/sys/lull.hoon` source code for all the possible vane - tasks, or see each vane's API Reference section in the [Arvo + You can refer to the `/sys/lull.hoon` source code for all the possible + vane tasks, or see each vane's API Reference section in the [Arvo documentation](/reference/arvo/overview) - `%pyre`: This is for aborting side-effects initiated during agent installation. The `tang` is an error message. +- `%grow`/`%tomb`/`%cull`: These are used for publishing and managing + data available for remote scries. For more information, see the + [remote scries guide](/guides/additional/remote-scry). A `note:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent). +--- + ### `task:agent` +The types of messages initiated by our agent and sent to another agent. + ```hoon +$ task $% [%watch =path] @@ -266,10 +343,9 @@ A `note:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent). == ``` -The types of messages initiated by our agent and sent to another agent. This is -in contrast to [`gift:agent`](#giftagent)s, which are responses to incoming -messages from agents or updates to agents already subscribed. The five kinds -are: +This is in contrast to [`gift:agent`](#giftagent)s, which are responses +to incoming messages from agents or updates to agents already +subscribed. The five kinds of `task:agent` are: - `%watch`: Subscribe to `path` on the target ship and agent. - `%watch-as`: Same as `%watch`, except you ask the target's Gall to convert @@ -285,8 +361,13 @@ are: A `task:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent). +--- + ### `gift:agent` +The types of messages our agent can either send in response to messages +from other agents, or send to subscribed agents. + ```hoon +$ gift $% [%fact paths=(list path) =cage] @@ -296,37 +377,46 @@ A `task:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent). == ``` -The types of messages our agent can either send in response to messages from -other agents, or send to subscribed agents. This is in contrast to -[`task:agent`](#taskagent)s, which are messages to other agents our agent -initiates rather than sends in response. The four kinds are: +This is in contrast to [`task:agent`](#taskagent)s, which are messages +to other agents our agent initiates rather than sends in response. The +four kinds of `gift:agent` are: -- `%fact`: An update to existing subscribers. The `paths` field specifies which - subscription paths the update should go out to. The `cage` is the data, and is - a `[mark vase]`. +- `%fact`: An update to existing subscribers. The `paths` field + specifies which subscription paths the update should go out to. The + `cage` is the data, and is a `[mark vase]`. - `%kick`: Kick subscriber, ending their subscription. The `paths` field - specifies which paths the subscriber should be kicked from, and the `ship` - field specifies the ship to kick. If the `ship` field is null, all subscribers - on the specified paths are kicked. Gall will automatically remove the - subscription from our agent's [`bitt`](#bitt) (inbound subscription `map`), - and subscriber will no longer receive updates on the `path`s in question. -- `%watch-ack`: Acknowledge a subscription request. If `p` is null, it's an ack - (positive acknowledgement), and if `p` is non-null, it's a nack (negative - acknowledgement). Simply crashing will caused Gall to nack a subscription - request, and not crashing but not explicitly producing a `%watch-ack` `gift` - will cause Gall to ack a subscription request. Therefore, you'd typically only - explicitly produce a `%watch-ack` `gift` if you wanted to nack a subscription - request with a custom error in the `tang`. -- `%poke-ack`: Acknowledge a poke. If `p` is null, it's an ack, and if `p` is - non-null, it's a nack. Simply crashing will cause Gall to nack a poke, and not - crashing but not explicitly producing a `%poke-ack` `gift` will cause Gall to - ack a poke. Therefore, you'd typically only explicitly produce a `%poke-ack` - `gift` if you wanted to nack a poke with a custom error in the `tang`. + specifies which paths the subscriber should be kicked from, and the + `ship` field specifies the ship to kick. If the `ship` field is null, + all subscribers on the specified paths are kicked. Gall will + automatically remove the subscription from our agent's [`bitt`](#bitt) + (inbound subscription `map`), and subscriber will no longer receive + updates on the `path`s in question. +- `%watch-ack`: Acknowledge a subscription request. If `p` is null, it's + an ack (positive acknowledgement), and if `p` is non-null, it's a nack + (negative acknowledgement). Simply crashing will caused Gall to nack a + subscription request, and not crashing but not explicitly producing a + `%watch-ack` `gift` will cause Gall to ack a subscription request. + Therefore, you'd typically only explicitly produce a `%watch-ack` + `gift` if you wanted to nack a subscription request with a custom + error in the `tang`. +- `%poke-ack`: Acknowledge a poke. If `p` is null, it's an ack, and if + `p` is non-null, it's a nack. Simply crashing will cause Gall to nack + a poke, and not crashing but not explicitly producing a `%poke-ack` + `gift` will cause Gall to ack a poke. Therefore, you'd typically only + explicitly produce a `%poke-ack` `gift` if you wanted to nack a poke + with a custom error in the `tang`. -A `gift:agent` is always wrapped in a `%give` [`card:agent`](#cardagent). +A `gift:agent` is always wrapped in a `%give` +[`card:agent`](#cardagent). + +--- ### `sign:agent` +A `sign` is like a [`gift:agent`](#giftagent) but it's something that +comes _in_ to our agent from another agent rather than something we send +out. + ```hoon +$ sign $% [%poke-ack p=(unit tang)] @@ -336,31 +426,34 @@ A `gift:agent` is always wrapped in a `%give` [`card:agent`](#cardagent). == ``` -A `sign` is like a [`gift:agent`](#giftagent) but it's something that comes -_in_ to our agent from another agent rather than something we send out. The -possible types are: +The possible types are: -- `%poke-ack`: Another agent has acked (positively acknowledged) or nacked - (negatively acknowledged) a `%poke` [`task:agent`](#taskagent) we previously - sent. It's an ack if `p` is null and a nack if `p` is non-null. The `tang` - contains an error or traceback if it's a nack. +- `%poke-ack`: Another agent has acked (positively acknowledged) or + nacked (negatively acknowledged) a `%poke` [`task:agent`](#taskagent) + we previously sent. It's an ack if `p` is null and a nack if `p` is + non-null. The `tang` contains an error or traceback if it's a nack. - `%watch-ack`: Another agent has acked or nacked a `%watch` - [`task:agent`](#taskagent) (subscription request) we previously sent. It's an - ack if `p` is null and a nack if `p` is non-null. The `tang` contains an error - or traceback if it's a nack. If it's a nack, Gall will automatically remove - the subscription from our agent's [`boat`](#boat) (outbound subscription - map). -- `%fact`: An update from another agent to which we've previously subscribed - with a `%watch` [`task:agent`](#taskagent) (subscription request). The `cage` - contains the data, and is a `[mark vase]`. -- `%kick`: Our subscription to another agent has been ended, and we'll no longer - receive updates. A `%kick` may be intentional, but it may also happen due to - certain network conditions or other factors. As a result, it's best to try and - resubscribe with another `%watch` [`task:agent`](#taskagent), and if they - nack the `%watch`, we can conclude it was intentional and give up. + [`task:agent`](#taskagent) (subscription request) we previously sent. + It's an ack if `p` is null and a nack if `p` is non-null. The `tang` + contains an error or traceback if it's a nack. If it's a nack, Gall + will automatically remove the subscription from our agent's + [`boat`](#boat) (outbound subscription map). +- `%fact`: An update from another agent to which we've previously + subscribed with a `%watch` [`task:agent`](#taskagent) (subscription + request). The `cage` contains the data, and is a `[mark vase]`. +- `%kick`: Our subscription to another agent has been ended, and we'll + no longer receive updates. A `%kick` may be intentional, but it may + also happen due to certain network conditions or other factors. As a + result, it's best to try and resubscribe with another `%watch` + [`task:agent`](#taskagent), and if they nack the `%watch`, we can + conclude it was intentional and give up. + +--- ### `form:agent` +This defines the structure of the agent itself. + ```hoon ++ form $_ ^| @@ -406,7 +499,8 @@ possible types are: -- ``` -This defines the structure of the agent itself. The agent is a door with a [`bowl`](#bowl) as its sample and exactly ten arms. Below we'll describe each arm briefly. +The agent is a door with a [`bowl`](#bowl) as its sample and exactly ten +arms. Below we'll describe each arm briefly. #### `on-init` @@ -434,16 +528,16 @@ This arm is called when another agent subscribes to our agent. - Accepts: `path` - Produces: [`step:agent`](#stepagent) -This arm is called when another agent unsubscribes from a subscription path on -our agent. +This arm is called when another agent unsubscribes from a +subscription path on our agent. #### `on-peek` - Accepts: `path` - Produces: `(unit (unit cage))` -This arm is called when a [scry](/reference/arvo/concepts/scry) is performed on our -agent. +This arm is called when a [scry](/reference/arvo/concepts/scry) is +performed on our agent. #### `on-agent` @@ -458,7 +552,8 @@ This arm is called when another agent give our agent a - Accepts: `[wire sign-arvo]` - Produces: [`step:agent`](#stepagent) -This arm is called when a vane gives our agent a `gift`. A `sign-arvo` is: +This arm is called when a vane gives our agent a `gift`. A `sign-arvo` +is: ```hoon +$ sign-arvo @@ -481,13 +576,15 @@ This arm is called when a vane gives our agent a `gift`. A `sign-arvo` is: == ``` -You can refer to the `/sys/lull.hoon` source code, or the API Reference of each -vane in the [Arvo documentation](/reference/arvo/overview). +You can refer to the `/sys/lull.hoon` source code, or the API Reference +of each vane in the [Arvo documentation](/reference/arvo/overview). #### `on-fail` - Accepts: `[term tang]` - Produces: [`step:agent`](#stepagent) -This arm is called if certain errors occur in Gall, such as if our agent tries -to create a duplicate subscription. +This arm is called if certain errors occur in Gall, such as if our agent +tries to create a duplicate subscription. + +--- diff --git a/content/reference/arvo/gall/gall-api.md b/content/reference/arvo/gall/gall-api.md index ff6fbc6..44e95e1 100644 --- a/content/reference/arvo/gall/gall-api.md +++ b/content/reference/arvo/gall/gall-api.md @@ -3,955 +3,579 @@ title = "API Reference" weight = 3 +++ -## Reference Agent - -This app stores a number for each user. You can increment your number, increment -someone else's number, start tracking someone else's number by poking. It -exposes a scry namespace to inspect everyone's number. - -### `app/example-gall.hoon` - -```hoon -:: Import sur/example-gall -/- *example-gall -:: Import lib/default-agent -/+ default-agent -|% -+$ card card:agent:gall -+$ note - $% - [%arvo =note-arvo] - [%agent [=ship name=term] =task:agent:gall] - == -+$ state-zero [%0 local=@ud] -+$ state-one [%1 local=@ud remote=(map @p @ud)] -+$ versioned-state - $% - state-zero - state-one - == --- -=| state-one -=* state - -^- agent:gall -|_ =bowl:gall -+* this . - def ~(. (default-agent this %|) bowl) -:: -:: Set local counter to 1 by default -++ on-init - ^- (quip card _this) - `this(local 1) -:: Expose state for saving -++ on-save - !>(state) -:: -:: Load old state and upgrade if neccessary -++ on-load - |= old=vase - ^- (quip card _this) - =/ loaded=versioned-state - !<(versioned-state old) - ?- -.loaded - %0 - `this(local local.loaded) :: Upgrade old state - %1 - `this(state loaded) - == -:: -:: Respond to poke -:: App can be poked in the dojo by running the following commands -:: Increment local counter -:: :example-gall &example-gall-action [%increment ~] -:: Increment ~zod's counter -:: :example-gall &example-gall-action [%increment-remote ~zod] -:: Subscribe to ~zod's counter -:: :example-gall &example-gall-action [%view ~zod] -:: Unsubscribe from ~zod's counter -:: :example-gall &example-gall-action [%stop-view ~zod] -:: -++ on-poke - |= [=mark =vase] - ^- (quip card _this) - :: Ensure poke is of mark %example-gall-action - ?> =(mark %example-gall-action) - =/ act=example-gall-action !<(example-gall-action vase) - ?- act - :: - :: Increment local counter and send new counter to subscribers - :: - [%increment ~] - :- [%give %fact ~[/local] %atom !>(+(local))]~ - this(local +(local)) - :: - :: Send remote %increment poke - :: - [%increment-remote who=@p] - :_ this - :~ :* - %pass - /inc/(scot %p who.act) - %agent - [who.act %example-gall] - %poke %example-gall-action !>([%increment ~]) - == == - :: - :: Subscribe to a remote counter - :: - [%view who=@p] - :_ this - [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %watch /local]~ - :: - :: Unsubscribe from remote counter and remove from state - :: - [%stop-view who=@p] - :_ this(remote (~(del by remote) who.act)) - [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %leave ~]~ - :: - == -:: -:: Print on unsubscribe -:: -++ on-leave - |= =path - ^- (quip card _this) - ~& "Unsubscribed by: {} on: {}" - `this -:: -:: Handle new subscription -:: -:: When another ship subscribes to our counter, give them the current state of -:: the counter immediately -:: -++ on-watch - |= =path - ^- (quip card _this) - :_ this - :: Crash if we see a subscription we don't recognise - ?+ path ~|("unexpected subscription" !!) - :: - [%local ~] - [%give %fact ~ %atom !>(local)]~ - == -:: -:: Expose scry namespace -:: -:: .^(@ %gx /=example-gall=/local/atom) will produce the current local counter -:: .^(@ %gx /=example-gall=/remote/~zod/atom) will produce the counter for ~zod -:: .^(arch %gy /=example-gall=/remote) will produce a listing of the current -:: remote counters -++ on-peek - |= =path - ^- (unit (unit cage)) - ?+ path [~ ~] - :: - :: Produce local counter - :: - [%x %local ~] - ``[%atom !>(local)] - :: - :: Produce remote counter - :: - [%x %remote who=@ta ~] - =* location i.t.t.path :: Ship name is third in the list - =/ res - (~(got by remote) (slav %p location)) - ``[%atom !>(res)] - :: - :: Produce listing of remote counters - :: - [%y %remote ~] - =/ dir=(map @ta ~) - %- molt :: Map from list of k-v pairs - %+ turn :: iterate over list of k-v pairs - ~(tap by remote) :: list of k-v pairs from map - |= [who=@p *] - [(scot %p who) ~] - ``[%arch !>(`arch`[~ dir])] - == -:: -:: Handle sign from agent -:: -++ on-agent - |= [=wire =sign:agent:gall] - ^- (quip card _this) - ?- -.sign - :: - :: Print error if poke failed - :: - %poke-ack - ?~ p.sign - `this - %- (slog u.p.sign) - `this - :: - :: Print error if subscription failed - :: - %watch-ack - ?~ p.sign - `this - =/ =tank leaf+"subscribe failed from {} on wire {}" - %- (slog tank u.p.sign) - `this - :: - :: Do nothing if unsubscribed - :: - %kick `this - :: - :: Update remote counter when we get a subscription update - :: - %fact - :- ~ - ?. ?=(%atom p.cage.sign) - this - this(remote (~(put by remote) src.bowl !<(@ q.cage.sign))) - == -:: -:: Handle arvo signs -:: -:: We never give any cards to arvo. Therefore we never need to handle any signs -:: from arvo. We use the default-agent library to avoid implementing this arm, -:: as gall apps must have all the arms. -:: -++ on-arvo on-arvo:def -:: -:: Handle error -:: -:: Print errors when they happen -:: -++ on-fail - |= [=term =tang] - ^- (quip card _this) - %- (slog leaf+"error in {}" >term< tang) - `this --- - -``` - -### `sur/example-gall.hoon` - -```hoon -|% -+$ example-gall-action - $% - [%view who=@p] - [%stop-view who=@p] - [%increment-remote who=@p] - [%increment ~] - == --- -``` - -### `mar/example-gall/action.hoon` - -```hoon -/- *example-gall -|_ act=example-gall-action -++ grab - |% - ++ noun example-gall-action - -- --- -``` - -## Arms - -### `++on-init` - -Application initialisation - -`++on-init` is called when an application is first started. It is not a gate and -has no input. - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent - -#### Example - -```hoon -:: From reference agent. -:: Set local counter to 1 by default -:: -++ on-init - ^- (quip card _this) - `this(local.sta 1) -:: From app/weather.hoon. This setup is typical of a landscape tile. -:: -++ on-init - :_ this - :~ [%pass /bind/weather %arvo %e %connect [~ /'~weather'] %weather] - :* %pass /launch/weather %agent [our.bol %launch] %poke - %launch-action !>([%weather /weathertile '/~weather/js/tile.js']) - == - == -``` - -### `++on-save` - -Expose state for saving. - -This arm is called immediately before the agent is upgraded. It packages the -permament state of the agent in a vase for the next version of the agent. -Unlike most handlers, this cannot produce effects. It is not a gate and has no input. - -#### Returns - -`vase` of permanent state. - -#### Example - -```hoon -:: From reference agent -:: -:: Expose state for saving -:: -++ on-save - !>(state) -``` - -### `++on-load` - -Application upgrade. - -This arm is called immediately after the agent is upgraded. It receives -a vase of the state of the previously-running version of the agent, obtained -from `+on-save`, which allows it to cleanly upgrade from the old agent. - -#### Accepts - -```hoon -=vase -``` - -`vase` of previous state, from `++on-save` - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent - -#### Examples - -```hoon -:: From reference agent -:: -:: Load old state and upgrade if neccessary -++ on-load - |= old=vase - ^- (quip card _this) - =/ loaded=versioned-state - !<(versioned-state old) - ?- -.loaded - %0 - `this(local local.loaded) :: Upgrade old state - %1 - `this(state loaded) - == -``` - -### `++on-poke` - -Handle application poke. - -This arm is called when the agent is "poked". The input is a cage, so -it's a pair of a mark and a dynamic vase. - -#### Accepts - -```hoon -[=mark =vase] -``` - -`mark` is the mark of the poked data. - -`vase` is a vase with the poked data inside. - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent. - -#### Example - -```hoon -:: From reference agent. -:: -:: Respond to poke -:: App can be poked in the dojo by running the following commands -:: Increment local counter -:: :example-gall &example-gall-action [%increment ~] -:: Increment ~zod's counter -:: :example-gall &example-gall-action [%increment-remote ~zod] -:: Subscribe to ~zod's counter -:: :example-gall &example-gall-action [%view ~zod] -:: Unsubscribe from ~zod's counter -:: :example-gall &example-gall-action [%stop-view ~zod] -:: -++ on-poke - |= [=mark =vase] - ^- (quip card _this) - :: Ensure poke is of mark %example-gall-action - ?> =(mark %example-gall-action) - =/ act=example-gall-action !<(example-gall-action vase) - ?- act - :: - :: Increment local counter and send new counter to subscribers - :: - [%increment ~] - :- [%give %fact ~[/local] %atom !>(+(local))]~ - this(local +(local)) - :: - :: Send remote %increment poke - :: - [%increment-remote who=@p] - :_ this - :~ :* - %pass - /inc/(scot %p who.act) - %agent - [who.act %example-gall] - %poke %example-gall-action !>([%increment ~]) - == == - :: - :: Subscribe to a remote counter - :: - [%view who=@p] - :_ this - [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %watch /local]~ - :: - :: Unsubscribe from remote counter and remove from state - :: - [%stop-view who=@p] - :_ this(remote (~(del by remote) who.act)) - [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %leave ~]~ - :: - == -``` - -### `++on-watch` - -Handle new subscriber. - -This arm is called when a program wants to subscribe to the agent on a -particular path. The agent may or may not need to perform setup steps -to intialize the subscription. It may produce a `%give` -`%subscription-result` to the subscriber to get it up to date, but after -this event is complete, it cannot give further updates to a specific -subscriber. It must give all further updates to all subscribers on a -specific path. - -If this arm crashes, then the subscription is immediately terminated. -More specifcally, it never started -- the subscriber will receive a -negative `%watch-ack`. You may also produce an explicit `%kick` to -close the subscription without crashing -- for example, you could -produce a single update followed by a `%kick`. - -#### Accepts - -```hoon -=path -``` - -Path of new subscription. - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent. - -#### Example - -```hoon -:: From reference agent -:: -:: Handle new subscription -:: -:: When another ship subscribes to our counter, give them the current state of -:: the counter immediately -:: -++ on-watch - |= =path - ^- (quip card _this) - :_ this - ?+ path on-watch:def - :: - [%local ~] - [%give %fact ~ %atom !>(local)]~ - == -``` - -### `++on-leave` - -Handle unsubscribe. - -This arm is called when a program becomes unsubscribed to you. -Subscriptions may close because the subscriber intentionally -unsubscribed, but they also could be closed by an intermediary. For -example, if a subscription is from another ship which is currently -unreachable, Ames may choose to close the subscription to avoid queueing -updates indefinitely. If the program crashes while processing an -update, this may also generate an unsubscription. You should consider -subscriptions to be closable at any time. - -#### Accepts - -```hoon -=path -``` - -Path of the closed subscription. - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent. - -#### Example - -```hoon -:: From reference agent. Prints a message when programs become unsubscribed. -++ on-leave - |= =path - ^- (quip card _this) - ~& "Unsubscribe by: {} on: {}" - `this -``` - -### `++on-peek` - -Handle scry request. - -This arm is called when a program reads from the agent's "scry" -namespace, which should be referentially transparent. Unlike most -handlers, this cannot perform IO, and it cannot change the state. All -it can do is produce a piece of data to the caller, or not. - -#### Accepts - -```hoon -=path -``` - -The path being scryed for. - -```hoon -:: Example scry to path mappings -:: -.^(arch %gy /=example-gall=/remote) -:: Path will be /y/remote -.^(@ %gx /=example-gall=/local/atom) -:: Path will be /x/local -``` - -#### Returns - -```hoon -(unit (unit cage)) -``` - -If this arm produces `[~ ~ data]`, then `data` is the value at the the -given path. If it produces `[~ ~]`, then there is no data at the given -path and never will be. If it produces `~`, then we don't know yet whether -there is or will be data at the given path. The head of the path is known as the -`care`. Requests with a care of `%x` should return a vase that matches or is -convertible to the mark at the end of the scry request. This mark is not -included in the path passed to `++on-peek`. Requests with a care of `%y` should -return a cage with a mark of `%arch` and a vase of `arch`. - -#### Example - -```hoon -:: From reference agent -:: -:: Expose scry namespace -:: -:: .^(@ %gx /=example-gall=/local/atom) will produce the current local counter -:: .^(@ %gx /=example-gall=/remote/~zod/atom) will produce the counter for ~zod -:: .^(arch %gy /=example-gall=/remote) will produce a listing of the current -:: remote counters -++ on-peek - |= =path - ^- (unit (unit cage)) - ?+ path [~ ~] - :: - :: Produce local counter - :: - [%x %local ~] - ``[%atom !>(local)] - :: - :: Produce remote counter - :: - [%x %remote who=@ta ~] - =* location i.t.t.path :: Ship name is third in the list - =/ res - (~(got by remote) (slav %p location)) - ``[%atom !>(res)] - :: - :: Produce listing of remote counters - :: - [%y %remote ~] - =/ dir=(map @ta ~) - %- molt :: Map from list of k-v pairs - %+ turn :: iterate over list of k-v pairs - ~(tap by remote) :: list of k-v pairs from map - |= [who=@p *] - [(scot %p who) ~] - ``[%arch !>(`arch`[~ dir])] - == -``` - -### `++on-agent` - -Handle `%pass` card - -This arm is called to handle responses to `%pass` cards to other agents. -It will be one of the following types of response: - -- `%poke-ack`: acknowledgment (positive or negative) of a poke. If the - value is `~`, then the poke succeeded. If the value is `[~ tang]`, - then the poke failed, and a printable explanation (eg a stack trace) - is given in the `tang`. - -- `%watch-ack`: acknowledgment (positive or negative) of a subscription. - If negative, the subscription is already ended (technically, it never - started). - -- `%fact`: update from the publisher. - -- `%kick`: notification that the subscription has ended. This happens because - either the target app passed a `%leave` note, or ames killed the subscription - due to backpressure. Most of the time you will want to resubscribe. If you can - no longer access the subscription you will get a negative `%watch-ack` and end - your flow there. - -#### Accepts - -```hoon -[=wire =sign:agent:gall] -``` - -`wire` is the wire from the `+gift` that triggered `++on-agent` - -`sign` is response for the gift. - -#### Returns - -```hoon -(quip card _this) -``` - -#### Example - -```hoon -:: From reference agent -:: -:: Handle sign from agent -:: -++ on-agent - |= [=wire =sign:agent:gall] - ^- (quip card _this) - ?- -.sign - :: - :: Print error if poke failed - :: - %poke-ack - ?~ p.sign - `this - %- (slog u.p.sign) - `this - :: - :: Print error if subscription failed - :: - %watch-ack - ?~ p.sign - `this - =/ =tank leaf+"subscribe failed from {} on wire {}" - %- (slog tank u.p.sign) - `this - :: - :: Do nothing if unsubscribed - :: - %kick `this - :: - :: Update remote counter when we get a subscription update - :: - %fact - :- ~ - ?. ?=(%atom p.cage.sign) - this - this(remote (~(put by remote) src.bowl !<(@ q.cage.sign))) - == -``` - -### `++on-arvo` - -Handle vane response - -This arm is called to handle responses for `%pass` cards to vanes. - -#### Accepts - -```hoon - [=wire =sign:agent:gall] -``` - -`wire` is the wire from the `++gift` that triggered `++on-arvo`. - -`sign` is the response from the vane. The list of possible responses from the -vanes is statically defined in sys/zuse.hoon (grep for `++ sign-arvo`). - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent. - -#### Example - -```hoon -:: From app/weather.hoon. Handles %bound, %wake and %http-response signs -++ on-arvo - |= [=wire =sign-arvo] - ^- (quip card:agent:gall _this) - ?: ?=(%bound +<.sign-arvo) - [~ this] - ?: ?=(%wake +<.sign-arvo) - =^ cards state - (wake:wc wire error.sign-arvo) - [cards this] - ?: ?=(%http-response +<.sign-arvo) - =^ cards state - (http-response:wc wire client-response.sign-arvo) - [cards this] - (on-arvo:def wire sign-arvo) -:: -:: From reference agent -:: -:: Handle arvo signs -:: -:: We never give any cards to arvo. Therefore we never need to handle any signs -:: from arvo. We use the default-agent library to avoid implementing this arm, -:: as gall apps must have all the arms specified in the agent:gall definition. -:: -++ on-arvo on-arvo:def -``` - -### `++on-fail` - -Handle error. - -If an error happens in `+on-poke`, the crash report goes into the -`%poke-ack` response. Similarly, if an error happens in -`+on-subscription`, the crash report goes into the `%watch-ack` -response. If a crash happens in any of the other handlers, the report -is passed into this arm. - -#### Accepts - -```hoon -[=term =tang] -``` - -`term` is a cord describing the error. - -`tang` is a stack trace for the error. - -#### Returns - -```hoon -(quip card _this) -``` - -List of cards and new agent - -#### Example - -```hoon -:: -:: Handle error -:: -:: Print errors when they happen -:: -++ on-fail - |= [=term =tang] - ^- (quip card _this) - %- (slog leaf+"error in {}" >term< tang) - `this -``` - -## Agent Gifts - -Giving a gift takes the general form of - -```hoon -[%give =gift] -``` - -### `%fact` - -Produce a subscription update. - -Produces a subscription update. A subscription update is a new piece of -subscription content for all subscribers on a given path. - -#### Structure - -```hoon -[%fact (list path) =cage] -``` - -`(list path)` is a list of the paths to send the update on. If no path is -given, then the update is only given to the program that instigated the -request. Typical use of this mode is in `+on-watch` to give an initial update -to a new subscriber to get them up to date. - -`cage` is a cage of the subscription update. - -#### Example - -```hoon -:: From ++on-watch in reference agent. -:: -:: Gives current local state to new subscribers. -[%give %fact ~ %atom !>(local.sta)] - - -:: From ++on-poke in reference agent -:: -:: Gives incremented local state to any subscribers on /local -[%give %fact ~[/local] %atom !>(+(local))] -``` - -### `%kick` - -Close subscription. - -Closes a subscription. A subscription close closes the subscription for all or -one subscribers on a given path. - -#### Structure - -```hoon -[%kick (unit path) (unit ship)] -``` - -`(unit path)` is the path of the subscription being updated. If no path is -given, then the update is only given to the program that instigated the request. -Typical use of this mode would be in `+on-watch` to produce a single update to a -subscription then close the subscription. - -`(unit ship)` is the ship to close the subscription for. If no path is given, -then the subscription is closed for all subscribers. - ## Agent Notes -Passing a agent note (a 'task') along a wire looks like so. +A `note` is a request to a vane or agent which you initiate. A `note` is +one of: ```hoon -[%pass =wire %agent [=ship name=term] =task] ++$ note + $% [%agent [=ship name=term] =task] + [%arvo note-arvo] + [%pyre =tang] + :: + [%grow =spur =page] + [%tomb =case =spur] + [%cull =case =spur] + == ``` -`wire` is used to identify the response to the note. - -`ship` is the ship to pass the note to. - -`name` is the name of the agent that should receive the note. - -`task` is the task itself, described below. - -### `%poke` - -Poke an application. - -This note is passed to poke an application with a cage, a marked vase. - -#### Structure +A `note` is always wrapped in a `%pass` `card`, like so: ```hoon -[%poke =cage] +[%pass p=wire q=note] ``` -`cage` is the marked data to poke the application with. It is a pair of a mark -and vase. +The `wire` is just a `path` like `/foo/bar/baz`. You use it as a tag to +identify responses. -#### Example +The possible cases of an `%agent` `note` are documented [separately +below](#agent-tasks). + +We'll look at the remaining cases here. + +### `%arvo` + +Pass a vane `task` to a vane (kernel module). ```hoon -:: From ++on-poke in reference agent. -:: -:: Sends an increment poke to the example-gall agent -:: on who.act. -:* - %pass - /inc/(scot %p who.act) - %agent - [who.act %example-gall] - %poke %example-gall-action !>([%increment ~]) -== +[%arvo note-arvo] ``` +A `note-arvo` is defined as the following: + +```hoon ++$ note-arvo + $~ [%b %wake ~] + $% [%a task:ames] + [%b task:behn] + [%c task:clay] + [%d task:dill] + [%e task:eyre] + [%g task:gall] + [%i task:iris] + [%j task:jael] + [%k task:khan] + [%$ %whiz ~] + [@tas %meta vase] + == +``` + +The first part is vane letter (`%g` for Gall, `%i` for Iris, etc). The +second part is a `task` belonging to that vane. + +--- + +### `%pyre` + +Abort event. + +```hoon +[%pyre =tang] +``` + +This `note` tells Gall to crash with the given `tang` in the stack +trace. You'd use it in `++on-load` or `++on-init` when you wanted the +upgrade/installation to fail under some condition. + +--- + +### `%grow` + +Publish remote scry file. + +```hoon +[%grow =spur =page] +``` + +The `spur` is the `path` the file should be published at. The revision +number will be determined implicitly. As an example, if the `spur` was +`/foo`, the agent `%bar`, and it was the first revision, the resulting +remote scry path would be `/g/x/0/bar//foo` + +The `page` is the file, a pair of `[p=mark q=noun]`. + +--- + +### `%tomb` + +Delete remote scry file. + +```hoon +[%tomb =case =spur] +``` + +The `case` is the file revision, for example `[%ud 3]`. The spur is the +`path` it's bound to, for example `/foo`. + +The file at the specified `spur` and specific `case` will be deleted and +replaced by a simple hash. + +--- + +### `%cull` + +Delete remote scry file up to the given revision. + +```hoon +[%cull =case =spur] +``` + +All revisions of the remote scry file published at the `path` in `spur` +up to and including the revision specified in `case` will be deleted. +For example, if the `case` is `[%ud 2]`, then revisions `0`, `1`, and +`2` will all be deleted. + +--- + +## Agent Tasks + +A `task` is a request to an agent you initiate, as opposed to a +[`gift`](#agent-gifts), which is a response. + +Passing an agent `task` looks like so: + +```hoon +[%pass p=wire q=[%agent [=ship name=term] =task]] +``` + +- `wire`: this is just a `path` like `/foo/bar/baz`. You use it as a tag + to identify any [`gift`](#agent-gifts) that come back in response. +- `ship`: is the ship to pass the `task` to. +- `name`: is the name of the agent on the specified ship that should + receive the `task`. +- `task`: the `task` itself, as described below. + ### `%watch` -Subscribe to an application. - -This note is given to subscribe to an application at a path - -#### Structure +Subscribe to a path on an agent for updates. ```hoon [%watch =path] ``` -`path` is the path to be subscribed to. +The `path` is a subscription `path` like `/foo/bar/baz` which the +receiving agent publishes updates on. The publisher's Gall will +automatically respond with a [`%watch-ack`](#watch-ack). The +`%watch-ack` will be positive (an "ack") if the agent did not crash +processing the `%watch`, and will be negative (a "nack") if it crashed. -#### Example +Assuming the subscription request was successful (and therefore the +`%watch-ack` was positive), the publisher will begin sending updates as +[`%fact`](#fact) `gift`s to the subscriber. The publisher will continue +sending updates until the subscriber [`%leave`](#leave)s or the +publisher [`%kick`](#kick)s them. + +--- + +### `%watch-as` + +Subscribe to a path on an agent for updates, asking for the updates to +have a specified `mark`. ```hoon -:: From ++on-poke in reference agent. -:: -:: Subscribes to the example-gall agent on who.act on the path /local -:: -[%pass /view/(scot %p who.act) %agent [who.act %example-gall] %watch /local] +[%watch-as =mark =path] ``` +The `path` is a subscription `path` like `/foo/bar/baz` which the +receiving agent publishes updates on. The `mark` is the `mark` you want +the publisher to use for the data it gives you in the updates. + +This behaves the same as an ordinary [`%watch`](#watch) request, except +the publisher's Gall will try to convert from the `mark` of the `%fact`s +the agent produced to the `mark` you specified before sending it off. If +the publisher's Gall is unable to perform the mark conversion, you'll +get [`%kick`](#kick)ed from the subscription, and they'll send +themselves a [`%leave`](#leave) on your behalf. + +--- + ### `%leave` -Unsubscribe from an application. - -This note is passed to unsubscribe from an application. It should be passed on -the same wire that the corresponding `%watch` note for the subscription was -passed on. - -#### Structure +Unsubscribe from a subscription path on an agent. ```hoon [%leave ~] ``` -#### Example +The subscription to end is determined by the `wire`, `ship` and agent +`name` in the `%pass` `card` this is wrapped in. That is, if you +originally subscribed to subscription path `/foo/bar/baz` in agent +`%foo` on ship `~zod` using `wire` `/x/y/z`, you'd unsubscribe by +specifying `/x/y/z`, `~zod` and `%foo`. + +Once sent, you'll stop receiving `%fact`s from the publisher for the +subscription in question. + +--- + +### `%poke` + +A one-off request/datagram to an agent. ```hoon -:: From ++on-poke in reference agent. -:: -:: Unsubscribes from the example-gall agent on who.act -[%pass /view/(scot %p who.act) %agent [who.act %example-gall] %leave ~] +[%poke =cage] ``` + +A `%poke` `task` is a one-off, unsolicited delivery of some data. This +is in contrast to a [`%fact`](#fact) `gift`, the other basic method of +passing data between agents, which is ultimately a solicited response to +a past [`%watch`](#watch) request for subscription updates. Unlike a +`%watch` request, the recipient of the `%poke` cannot directly send data +back to the `%poke`-er (though they could conceivably send a new, +separate `%poke` back). The only response you get to a `%poke` is a +[`%poke-ack`](#poke-ack), indicating a simple success/failure result. + +The data of the `%poke` is contained in the `cage`, which is a pair of +`[p=mark q=vase]`. It's the basic way to pass around dynamically typed +data. + +--- + +### `%poke-as` + +A one-off request/datagram to an agent, asking the recipient's Gall to +convert the data to the specified `mark` before delivering it to the +agent. + +```hoon +[%poke-as =mark =cage] +``` + +This behaves the same as an ordinary [`%poke`](#poke) but with +additional `mark` conversion to the `mark` you specify by the +recipient's Gall. + +The `mark` is the `mark` you want the `cage` converted *to* before +delivery to the agent. The `cage` is the data itself, a pair of `[p=mark +q=vase]`. The mark conversion will be performed by the recipient's Gall, +not the sender's. + +If the `mark` conversion fails, the sender will be sent a negative +[`%poke-ack`](#poke-ack) (nack). Otherwise, the recipient will receive a +`%poke` with the target `mark` specified. + +--- + +## Agent Gifts + +An agent `gift` is ultimately a response to an agent `task`. Sometimes +it's an immediate, direct response, and other times it happens down the +line, or there's an ongoing series of gifts, as in the case of +subscriptions. They do all ultimately arise from an original `task`, +though, be it a a `%watch` subscription request or a `%poke`. A `gift` +cannot be sent out unsolicited to other agents. Where they are routed +to, whether another local agent, an agent on a remote ship, or even to +vanes or a browser-based front-end in some cases, is determined by the +original `task`. + +Giving a gift takes the general form of: + +```hoon +[%give p=gift] +``` + +Each possible `gift` is detailed below. + +### `%fact` + +Produce a subscription update. + +```hoon +[%fact paths=(list path) =cage] +``` + +A `%fact` is a piece of data given to all subscribers on one or more +subscription paths. + +The fields are: + +- `paths`: a list of subscription paths to send the update on. In + `+on-watch` alone, if no path is given, then the update is given + exclusively to the source of the `%watch` request. This is useful for + giving initial state to new subscribers. In other contexts, one or + more subscription paths should be provided. +- `cage`: the data. A `cage` is a pair of `[p=mark q=vase]`. + +--- + +### `%kick` + +Close subscription. + +```hoon +[%kick paths=(list path) ship=(unit ship)] +``` + +If `ship` is null, all subscribers will be kicked from the specified +subscription `paths` and will stop receiving updates. If `ship` is +non-null, only the specified ship will be kicked from the given `paths`. + +It should be noted that `%kick` `gift`s are not *only* emitted +intentionally by the publishing agent. Gall itself will `%kick` remote +subscribers if too many undelivered outbound `%fact`s queue up due to +network connectivity problems. On the subscriber side, their Gall will +`%kick` themselves if they crash while processing an incoming `%fact`. +It should therefore not be assumed the `%kick` was intentional. +Typically agents will be designed to resubscribe on `%kick` with a new +`%watch`, only giving up on negative `%watch-ack`. You should be careful +with automatic resubscribe logic, though, because you can inadvertently +create a network loop of infinite resubscribes and kicks if, for +example, a crash on `%fact` is repeatable. + +--- + +### `%watch-ack` + +Acknowledge a subscription request. + +```hoon +[%watch-ack p=(unit tang)] +``` + +A `%watch-ack` is automatically given by Gall in response to a `%watch` +`task`. A `%watch-ack` is either positive (an "ack") or negative (a +"nack"). It's an ack when `p` is null, and a nack when `p` is non-null, +instead containing a stack trace. + +A `%watch-ack` is given *automatically* and *implicitly* by Gall itself, +it is unnecessary for an agent to emit one explicitly. An ack will be +given as long as `++on-watch` doesn't crash. A nack will be given if it +*does* crash, with a trace of the crash in `p`. Your agent should +therefore be designed to accept or reject a subscription request by +crashing or not crashing, respectively. + +--- + +### `%poke-ack` + +Acknowledge a poke. + +```hoon +[%poke-ack p=(unit tang)] +``` + +A `%poke-ack` is automatically given by Gall in response to a `%poke` +`task`. A `%poke-ack` is either positive (an "ack") or negative (a +"nack"). It's an ack when `p` is null, and a nack when `p` is non-null, +instead containing a stack trace. + +A `%poke-ack` is given *automatically* and *implicitly* by Gall itself, +it is unnecessary for an agent to emit one explicitly. An ack will be +given as long as `++on-poke` doesn't crash. A nack will be given if it +*does* crash, with a trace of the crash in `p`. Your agent should +therefore be designed to accept or reject a poke by crashing or not +crashing, respectively. + +--- + +## Vane Tasks + +These are the Vane `task`s that can be `%pass`ed to Gall itself in an +`%arvo` `note`. Most of these are only used internally by the kernel, +though some app management `task`s might be of use in userspace. + +### `%deal` + +Full transmission. + +```hoon +[%deal p=sock q=term r=deal] +``` + +Gall translates agent +[`task:agent`](/reference/arvo/gall/data-types#taskagent)s emitted by +agents into `%deal` tasks, as well as requests from over the network. +This `task` is kernel-level only, it cannot be used directly from +userspace. + +Its fields are: + +- `p`: A `sock`, a `(pair ship ship)`, the sending and receiving ships. +- `q`: The source agent. +- `r`: A [`deal`](/reference/arvo/gall/data-types#deal) is either a + [`task:agent`](/reference/arvo/gall/data-types#taskagent) or a + `%raw-poke`. This is the request itself. + +#### Returns + +Gall returns no `gift` in response to a `%deal`. + +--- + +### `%sear` + +Clear pending queues. + +```hoon +[%sear =ship] +``` + +This `task` clears blocked inbound `move`s from the given ship. Moves +get blocked and queued when sent to an agent that isn't currently +running. + +#### Returns + +Gall returns no `gift` in response to a `%sear`. + +--- + +### `%jolt` + +Restart agent (deprecated). + +```hoon +[%jolt =desk =dude] +``` + +Restart agent `dude` on desk `desk`. This `task` is deprecated and now a +no-op. + +#### Returns + +Gall returns no `gift` in response to a `%jolt`. + +--- + +### `%idle` + +Suspend agent. + +```hoon +[%idle =dude] +``` + +The agent specified in `dude` will be suspended. Note it is usually +better to suspend agents with a +[`%rein`](/reference/arvo/clay/tasks#rein---force-apps) `task` to Clay +rather than an `%idle` `task` to Gall. + +#### Returns + +Gall returns no `gift` in response to an `%idle`. + +--- + +### `%load` + +Load agents. + +```hoon +[%load =load] +``` + +This `task` is given to Gall by Clay. It contains the compiled agents to +be installed or updated. This `task` would not be used from userspace. + +See the [`load`](/reference/arvo/gall/data-types#load) entry in the +type reference for more details of the datastructure in this `task`. + +#### Returns + +Gall returns no `gift` in response to a `%load`. + +--- + +### `%nuke` + +Delete agent. + +```hoon +[%nuke =dude] +``` + +The agent in `dude` will be stopped and its state discarded. + +{% callout %} + +**WARNING:** This will irreversibly erase all data stored in the state +of the agent. Use with care and caution. + +{% /callout %} + +#### Returns + +Gall returns no `gift` in response to a `%nuke`. + +--- + +### `%doff` + +Kill old-style subscriptions. + +```hoon +[%doff dude=(unit dude) ship=(unit ship)] +``` + +Kills nonceless outgoing subscriptions. If `dude` is non-null, it only +applies to the specified agent. If the `ship` is non-null, it only +applies to subscriptions to the specified ship. Otherwise, it applies to +all subscriptions. + +You're unlikely to use this `task` from userspace. + +#### Returns + +Gall returns no `gift` in response to a `%doff`. + +--- + +### `%rake` + +Reclaim old subscriptions. + +```hoon +[%rake dude=(unit dude) all=?] +``` + +This sends an Ames `%cork` on any old subscription ducts. If `dude` is +null, it applies to all agents, otherwise to the specified one. The +`all` flag should only be set if you want the ship to try and kill an +old subscription at sub-nonce zero. + +You are unlikely to use this `task`. + +#### Returns + +Gall returns no `gift` in response to a `%rake`. + +--- + +### `%spew` + +Set verbosity. + +```hoon +[%spew veb=(list verb)] +``` + +This sets verbosity flags for Gall. Currently there's only one +[`verb`](/reference/arvo/gall/data-types#verb), `%odd`, which prints +messages for unusual error cases. This overwrites the existing verbosity +settings: an empty list will turn all verbosity flags off. + +#### Returns + +Gall returns no `gift` in response to a `%spew`. + +--- + +### `%sift` + +Filter verbose debug printing to certain agents. + +```hoon +[%sift dudes=(list dude)] +``` + +The `dudes` are the agents you want verbose debug printing for. An empty +list enables it for all agents. See [`%spew`](#spew) for setting +verbosity. + +#### Returns + +Gall returns no `gift` in response to a `%sift`. + +--- + diff --git a/content/reference/arvo/gall/gall.md b/content/reference/arvo/gall/gall.md index fc0a0fc..6c07b08 100644 --- a/content/reference/arvo/gall/gall.md +++ b/content/reference/arvo/gall/gall.md @@ -59,10 +59,11 @@ definable in a regular recursive data type). ## Specification -An agent is defined as a [core](/reference/glossary/core/) with a set of [arms](/reference/glossary/arm/) to handle various -events. These handlers usually produce a list of effects and the next -state of the agent. The interface definition can be found in -`sys/lull.hoon`, which at the time of writing is: +An agent is defined as a [core](/reference/glossary/core/) with a set of +[arms](/reference/glossary/arm/) to handle various events. These +handlers usually produce a list of effects and the next state of the +agent. The interface definition can be found in `sys/lull.hoon`, which +at the time of writing is: ```hoon ++ agent @@ -71,8 +72,13 @@ state of the agent. The interface definition can be found in +$ step (quip card form) +$ card (wind note gift) +$ note - $% [%arvo =note-arvo] - [%agent [=ship name=term] =task] + $% [%agent [=ship name=term] =task] + [%arvo note-arvo] + [%pyre =tang] + :: + [%grow =spur =page] + [%tomb =case =spur] + [%cull =case =spur] == +$ task $% [%watch =path] @@ -82,8 +88,8 @@ state of the agent. The interface definition can be found in [%poke-as =mark =cage] == +$ gift - $% [%fact path=(unit path) =cage] - [%kick path=(unit path) ship=(unit ship)] + $% [%fact paths=(list path) =cage] + [%kick paths=(list path) ship=(unit ship)] [%watch-ack p=(unit tang)] [%poke-ack p=(unit tang)] == @@ -134,6 +140,7 @@ state of the agent. The interface definition can be found in |~ [term tang] *(quip card _^|(..on-init)) -- + -- ``` Here's a skeleton example of an implementation: diff --git a/content/reference/arvo/gall/scry.md b/content/reference/arvo/gall/scry.md new file mode 100644 index 0000000..a04d89b --- /dev/null +++ b/content/reference/arvo/gall/scry.md @@ -0,0 +1,252 @@ ++++ +title = "Scry Reference" +weight = 4 ++++ + +Gall's scry interface is mainly used to scry into the individual agents it's +running. The vane itself does have its own interface, however. Both [agent +scries](#agent-scries) and [vane scries](#vane-scries) are documented below. + +Note that for all agent scries and most vane scries, `q.beak`, where there'd +usually be a `desk`, will be the agent name instead, like: + +``` +.^(some-type %gx /=agent-name-here=/some/path/noun) +``` + +{% callout %} + +**Important:** Vane scries are differentiated from agent scries by an extra +empty (`%$`) element at the beginning of the `spur` (the path after the +`beak`), like: `/=agent-name/$` or `/=agent-name=//some/more/fields`. Without +that empty element, Gall will try route the scry to an agent instead. + +{% /callout %} + +## Agent scries + +In order to hit the `+on-peek` arm of a Gall agent, you need to: + +1. Put the agent in `q.beak` of the scry path (where the `desk` usually goes), like `/=some-agent=`. +2. Make sure the beginning of the `spur` is *not* an empty `%$` element, as + that will route the scry to the [vane endpoints](#vane-scries) instead. An + agent scry must be `/=some-agent=/some/path` not `/=some-agent=//some/path`. + +Any `care` can be used (dependent on what the agent accepts, of course). The +most common is `%x`. + +{% callout %} + +Note that `%x` cares alone must include an extra `mark` field at the end of the +`spur`. This mark field lets Gall perform any necessary mark conversions before +returning your data. For plain unmarked data, you can just use the `%noun` +mark. As an example, if an agent specifies an endpoint `/x/some/path` and just +returns ordinary data, you'd do `.^(some-type %gx +/=some-agent=/some/path/noun)`. If the endpoint returns a `%json` mark or +whatever (and that's what you want), you'd put that at the end instead. + +{% /callout %} + +--- + +## Vane scries + +Gall itself provides the special vane-level endpoints listed below. They are +organized by the `care`. In order to hit the vane-level endpoints, the +beginning of the the `spur` (e.g. the `path` after the `beak`) *must* be a `%$` +empty element. For example: + +```hoon +.^(desk %gd /=acme=/$) +.^((set [=dude:gall live=?]) %ge /=base=/$) +.^((list path) %gt /=acme=//foo) +``` + +Note you can use `$` to make the last element empty since it won't allow a +trailing `/`. Note how in the third example, the empty element is at the +*beginning* of the `spur` and *after* the `beak`. If you fail to include this +empty element, Gall will try route the scry to an agent for handling instead. + +### `%d`: get desk of app + +A scry with a `%d` care and an agent in `q.beak` will +give you the desk that agent is on. + +#### Produces + +A `desk` + +#### Example + +``` +> .^(desk %gd /=acme=/$) +%base +``` + +--- + +### `%e`: running apps + +A scry with an `%e` care will give you the agents on the desk given in +`q.beak`. + +#### Produces + +A `(set [=dude live=?])`, where `live` is true if running and false if not. + +#### Examples + +``` +> .^((set [=dude:gall live=?]) %ge /=base=/$) +{ [dude=%acme live=%.y] + [dude=%hood live=%.y] + [dude=%lens live=%.y] + [dude=%dbug live=%.y] + [dude=%azimuth live=%.y] + [dude=%ping live=%.y] + [dude=%dojo live=%.y] + [dude=%eth-watcher live=%.y] + [dude=%spider live=%.y] + [dude=%herm live=%.y] +} +``` + +--- + +### `%f`: nonces of apps + +A scry with a care of `%f` and anything in `q.beak` will produce the +subscription nonces of all apps. You are unlikely to use this, it's +mostly for kernel debugging. + +#### Produces + +A `(map dude @)` where the `@` is the nonce. + +#### Examples + +``` +> .^((map dude:gall @) %gf /=//=/$) +[ n=[p=%treaty q=2] + l + [ n=[p=%metadata-hook q=1] + l={[p=%contacts q=1] [p=%notify q=2] [p=%groups q=1] [p=%dm-hook q=1] [p=%spider q=1]} + r + { [p=%docket q=9] + [p=%bait q=1] + [p=%hood q=15] + [p=%hark-graph-hook q=2] + [p=%s3-store q=1] + [p=%hark-system-hook q=1] +......(truncated for brevity)...... +``` + +--- + +### `%n`: get nonce of subscription + +A scry with a care of `%n`, an agent in `q.beak` and a path of +`//[ship]/[agent]/[wire]` will produce the nonce for that subscription. +You are unlikely to use this, it's mostly for kernel debugging. + +#### Produces + +A `@`. + +--- + +### `%t`: remote scry subpaths + +A scry with a `%t` care, an agent in `q.beak` and a path of `//some/path` +will give you the list of remote scry subpaths bound under the given +path. + +See the [remote scry guide](/guides/additional/remote-scry) for more +details. + +#### Produces + +A `(list path)` + +#### Examples + +``` +> .^((list path) %gt /=acme=//foo) +~ +``` + +--- + +### `%u`: check if installed + +A scry with a `%u` care will check whether the given agent is installed and +running. + +#### Produces + +A `?` + +#### Examples + + +``` +> .^(? %gu /=acme=/$) +%.y +``` + +``` +> .^(? %gu /=doesnt-exist=/$) +%.n +``` + +--- + +### `%w`: latest revision of path + +A scry with a `%w` care and an agent in `q.beak` will get the latest revision +number of the bound remote scry path given in the `spur`. + +See the [remote scry guide](/guides/additional/remote-scry) for more +details. + +#### Produces + +A `cass:clay`, specifically the `%ud` kind. + +--- + +### `%x`: remote scry file + +A scry with a `%x` care and an agent in `q.beak` will get the value +bound at the remote scry path given in the `spur`. The revision of the +file must be given in the `beak` portion of the scry path. The general +format is therefore: + +```hoon +[%gx /[ship]/[agent]/[file revision]//[file path] +``` + +See the [remote scry guide](/guides/additional/remote-scry) for more +details. + +#### Produces + +The type returned is an `(each page @uvI)`. If the file has been +tombstoned, it'll be the `@uvI` hash of the file. Otherwise, it will be +the data as a `page` (a pair of `mark` and `noun`). + +--- + +### `%z`: hash of value at path + +A scry with a `%z` care and an agent in `q.beak` will get the hash identifier +of the value bound at the remote scry path given in the `spur`. + +See the [remote scry guide](/guides/additional/remote-scry) for more +details. + +#### Produces + +A `@uvI`. + +--- diff --git a/content/reference/arvo/gall/threads.md b/content/reference/arvo/gall/threads.md deleted file mode 100644 index 32e870c..0000000 --- a/content/reference/arvo/gall/threads.md +++ /dev/null @@ -1,86 +0,0 @@ -+++ -title = "Threads Reference" -weight = 50 -+++ - -## Start thread - -Poke `spider` with mark `%spider-start` and a vase containing `start-args`: - -```hoon -+$ start-args - [parent=(unit tid) use=(unit tid) =beak file=term =vase] -``` - -Where: - -- `parent` - optional `tid` of parent thread if the thread is a child. If specified, the child thread will be killed with the parent thread ends. -- `use` - `tid` (thread ID) to give the new thread. Can be generated with something like `(scot %ta (cat 3 'my-agent_' (scot %uv (sham eny))))`. However you do it, make sure it's unique. -- `beak` - A `$beak` is a triple of `[p=ship q=desk r=case]`. `p` is always our ship, `q` is the desk which contains the thread we want to run. `r` is a `case`, which specifies a desk revision and is a tagged union of: - ```hoon - +$ case - $% [%da p=@da] :: date - [%tas p=@tas] :: label - [%ud p=@ud] :: number - == - ``` - You'll almost always just want the current revision, so you can specify the `case` as `da+now.bowl`. If the thread is on the same desk as the agent you can also just use `byk.bowl(r da+now)` for the `beak`. -- `file` - name of the thread file in `/ted`. For example, if the thread you want to start is `/ted/foo/hoon` you'd specify `%foo`. -- `vase` - `vase` to be given to the thread when it's started. Can be whatever or just `!>(~)` if it doesn't need any args. - -#### Example - -```hoon -[%pass /some-path %agent [our.bowl %spider] %poke %spider-start !>([~ `tid byk.bowl %foo !>(~)])] -``` - -## Stop thread - -Poke `spider` with mark `%spider-stop` and a vase containing `[tid ?]`, where: - -- `tid` - the `tid` of the thread you want to stop -- `?` - whether thread should end nicely. If `%.y` it'll end with mark `%thread-done` and the bunt value of a vase. If `%.n` it'll end with mark `%thread-fail` and a `[term tang]` where `term` is `%cancelled` and `tang` is `~`. - -#### Example - -```hoon -[%pass /some-path %agent [our.bowl %spider] %poke %spider-stop !>([tid %.y)] -``` - -## Subscribe for result - -Spider will send the result on `/thread-result/[tid]` so you can subscribe there for the result. You should subscribe before starting the thread. - -The result will have a mark of either `%thread-fail` or `%thread-done`. - -- `%thread-fail` - has a vase containing a `[term tang]` where `term` is an error message and `tang` is a traceback. -- `%thread-done` - has a vase of the result of the thread. - -#### Example - -```hoon -[%pass /some-path %agent [our.bowl %spider] %watch /thread-result/[tid]] -``` - -## Subscribe to thread - -You can subscribe to a thread on `/thread/[tid]/path`. Note this is for facts sent off by the thread while it's running, not the final result. The path depends on the particular thread. - -#### Example - -```hoon -[%pass /some-path %agent [our.bowl %spider] %watch /thread/[tid]/thread-path] -``` - -## Poke thread - -To poke a thread you poke spider with a mark of `%spider-input` and a vase of `[tid cage]`. - -- `tid` is the tid of the thread you want to poke -- `cage` is whatever mark and vase you want to poke it with - -#### Example - -```hoon -[%pass /some-path %agent [our.bowl %spider] %poke %spider-input !>([tid %foo !>('foooo')])] -``` diff --git a/content/reference/arvo/jael/scry.md b/content/reference/arvo/jael/scry.md index fe52f18..e88b615 100644 --- a/content/reference/arvo/jael/scry.md +++ b/content/reference/arvo/jael/scry.md @@ -27,6 +27,27 @@ A `%code` scry gets the current web login code. It takes your ship name as its ` ~lidlut-tabwed-pillex-ridrup ``` +## `%fake` + +A `%fake` scry checks whether the current ship is fake (a development ship +booted with the `-F` option). The type returned is a `?`. + +#### Example + +On a fake `~zod`: + +``` +> .^(? %j /=fake=) +%.y +``` + +On a real planet: + +``` +> .^(? %j /=fake=) +%.n +``` + ## `%life` A `%life` scry gets the current `life` (key revision number) of a ship if known, otherwise it crashes. It takes a ship as its `path` and the type returned is a `@ud`. diff --git a/content/reference/arvo/jael/tasks.md b/content/reference/arvo/jael/tasks.md index bb09914..fca9fd9 100644 --- a/content/reference/arvo/jael/tasks.md +++ b/content/reference/arvo/jael/tasks.md @@ -3,222 +3,451 @@ title = "API Reference" weight = 2 +++ -This document details all the `task`s you may wish to send Jael, as well as the `gift`s you'll receive in response. +This document details all the tasks you may wish to send Jael, as well +as the gifts you'll receive in response. -You may also wish to reference the [Data Types](/reference/arvo/jael/data-types) document for details of the types referenced here, and the [Examples](/reference/arvo/jael/examples) document for practical examples of using these `task`s. +You may also wish to reference the [Data +Types](/reference/arvo/jael/data-types) document for details of the +types referenced here, and the [Examples](/reference/arvo/jael/examples) +document for practical examples of using these tasks. -## `%dawn` +## Tasks + +### `%dawn` + +Boot from keys. ```hoon [%dawn dawn-event] ``` -Boot from keys. +This task is called once per ship during the vane initialization phase +immediately following the beginning of the [adult +stage](/reference/arvo/overview#structural-interface-core). This task +is `%pass`ed to Jael by Dill, as Dill is the first vane to be loaded for +technical reasons, though we consider Jael to be the true "first" vane. +This task is only used for ships that will join the Ames network - +fake ships (i.e. made with `./urbit -F zod`) use the [%fake](#fake) +task instead. -This `task` is called once per ship during the vane initialization phase immediately following the beginning of the [adult stage](/reference/arvo/overview#structural-interface-core). This `task` is `%pass`ed to Jael by Dill, as Dill is the first vane to be loaded for technical reasons, though we consider Jael to be the true "first" vane. This `task` is only used for ships that will join the Ames network - fake ships (i.e. made with `./urbit -F zod`) use the [%fake](#fake) `task` instead. - -`%dawn` is used to perform a sequence of initialization tasks related to saving information about Azimuth and the Ames network and booting other vanes for the first time. Upon receipt of a `%dawn` `task`, Jael will: +`%dawn` is used to perform a sequence of initialization tasks related to +saving information about Azimuth and the Ames network and booting other +vanes for the first time. Upon receipt of a `%dawn` task, Jael will: - record the Ethereum block the public key is registered to, - record the URL of the Ethereum node used, - save the signature of the parent planet (if the ship is a moon), - load the initial public and private keys for the ship, -- set the DNS suffix(es) used by the network (currently just `urbit.org`), +- set the DNS suffix(es) used by the network (currently just + `urbit.org`), - save the public keys of all galaxies, - set Jael to subscribe to `%azimuth-tracker`, -- `%slip` a `%init` `task` to Ames, Clay, Gall, Dill, and Eyre, and `%give` an `%init` - `gift` to Arvo, which then informs Unix that the initialization process has concluded. +- `%slip` a `%init` task to Ames, Clay, Gall, Dill, and Eyre, and + `%give` an `%init` gift to Arvo, which then informs Unix that the + initialization process has concluded. -This `task` takes a [$dawn-event](/reference/arvo/jael/data-types#dawn-event) as its argument. +This task takes a +[$dawn-event](/reference/arvo/jael/data-types#dawn-event) as its +argument. -You would not use this `task` manually. +You would not use this task manually. #### Returns -Jael `%give`s an `%init` `gift` to Unix. This occurs after the Dill `%slip` init. +Jael `%give`s an `%init` gift to Unix. This occurs after the Dill +`%slip` init. -## `%fake` +--- + +### `%fake` + +Boot fake ship. ```hoon [%fake =ship] ``` -Boot fake ship. +This task is used instead of [%dawn](#dawn) when creating a fake ship +via the `-F` flag when calling the Urbit binary. It performs a subset of +the actions that `%dawn` performs, modified to accommodate the fake +ship. -This `task` is used instead of [%dawn](#dawn) when creating a fake ship via the `-F` flag when calling the Urbit binary. It performs a subset of the actions that `%dawn` performs, modified to accommodate the fake ship. - -`%fake` endows the ship with a private key and a public key deterministically derived from the ship's `@p`. It sets `fak.own.pki` to `%.y`, which is the bit that determines whether or not a ship is fake. Other parts of the Jael state, such as the sponsorship chain and galaxy public keys are left at their bunted values. +`%fake` endows the ship with a private key and a public key +deterministically derived from the ship's `@p`. It sets `fak.own.pki` to +`%.y`, which is the bit that determines whether or not a ship is fake. +Other parts of the Jael state, such as the sponsorship chain and galaxy +public keys are left at their bunted values. The `ship` field specifies the `@p` of the fake ship being created. -You would not use this `task` manually. +You would not use this task manually. #### Returns -Jael `%give`s a `%init` `gift` to Unix. +Jael `%give`s a `%init` gift to Unix. -## `%listen` +--- + +### `%listen` + +Set Ethereum source. ```hoon [%listen whos=(set ship) =source] ``` -Set Ethereum source. +Sets the source that the public keys for a set of `ship`s should be +obtained from. This can either be a Gall app that communicates with an +Ethereum node such as `%azimuth-tracker`, as in the case of galaxies, +stars, and planets, or a ship, as in the case of moons. -Sets the source that the public keys for a set of `ship`s should be obtained from. This can either be a Gall app that communicates with an Ethereum node such as `%azimuth-tracker`, as in the case of galaxies, stars, and planets, or a ship, as in the case of moons. +`whos` is the set of ships whose key data source is to be monitored. The +[$source](/reference/arvo/jael/data-types#source) is either a ship or +the name of a Gall app to use as a source. A `%listen` task with empty +`whos` will set the default source. When the `source` is a ship, Jael +will obtain public keys for ships in `(set ship)` from the given ship. +By default, the `source` for a moon will be the planet that spawned that +moon. -`whos` is the set of ships whose key data source is to be monitored. The [$source](/reference/arvo/jael/data-types#source) is either a ship or the name of a Gall app to use as a source. A `%listen` `task` with empty `whos` will set the default source. When the `source` is a ship, Jael will obtain public keys for ships in `(set ship)` from the given ship. By default, the `source` for a moon will be the planet that spawned that moon. - -You are unlikely to use this `task` manually. +You are unlikely to use this task manually. #### Returns -Jael will not return any `gift`s in response to a `%listen` `task`. +Jael will not return any gifts in response to a `%listen` task. -## `%meet` +--- + +### `%meet` + +This task is deprecated and does not perform any actions. ```hoon [%meet =ship =life =pass] ``` -This `task` is deprecated and does not perform any actions. +--- -## `%moon` +### `%moon` + +Register moon keys or otherwise administer a moon. ```hoon [%moon =ship =udiff:point] ``` -Register moon keys or otherwise administer a moon. - -This is what is sent to Jael by `%hood` behind the scenes when you run `|moon`, `|moon-breach` or `|moon-cycle-keys`. The `ship` field is the moon's `@p`. The [$udiff:point](/reference/arvo/jael/data-types#udiffpoint) will contain the bunt of an [$id:block](/reference/arvo/jael/data-types#idblock) (since moons aren't registered in Azimuth) and one of the `udiff` actions depending on what you want to do. +This is what is sent to Jael by `%hood` behind the scenes when you run +`|moon`, `|moon-breach` or `|moon-cycle-keys`. The `ship` field is the +moon's `@p`. The +[$udiff:point](/reference/arvo/jael/data-types#udiffpoint) will contain +the bunt of an [$id:block](/reference/arvo/jael/data-types#idblock) +(since moons aren't registered in Azimuth) and one of the `udiff` +actions depending on what you want to do. #### Returns -Jael does not return any `gift`s in response to a `%moon` `task`. +Jael does not return any gifts in response to a `%moon` task. -## `%nuke` +--- + +### `%nuke` + +Cancel subscription to public or private key updates. ```hoon [%nuke whos=(set ship)] ``` -Cancel subscription to public or private key updates. +If you've subscribed to public or private key updates from Jael with a +[%private-keys](#private-keys) or [%public-keys](#public-keys) task, +you can unsubscribe and stop receiving updates with a `%nuke` task. +The `(set ship)` is the `set` of `ship`s which you want to stop +tracking. Jael organises subscriptions based on `duct`s, and will +determine which subscription to cancel implicitly based on the `duct` +the `%nuke` task came from. This means a `%nuke` task only works +from the same thread or agent and on the same `path` as the original +subscription request. -If you've subscribed to public or private key updates from Jael with a [%private-keys](#private-keys) or [%public-keys](#public-keys) `task`, you can unsubscribe and stop receiving updates with a `%nuke` `task`. The `(set ship)` is the `set` of `ship`s which you want to stop tracking. Jael organises subscriptions based on `duct`s, and will determine which subscription to cancel implicitly based on the `duct` the `%nuke` `task` came from. This means a `%nuke` `task` only works from the same thread or agent and on the same `path` as the original subscription request. - -To cancel a subscription to the ship's private keys you must leave `whos` empty like `[%nuke ~]`. +To cancel a subscription to the ship's private keys you must leave +`whos` empty like `[%nuke ~]`. #### Returns -Jael does not return a `gift` in response to a `%nuke` `task`. +Jael does not return a gift in response to a `%nuke` task. #### Examples -See the [%public-keys and %nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of the Examples document for an example of using `%nuke` to cancel a `%public-keys` subscription. See the thread in the [%private-keys](/reference/arvo/jael/examples#private-keys) example for cancelling a `%private-keys` subscription. +See the [%public-keys and +%nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of +the Examples document for an example of using `%nuke` to cancel a +`%public-keys` subscription. See the thread in the +[%private-keys](/reference/arvo/jael/examples#private-keys) example for +cancelling a `%private-keys` subscription. -## `%private-keys` +--- + +### `%private-keys` + +Subscribe to private key updates. ```hoon [%private-keys ~] ``` -Subscribe to private key updates. - -Subscribe to be notified of private key updates for the local ship. The subscription will continue until Jael receives a [%nuke](#nuke) `task` to cancel it. +Subscribe to be notified of private key updates for the local ship. The +subscription will continue until Jael receives a [%nuke](#nuke) task +to cancel it. #### Returns -Jael responds to a `%private-keys` `task` with a `%private-keys` `gift` which looks like: +Jael responds to a `%private-keys` task with a [`%private-keys` +gift](#private-keys-1). -```hoon -[%private-keys =life vein=(map life ring)] -``` - -The `life` is the current life of the ship and the `vein` `map` contains the private key for each life up to (and including) the current one. Upon first subscribing, Jael will immediately respond with a `%private-keys` `gift`. Then, whenever the ship's private keys are changed, it'll send a new and updated `%private-keys` `gift`. +Jael will immediately respond with a `%private-keys` gift. Then, +whenever the ship's private keys are changed, it'll send a new and +updated `%private-keys` gift. #### Example -See the [%private-keys](/reference/arvo/jael/examples#private-keys) section of the Examples document for a practical example. +See the [%private-keys](/reference/arvo/jael/examples#private-keys) +section of the Examples document for a practical example. -## `%public-keys` +--- + +### `%public-keys` + +Subscribe to public key (and related) updates from Jael. ```hoon [%public-keys ships=(set ship)] ``` -Subscribe to public key (and related) updates from Jael. - -An agent or thread can subscribe to be notified of public key updates, sponsorship changes and continuity breaches for the `set` of `ship`s specified in the `ships` field. The subscription will continue until Jael receives a [%nuke](#nuke) `task` to cancel it. +An agent or thread can subscribe to be notified of public key updates, +sponsorship changes and continuity breaches for the `set` of `ship`s +specified in the `ships` field. The subscription will continue until +Jael receives a [%nuke](#nuke) task to cancel it. #### Returns -Jael responds to a `%public-keys` `task` with `%public-keys` `gift`s which look like: +Jael responds to a `%public-keys` task with [`%public-keys` +gift](#public-keys-1). -```hoon -[%public-keys =public-keys-result] -``` - -The [$public-keys-result](/reference/arvo/jael/data-types#public-keys-result) contains whatever changes have occurred. - -Upon subscription, Jael will immeditely respond with a `%public-keys` `gift` containing a `%full` `public-keys-result` with the public key for each `life` up until the current one for each `ship` specified in the original `task`. After than, Jael will send a `%public-keys` `gift` with either a `%diff` or `%breach` `public-keys-result` each time a change occurs for any of the `ship`s to which you're subscribed. +Upon subscription, Jael will immeditely respond with a `%public-keys` +gift containing a `%full` `public-keys-result` with the public key for +each `life` up until the current one for each `ship` specified in the +original task. After than, Jael will send a `%public-keys` gift with +either a `%diff` or `%breach` +[`$public-keys-result`](/reference/arvo/jael/data-types#public-keys-result) +each time a change occurs for any of the `ship`s to which you're +subscribed. #### Example -See the [%public-keys and %nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of the Examples document for a practical example. +See the [%public-keys and +%nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of +the Examples document for a practical example. -## `%rekey` +--- + +### `%rekey` + +Update private keys. ```hoon [%rekey =life =ring] ``` -Update private keys. - -This is what is sent to Jael by `%hood` when you run `|rekey`, as you must after setting new Azimuth keys or running `|cycle-moon-keys` on a moon's parent. It will update your `life` (key revision number) and private keys. The `life` field is the new `life` (typically an increment of the current `life`) and the `ring` is a private key `@`. +This is what is sent to Jael by `%hood` when you run `|rekey`, as you +must after setting new Azimuth keys or running `|cycle-moon-keys` on a +moon's parent. It will update your `life` (key revision number) and +private keys. The `life` field is the new `life` (typically an increment +of the current `life`) and the `ring` is a private key `@`. #### Returns -Jael does not return any `gift` in response to a `%rekey` `task`. +Jael does not return any gift in response to a `%rekey` task. -## `%turf` +--- + +### `%resend` + +Resend private keys. + +```hoon +[%resend ~] +``` + +This task asks Jael to resend our private keys to subscribers who have +subscribed with a [`%private-keys` task](#private-keys). + +#### Returns + +Jael doesn't return any gifts in response to a `%rekey` task, but +`%private-keys` subscribers will receive a [`%private-keys` +gift](#private-keys-1). + +--- + +### `%ruin` + +Pretend breach. + +```hoon +[%ruin ships=(set ship)] +``` + +This simulates a breach locally for the given `set` of `ship`s. Jael +will blast out a `%breach` [`%public-keys` gift](#public-keys-1) to all +subscribers. Ames will delete all message state for the ships in +question in response to the `%breach` gift. + +{% callout %} + +**WARNING** + +This will break communications with the given ships, and is not +reversible until they actually breach. **Use with extreme caution.** + +Note it's better to use the [`%snub` Ames +task](/reference/arvo/ames/tasks#snub) if you want to block packets from +ships. + +{% /callout %} + +#### Returns + +Jael doesn't return any gifts in response to a `%ruin` task. + +--- + +### `%turf` + +View domains. ```hoon [%turf ~] ``` -View domains. - -The domains returned by a `%turf` `task` are used as the base for individual galaxy domain names (e.g. from `urbit.org` you get `zod.urbit.org`, `bus.urbit.org`, etc). Jael gets these from Azimuth, then Ames gets them from Jael and passes them to the runtime, which will perform the DNS lookups and give Ames back the galaxy IP addresses. A `%turf` task takes no additional arguments. You're unlikely to use this manually - if you want the current `turf`s you'd likely want to do a [turf scry](/reference/arvo/jael/scry#turf) instead. +The domains returned by a `%turf` task are used as the base for +individual galaxy domain names (e.g. from `urbit.org` you get +`zod.urbit.org`, `bus.urbit.org`, etc). Jael gets these from Azimuth, +then Ames gets them from Jael and passes them to the runtime, which will +perform the DNS lookups and give Ames back the galaxy IP addresses. A +`%turf` task takes no additional arguments. You're unlikely to use this +manually - if you want the current `turf`s you'd likely want to do a +[turf scry](/reference/arvo/jael/scry#turf) instead. #### Returns -Jael will respond to a `%turf` `task` with a `%turf` `gift`, which looks like: - -```hoon -[%turf turf=(list turf)] -``` - -The `turf` in the `(list turf)` is a domain as a `(list @t)`, TLD-first. The current default is `[['org' 'urbit' ~] ~]`. +Jael will respond to a `%turf` task with a [`%turf` gift](#turf-1). #### Example -See the [%turf section of the Examples document](/reference/arvo/jael/examples#turf) for a practical example. +See the [%turf section of the Examples +document](/reference/arvo/jael/examples#turf) for a practical example. -## `%step` +--- + +### `%step` + +Reset web login code. ```hoon [%step ~] ``` -Reset web login code. - -Jael maintains a `step` value that represents the web login code revision number, and uses it to derive the code itself. It begins at `0` and is incremented each time the code is changed. When Jael updates the web login code, it sends Eyre a `%code-changed` `task:eyre` so that Eyre can throw away all of its current cookies and sessions. A `%step` task takes no additional argument. +Jael maintains a `step` value that represents the web login code +revision number, and uses it to derive the code itself. It begins at `0` +and is incremented each time the code is changed. When Jael updates the +web login code, it sends Eyre a `%code-changed` `task:eyre` so that Eyre +can throw away all of its current cookies and sessions. A `%step` task +takes no additional argument. #### Returns -Jael does not return a `gift` in response to a `%step` `task`. +Jael does not return a gift in response to a `%step` task. #### Example -See the [%step](/reference/arvo/jael/examples#step) section of the Examples document for a practical example. +See the [%step](/reference/arvo/jael/examples#step) section of the +Examples document for a practical example. + +--- + +## Gifts + +### `%done` + +Ames message (n)ack. + +```hoon +[%done error=(unit error:ames)] +``` + +This is given in response to a `%plea` from Ames. You would not use this +from userspace. + +--- + +### `%boon` + +Ames response. + +```hoon +[%boon payload=*] +``` + +This is given in response to a request from Ames. You would not use this +from userspace. + +--- + +### `%private-keys` + +Private keys. + +```hoon +[%private-keys =life vein=(map life ring)] +``` + +This is given to those who have subscribed with a [`%private-keys` +task](#private-keys) whenever our keys change. + +The `life` is our current key revision number, and the `vein` contains a +map from current and previous `life`s to private keys as `ring`s. + +--- + +### `%public-keys` + +Ethereum changes. + +```hoon +[%public-keys =public-keys-result] +``` + +Public key information, diffs, and breach notifications. This is given +to those who have subscribed with a [`%public-keys` task](#public-keys). + +See the +[`$public-keys-result`](/reference/arvo/dill/data-types#public-keys-result) +entry in the data types reference for details of the data this gift +contains. + +--- + +### `%turf` + +Domains. + +```hoon +[%turf turf=(list turf)] +``` + +This is given in response to a [`%turf` task](#turf), and contains the +list of domains used for galaxies (the `urbit.org` part in +`zod.urbit.org`). + +A `$turf` is a `(list @t)` of domain components, TLD-first. For example, +`urbit.org` would be `~['org' 'urbit']`. + +--- diff --git a/content/reference/arvo/khan/khan.md b/content/reference/arvo/khan/khan.md index ce182fa..7375d9d 100644 --- a/content/reference/arvo/khan/khan.md +++ b/content/reference/arvo/khan/khan.md @@ -12,13 +12,15 @@ not yet proper libraries for other languages that can make use of it. Therefore, these documents will only touch on Khan's internal interface. Khan's internal interface lets you run threads via Khan rather than having to -poke [Spider](/reference/arvo/threads/reference) and subscribe for the result. This interface is simpler and more -ergonomic than Spider's, so is usually preferable. +poke [Spider](/reference/arvo/threads/reference) and subscribe for the result. +This interface is simpler and more ergonomic than Spider's, so is usually +preferable. -There are currently three `task`s to run a thread: `%fard`, `%fyrd`, and `%lard`. -Only `%fard` is currently documented in -the [API Reference](/reference/arvo/khan/tasks) section, and a practical example -is given in the [Example](/reference/arvo/khan/example) section. +There are two `task`s for running threads from userspace: +[`%fard`](/reference/arvo/khan/tasks#fard) and +[`%lard`](/reference/arvo/khan/tasks#lard). The former is for running a thread +from file and the latter is for running an "in-line" thread, where you pass +Khan the thread directly. ## Sections diff --git a/content/reference/arvo/khan/tasks.md b/content/reference/arvo/khan/tasks.md index b8a46f8..627379b 100644 --- a/content/reference/arvo/khan/tasks.md +++ b/content/reference/arvo/khan/tasks.md @@ -3,31 +3,101 @@ title = "API Reference" weight = 2 +++ -Khan's external interface is still experimental, so there's only one `task` that -is currently useful: +These are the `task`s Khan can be passed and the `gift`s it can give. -## `%fard` +## Tasks + +Here are the `task`s you can pass Khan. You'd either use +[`%fard`](#fard) to run a thread from a file or [`%lard`](#lard) to run +an in-line thread. + +### `%fard` + +Run a thread from within Arvo. ```hoon [%fard p=(fyrd cage)] ``` -Run a thread from within Arvo - `p` contains the thread location, name, and start arguments. See the [`fyrd`](/reference/arvo/khan/types#fyrd) data type reference entry for details. #### Returns When the thread finishes, either by succeeding or failing, Khan will return an -`%arow` `gift`, which looks like : +[`%arow`](#arow) gift. + +--- + +### `%fyrd` + +External thread. + +```hoon +[%fyrd p=(fyrd cast)] +``` + +This is passed to Khan by the runtime when a thread is run externally. +You would not use this from userspace. + +--- + +### `%lard` + +In-line thread. + +```hoon +[%lard =bear =shed] +``` + +The [`bear`](/reference/arvo/khan/types#bear) is either a `desk` or +`beak`. The [`shed`](/reference/arvo/khan/types#shed) is the thread +itself. Since Spider doesn't need to read out the thread from Clay, the +`bear` doesn't do much apart from be included in the thread name that +Spider generates. Khan will have Spider run the given thread, and +eventually give an [`%arow`](#arow) gift back with the result. + +#### Returns + +When the thread eventually finishes (or if it fails), Khan with give an +[`%arow`](#arow) gift back with the result. + +--- + +## Gifts + +These are the two `gift`s Khan can give. In userspace, you'd only +receive an [`%arow`](#arow). + +### `%arow` + +In-arvo result. ```hoon [%arow p=(avow cage)] ``` -`p` either contains the result in a `cage`, or an error and stack trace if it -failed. See the [`avow`](/reference/arvo/khan/types#avow) data type reference -entry for details. +This gift contains the result of a finished thread if successful, or an +error and stack trace if it failed. It's given for threads run from +within Arvo. See the [`avow`](/reference/arvo/khan/types#avow) entry in +the types reference for more details. + +--- + +### `%avow` + +External result. + +```hoon +[%avow p=(avow page)] +``` + +This gift contains the result of running a thread externally. You would +not receive this in userspace. + + +A `page` is a pair of `mark` and `noun`. See the +[`avow`](/reference/arvo/khan/types#avow) entry in the types reference +for more details of that mold builder. --- diff --git a/content/reference/arvo/overview.md b/content/reference/arvo/overview.md index 6284bd5..f7bc066 100644 --- a/content/reference/arvo/overview.md +++ b/content/reference/arvo/overview.md @@ -276,7 +276,7 @@ every step in the path the request takes onto the chain until we get to the terminal cause of the computation. Then we use this causal stack to route results back to the caller. -The Arvo causal stack is called a `duct`. This is represented simply as a list of paths, where each path represents a step in the causal chain. The first element in the path is the first letter of whichever vane handled that step in the computation, or the empty span for Unix. +The Arvo causal stack is called a `duct`. This is represented simply as a list of paths, where each path represents a step in the causal chain. The first element in the path is the first letter of whichever vane handled that step in the computation, or the empty path element for Unix. Here's a `duct` that was recently observed in the wild upon entering `-time ~s1` into the dojo and pressing Enter, which sets a timer for one second that will @@ -301,7 +301,7 @@ time app. This app returns a `@d` which denotes the current time, which falls do which drops it through to the terminal. Terminal drops this down to Dill, which converts it into an effect that Unix will recognize as a request to print the current time to the screen. When Dill produces this, the last path in the `duct` has an -initial element of the empty span, so this is routed to Unix, which applies the effects. +initial empty element, so this is routed to Unix, which applies the effects. This is a call stack, with a crucial feature: the stack is a first-class citizen. You can respond over a `duct` zero, one, or many times. You can save `duct`s for later use. There are definitely parallels to Scheme-style continuations, but simpler and with more structure. diff --git a/content/reference/arvo/threads/_index.md b/content/reference/arvo/threads/_index.md index 279a185..17f96b6 100644 --- a/content/reference/arvo/threads/_index.md +++ b/content/reference/arvo/threads/_index.md @@ -9,9 +9,17 @@ insert_anchor_links = "right" Overview of threads. -## [Threads Guide](/guides/additional/threads/) +## [HTTP API](/reference/arvo/threads/http-api) -Tutorial and explanation of the basics of writing threads. +Documenation of Spider's HTTP API. + +## [Reference](/reference/arvo/threads/reference) + +Documentation of Spider's API. + +## [Strandio](/reference/arvo/threads/strandio) + +Reference documentation of the Strandio helper library. ## [Gall](/reference/arvo/threads/gall/) diff --git a/content/reference/arvo/threads/http-api.md b/content/reference/arvo/threads/http-api.md index cad33a7..3b670aa 100644 --- a/content/reference/arvo/threads/http-api.md +++ b/content/reference/arvo/threads/http-api.md @@ -15,12 +15,12 @@ The `desk` is the desk in which the thread resides. The `inputMark` is the `mark When Spider receives an HTTP request, the following steps happen: -1. It converts the raw body of the message to `json` using `de-json:html` +1. It converts the raw body of the message to `json` using `de:json:html` 2. It creates a `tube:clay` (`mark` conversion gate) from `json` to whatever input `mark` you've specified and does the conversion. 3. It runs the specified thread and provides a `vase` of `(unit inputMark)` as the argument. 4. The thread does its thing and finally produces its result as a `vase` of `outputMark`. 5. Spider creates another `tube:clay` from the output `mark` to `json` and converts it. -6. It converts the `json` back into raw data suitable for the HTTP response body using `en-json:html`. +6. It converts the `json` back into raw data suitable for the HTTP response body using `en:json:html`. 7. Finally, it composes the HTTP response and passes it back to Eyre which passes it on to the client. Thus, it's important to understand that the original HTTP request and final HTTP response must contain JSON data, and therefore the input & output `mark`s you specify must each have a `mark` file in `/mar` that includes a conversion method for `json -> inputMark` and `outputMark -> json` respectively. diff --git a/content/reference/arvo/threads/overview.md b/content/reference/arvo/threads/overview.md index 823199b..08475fa 100644 --- a/content/reference/arvo/threads/overview.md +++ b/content/reference/arvo/threads/overview.md @@ -79,3 +79,7 @@ These docs walk through the basics of interacting with threads from gall agents. ## [Reference](/reference/arvo/threads/reference) Basic reference information. For usage of particular `strandio` functions just refer directly to `/lib/strandio/hoon` since they're largely self-explanatory. + +## [Strandio](/reference/arvo/threads/strandio) + +Reference documentation of the Strandio helper library. diff --git a/content/reference/arvo/threads/strandio.md b/content/reference/arvo/threads/strandio.md new file mode 100644 index 0000000..f6081bb --- /dev/null +++ b/content/reference/arvo/threads/strandio.md @@ -0,0 +1,2654 @@ ++++ +title = "Strandio" +weight = 60 +template = "doc.html" ++++ + +Documented below are the many useful functions in the +`/lib/strandio.hoon` helper library. + +## Send Cards + +### `send-raw-cards` + +Send a list of `card`s. + +#### Accepts + +A `(list card:agent:gall)`. + +#### Produces + +`~` + +#### Source + +```hoon +++ send-raw-cards + |= cards=(list =card:agent:gall) + =/ m (strand ,~) + ^- form:m + |= strand-input:strand + [cards %done ~] +``` + +#### Example + +```hoon +;~ now=@da bind:m get-time +=/ cards=(list card) + :~ [%pass /foo %agent [~zod %foo] %poke %noun !>(~)] + [%pass /bar %arvo %b %wait now] + == +;< ~ bind:m (send-raw-cards cards) +``` + +--- + +### `send-raw-card` + +Send a single `card`. + +#### Accepts + +A `card:agent:gall` + +#### Produces + +`~` + +#### Source + +```hoon +++ send-raw-card + |= =card:agent:gall + =/ m (strand ,~) + ^- form:m + (send-raw-cards card ~) +``` + +#### Example + +```hoon +=/ card [%pass /foo %agent [~zod %foo] %poke %noun !>(~)] +;< ~ bind:m (send-raw-card card) +``` + +--- + +## Bowl + +### `get-bowl` + +Get the bowl. + +#### Accepts + +Nothing. + +#### Produces + +A `bowl:rand`. + +#### Source + +```hoon +++ get-bowl + =/ m (strand ,bowl:strand) + ^- form:m + |= tin=strand-input:strand + `[%done bowl.tin] +``` + +#### Example + +```hoon +;< =bowl:rand bind:m get-bowl +``` + +--- + +### `get-beak` + +Get the beak. + +#### Accepts + +Nothing. + +#### Produces + +A `beak`. + +#### Source + +```hoon +++ get-beak + =/ m (strand ,beak) + ^- form:m + |= tin=strand-input:strand + `[%done [our q.byk da+now]:bowl.tin] +``` + +#### Example + +```hoon +;< =beak bind:m get-beak +``` + +--- + +### `get-time` + +Get the current date-time. + +#### Accepts + +Nothing. + +#### Produces + +A `@da`. + +#### Source + +```hoon +++ get-time + =/ m (strand ,@da) + ^- form:m + |= tin=strand-input:strand + `[%done now.bowl.tin] +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +``` + +--- + +### `get-our` + +Get our ship. + +#### Accepts + +Nothing. + +#### Produces + +A `@p`. + +#### Source + +```hoon +++ get-our + =/ m (strand ,ship) + ^- form:m + |= tin=strand-input:strand + `[%done our.bowl.tin] +``` + +#### Example + +```hoon +;< our=@p bind:m get-our +``` + +--- + +### `get-entropy` + +Get some entropy. + +#### Accepts + +Nothing. + +#### Produces + +A `@uvJ`. + +#### Source + +```hoon +++ get-entropy + =/ m (strand ,@uvJ) + ^- form:m + |= tin=strand-input:strand + `[%done eny.bowl.tin] +``` + +#### Example + +```hoon +;< eny=@uvJ bind:m get-entropy +``` + +--- + +## Misc + +### `install-domain` + +Install a domain in Eyre, triggering the setup of an SSL certificate. + +#### Accepts + +A `turf`. + +#### Produces + +`~` + +#### Source + +```hoon +++ install-domain + |= =turf + =/ m (strand ,~) + ^- form:m + (send-raw-card %pass / %arvo %e %rule %turf %put turf) +``` + +#### Example + +```hoon +;< ~ bind:m (install-domain 'com' 'example' ~) +``` + +--- + +### `check-online` + +Require that a peer respond before timeout. + +The peer is pinged with a "hi" and must ack the poke before the timeout. + +#### Accepts + +A pair of `[ship @dr]`. The `@dr` is the amount of time the peer has to +respond before failure. + +#### Produces + +`~` + +#### Source + +```hoon +++ check-online + |= [who=ship lag=@dr] + =/ m (strand ,~) + ^- form:m + %+ (map-err ,~) |=(* [%offline *tang]) + %+ (set-timeout ,~) lag + ;< ~ bind:m + (poke [who %hood] %helm-hi !>(~)) + (pure:m ~) +``` + +#### Example + +```hoon +;< ~ bind:m (check-online ~zod ~s10) +``` + +--- + +### `take-sign-arvo` + +Wait for a sign from Arvo. + +#### Accepts + +Nothing. + +#### Produces + +A pair of `[wire sign-arvo]`. + +#### Source + +```hoon +++ take-sign-arvo + =/ m (strand ,[wire sign-arvo]) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ + `[%wait ~] + :: + [~ %sign *] + `[%done [wire sign-arvo]:u.in.tin] + == +``` + +#### Example + +```hoon +;< [=wire =sign-arvo] bind:m take-sign-arvo +``` + +--- + +## Pokes + +### `poke` + +Poke an agent, then await a positive ack. + +#### Accepts + +A pair of `[dock cage]`, where the `dock` is the ship and agent you want +to poke, and the `cage` is the data. + +#### Produces + +`~` + +#### Source + +```hoon +++ poke + |= [=dock =cage] + =/ m (strand ,~) + ^- form:m + =/ =card:agent:gall [%pass /poke %agent dock %poke cage] + ;< ~ bind:m (send-raw-card card) + (take-poke-ack /poke) +``` + +#### Example + +```hoon +;< ~ bind:m (poke [~zod %foo] %noun !>(~)) +``` + +--- + +### `raw-poke` + +Poke an agent then await a (n)ack. + +This doesn't care whether the ack is positive or negative, unlike the +ordinary [poke](#poke). + +#### Accepts + +A pair of `[dock cage]`, where the `dock` is the ship and agent to poke, +and the `cage` is the data. + +#### Produces + +`~` + +#### Source + +```hoon +++ raw-poke + |= [=dock =cage] + =/ m (strand ,~) + ^- form:m + =/ =card:agent:gall [%pass /poke %agent dock %poke cage] + ;< ~ bind:m (send-raw-card card) + =/ m (strand ,~) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ + `[%wait ~] + :: + [~ %agent * %poke-ack *] + ?. =(/poke wire.u.in.tin) + `[%skip ~] + `[%done ~] + == +``` + +#### Example + +```hoon +;< ~ bind:m (raw-poke [~zod %foo] %noun !>(~)) +``` + +--- + +### `raw-poke-our` + +Poke a local agent then await a (n)ack. + +This doesn't care whether the ack is positive or negative, unlike the +ordinary [poke-our](#poke-our). + +#### Accepts + +A pair of `[app=term =cage]`, where `app` is the local agent to poke and +`cage` is the data. + +#### Produces + +`~` + +#### Source + +```hoon +++ raw-poke-our + |= [app=term =cage] + =/ m (strand ,~) + ^- form:m + ;< =bowl:spider bind:m get-bowl + (raw-poke [our.bowl app] cage) +``` + +#### Example + +```hoon +;< ~ bind:m (raw-poke-our %foo %noun !>(~)) +``` + +--- + +### `poke-our` + +Poke a local agent then await an ack. + +Note this fails if it gets a nack back. + +#### Accepts + +A pair of `[=term =cage]` where `term` is the name of a local agent and +`cage` is the data. + +#### Produces + +`~` + +#### Source + +```hoon +++ poke-our + |= [=term =cage] + =/ m (strand ,~) + ^- form:m + ;< our=@p bind:m get-our + (poke [our term] cage) +``` + +#### Example + +```hoon +;< ~ bind:m (poke-our %foo %noun !>(~)) +``` + +--- + +### `take-poke-ack` + +Take a poke ack on the given wire. + +If the ack is a nack, the strand fails. + +#### Accepts + +A `wire`. + +#### Produces + +`~` + +#### Source + +```hoon +++ take-poke-ack + |= =wire + =/ m (strand ,~) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %agent * %poke-ack *] + ?. =(wire wire.u.in.tin) + `[%skip ~] + ?~ p.sign.u.in.tin + `[%done ~] + `[%fail %poke-fail u.p.sign.u.in.tin] + == +``` + +#### Example + +```hoon +;< ~ bind:m (take-poke-ack /foo) +``` + +--- + +### `take-poke` + +Wait for a poke with a particular mark. + +#### Accepts + +A `mark`. + +#### Produces + +A `vase`. + +#### Source + +```hoon +++ take-poke + |= =mark + =/ m (strand ,vase) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ + `[%wait ~] + :: + [~ %poke @ *] + ?. =(mark p.cage.u.in.tin) + `[%skip ~] + `[%done q.cage.u.in.tin] + == +``` + +#### Example + +```hoon +;< =vase bind:m (take-poke %noun) +``` + +--- + +## Subscriptions + +### `watch` + +Watch a subscription path on an agent, then await a positive watch ack. + +Note this fails if it gets a watch nack back. + +#### Accepts + +A triple of `[=wire =dock =path]` where `dock` is the ship and agent, +and `path` is the subscription path. + +#### Produces + +`~` + +#### Source + +```hoon +++ watch + |= [=wire =dock =path] + =/ m (strand ,~) + ^- form:m + =/ =card:agent:gall [%pass watch+wire %agent dock %watch path] + ;< ~ bind:m (send-raw-card card) + (take-watch-ack wire) +``` + +#### Example + +```hoon +;< ~ bind:m (watch /foo [~zod %foo] /some/path) +``` + +--- + +### `watch-one` + +Subscribe to a watch path on an agent, take a single fact, then await a +kick. + +#### Accepts + +A triple of `[=wire =dock =path]` where `dock` is a ship and agent, and +`path` is the subscription path. + +#### Produces + +The `cage` of the received fact. + +#### Source + +```hoon +++ watch-one + |= [=wire =dock =path] + =/ m (strand ,cage) + ^- form:m + ;< ~ bind:m (watch wire dock path) + ;< =cage bind:m (take-fact wire) + ;< ~ bind:m (take-kick wire) + (pure:m cage) +``` + +#### Example + +```hoon +;< [=mark =vase] bind:m (watch-one /foo [~zod %foo] /some/path) +``` + +--- + +### `watch-our` + +Subscribe to a watch path on a local agent, then wait for a positive +ack. + +This will fail if it gets a watch nack. + +#### Accepts + +A triple of `[=wire =term =path]` where `term` is the name of the agent +and `path` is the subscription path. + +#### Produces + +`~` + +#### Source + +```hoon +:: +++ watch-our + |= [=wire =term =path] + =/ m (strand ,~) + ^- form:m + ;< our=@p bind:m get-our + (watch wire [our term] path) +``` + +#### Example + +```hoon +;< ~ bind:m (watch-our /foo %foo /some/path) +``` + +--- + +### `leave` + +Leave a subscription. + +#### Accepts + +A pair of `[=wire =dock]` where `dock` is the ship and agent in question. + +#### Produces + +`~` + +#### Source + +```hoon +++ leave + |= [=wire =dock] + =/ m (strand ,~) + ^- form:m + =/ =card:agent:gall [%pass watch+wire %agent dock %leave ~] + (send-raw-card card) +``` + +#### Example + +```hoon +;< ~ bind:m (leave /foo ~zod %foo) +``` + +--- + +### `leave-our` + +Unsubscribe from a local agent. + +#### Accepts + +A pair of `[=wire =term]` where `term` is the local agent. + +#### Produces + +`~` + +#### Source + +```hoon +++ leave-our + |= [=wire =term] + =/ m (strand ,~) + ^- form:m + ;< our=@p bind:m get-our + (leave wire [our term]) +``` + +#### Example + +```hoon +;< ~ bind:m (leave-our /foo %foo) +``` + +--- + +### `rewatch` + +Resubscribe on kick. + +This waits for a kick on a given wire, then rewatches the given ship, +agent and path on the same wire. It then waits for a positive watch ack. + +#### Accepts + +A triple of `[=wire =dock =path]` where `dock` is the ship and agent, +and `path` is the subscription path. + +#### Produces + +`~` + +#### Source + +```hoon +++ rewatch + |= [=wire =dock =path] + =/ m (strand ,~) + ;< ~ bind:m ((handle ,~) (take-kick wire)) + ;< ~ bind:m (flog-text "rewatching {} {}") + ;< ~ bind:m (watch wire dock path) + (pure:m ~) +``` + +#### Exmaple + +```hoon +;< ~ bind:m (rewatch /foo [~zod %foo] /some/path) +``` + +--- + +### `take-fact-prefix` + +Wait for a subscription update on a wire. + +#### Accepts + +A `wire` as the *prefix* of what you expect. E.g. if `/foo` is given, a +fact with a wire of `/foo`, `/foo/bar`, `/foo/bar/baz`, etc, will be +accepted. + +#### Produces + +A cell of `[wire cage]`. + +#### Source + +```hoon +++ take-fact-prefix + |= =wire + =/ m (strand ,[path cage]) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %agent * %fact *] + ?. =(watch+wire (scag +((lent wire)) wire.u.in.tin)) + `[%skip ~] + `[%done (slag (lent wire) wire.u.in.tin) cage.sign.u.in.tin] + == +``` + +#### Example + +```hoon +;< [=wire =mark =vase] bind:m (take-fact-prefix /foo) +``` + +--- + +### `take-fact` + +Wait for a subscription update on a wire. + +#### Accepts + +The `wire` you want to listen on. + +#### Produces + +A `cage`. + +#### Source + +```hoon +++ take-fact + |= =wire + =/ m (strand ,cage) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %agent * %fact *] + ?. =(watch+wire wire.u.in.tin) + `[%skip ~] + `[%done cage.sign.u.in.tin] + == +``` + +#### Example + +```hoon +;< [=mark =vase] bind:m (take-fact /foo) +``` + +--- + +### `take-kick` + +Wait for a subscription close. + +#### Accepts + +The `wire` you want to listen on. + +#### Produces + +`~` + +#### Source + +```hoon +++ take-kick + |= =wire + =/ m (strand ,~) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %agent * %kick *] + ?. =(watch+wire wire.u.in.tin) + `[%skip ~] + `[%done ~] + == +``` + +#### Example + +```hoon +;< ~ bind:m (take-kick /foo) +``` + +--- + +### `take-watch-ack` + +Take a watch ack on a given wire. + +If the watch ack is a nack, the strand fails. + +#### Accepts + +A `wire`. + +#### Produces + +`~` + +#### Source + +```hoon +++ take-watch-ack + |= =wire + =/ m (strand ,~) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %agent * %watch-ack *] + ?. =(watch+wire wire.u.in.tin) + `[%skip ~] + ?~ p.sign.u.in.tin + `[%done ~] + `[%fail %watch-ack-fail u.p.sign.u.in.tin] + == +``` + +#### Example + +```hoon +;< ~ bind:m (take-watch-ack /foo) +``` + +--- + +### `take-watch` + +Wait for a subscription request. + +#### Accepts + +Nothing. + +#### Produces + +The subscription `path`. + +#### Source + +```hoon +++ take-watch + =/ m (strand ,path) + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %watch *] + `[%done path.u.in.tin] + == +``` + +#### Example + +```hoon +;< =path bind:m take-watch +``` + +--- + +## Scries + +### `scry` + +Scry an agent or vane. + +#### Accepts + +A pair of `[=mold =path]` where `mold` is the type returned and `path` +has the following format: + +```hoon +/[vane letter and care]/[desk]/[rest of path after beak] +``` + +The strand implicitly fills in `our` and `now` in the beak. + +#### Produces + +Data of the type produced by the mold you specified. + +#### Source + +```hoon +++ scry + |* [=mold =path] + =/ m (strand ,mold) + ^- form:m + ?> ?=(^ path) + ?> ?=(^ t.path) + ;< =bowl:spider bind:m get-bowl + %- pure:m + .^(mold i.path (scot %p our.bowl) i.t.path (scot %da now.bowl) t.t.path) +``` + +#### Example + +```hoon +;< has=? bind:m (scry ? %cu %base /gen/vats/hoon) +``` + +--- + +### `keen` + +Make a remote scry request. + +Note this doesn't wait for a response, you'd have to use a separate +[take-tune](#take-tune) strand to receive the result. + +#### Accept + +A pair of `[=wire =spar:ames]`. + +#### Produces + +`~` + +#### Source + +```hoon +++ keen + |= [=wire =spar:ames] + =/ m (strand ,~) + ^- form:m + (send-raw-card %pass wire %arvo %a %keen spar) +``` + +#### Example + +```hoon +;< ~ bind:m (keen /foo ~sampel /c/x/4/base/sys/hoon/hoon) +``` + +--- + +### `take-tune` + +Wait for a remote scry result on a particular wire. + +#### Accepts + +A `wire`. + +#### Produces + +A `[spar:ames (unit roar:ames)]` + +#### Source + +```hoon +++ take-tune + |= =wire + =/ m (strand ,[spar:ames (unit roar:ames)]) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + :: + [~ %sign * %ames %tune ^ *] + ?. =(wire wire.u.in.tin) + `[%skip ~] + `[%done +>.sign-arvo.u.in.tin] + == +``` + +#### Example + +```hoon +;< [spar roar=(unit roar)] bind:m (take-tune /foo) +``` + +--- + +## Time + +### `wait` + +Send a `%wait` to Behn and wait for the `%wake`. + +Note there's also [sleep](#sleep) to wait for a relative amount of time +rather than having to specify an absolute time. + +#### Accepts + +A `@da` of when the timer should fire. + +#### Produces + +`~` + +#### Source + +```hoon +++ wait + |= until=@da + =/ m (strand ,~) + ^- form:m + ;< ~ bind:m (send-wait until) + (take-wake `until) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< ~ bind:m (wait (add now ~s2)) +``` + +--- + +### `sleep` + +Wait for a relative amount of time. + +#### Accepts + +A `@dr`. + +#### Produces + +`~` + +#### Source + +```hoon +++ sleep + |= for=@dr + =/ m (strand ,~) + ^- form:m + ;< now=@da bind:m get-time + (wait (add now for)) +``` + +#### Example + +```hoon +;< ~ bind:m (sleep ~s2) +``` + +--- + +### `send-wait` + +Send Behn a `%wait` but don't wait for the `%wake`. + +#### Accepts + +A `@da`. + +#### Produces + +`~` + +#### Source + +```hoon +++ send-wait + |= until=@da + =/ m (strand ,~) + ^- form:m + =/ =card:agent:gall + [%pass /wait/(scot %da until) %arvo %b %wait until] + (send-raw-card card) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< ~ bind:m (send-wait (add ~s2 now)) +``` + +--- + +### `set-timeout` + +Make a strand fail if it takes too long. + +#### Accepts + +This takes the `mold` produced but the strand you're timing, and +produces a gate. The gate takes a pair of the `@dr` timeout and the +strand being timed. + +#### Produces + +Data of the type produced by the strand being timed. + +#### Source + +```hoon +++ set-timeout + |* computation-result=mold + =/ m (strand ,computation-result) + |= [time=@dr computation=form:m] + ^- form:m + ;< now=@da bind:m get-time + =/ when (add now time) + =/ =card:agent:gall + [%pass /timeout/(scot %da when) %arvo %b %wait when] + ;< ~ bind:m (send-raw-card card) + |= tin=strand-input:strand + =* loop $ + ?: ?& ?=([~ %sign [%timeout @ ~] %behn %wake *] in.tin) + =((scot %da when) i.t.wire.u.in.tin) + == + `[%fail %timeout ~] + =/ c-res (computation tin) + ?: ?=(%cont -.next.c-res) + c-res(self.next ..loop(computation self.next.c-res)) + ?: ?=(%done -.next.c-res) + =/ =card:agent:gall + [%pass /timeout/(scot %da when) %arvo %b %rest when] + c-res(cards [card cards.c-res]) + c-res +``` + +#### Example + +```hoon +;< ~ bind:m ((set-timeout ,~) ~s10 (poke-our %foo %noun !>(~))) +``` + +--- + +### `take-wake` + +Wait for a wake from Behn. + +This is meant for internal use by [wait](#wait), you'd not typically +use it directly. + +#### Accepts + +A `(unit @da)`. If the unit is non-null, it'll only accept a `%wake` +whose wire is of the form `/wait/(scot %da the-given-time)`. If the unit +is null, it'll accept a `%wake` with a wire of `/wait/(scot %da +any-time)`. + +#### Produces + +`~` + +#### Source + +```hoon +:: +++ take-wake + |= until=(unit @da) + =/ m (strand ,~) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %sign [%wait @ ~] %behn %wake *] + ?. |(?=(~ until) =(`u.until (slaw %da i.t.wire.u.in.tin))) + `[%skip ~] + ?~ error.sign-arvo.u.in.tin + `[%done ~] + `[%fail %timer-error u.error.sign-arvo.u.in.tin] + == +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +=/ card=card:agent:gall [%pass /wait/(scot %da now) %arvo %b %wait now] +;< ~ bind:m (send-raw-card card) +;< ~ bind:m (take-wake `now) +``` + +--- + +## Errors + +### `retry` + +Retry a strand that produces a `unit` if the `unit` is null, with a backoff. + +#### Accepts + +`retry` first takes a `result=mold` of the return type and produces a gate. +That gate takes two arguments: + +- `crash-after=(unit @ud)`: the number of tries before failing. +- `computation`: A strand that produces a `(unit result)`. + +#### Produces + +The type of `result`. + +#### Source + +```hoon +++ retry + |* result=mold + |= [crash-after=(unit @ud) computation=_*form:(strand (unit result))] + =/ m (strand ,result) + =| try=@ud + |- ^- form:m + =* loop $ + ?: =(crash-after `try) + (strand-fail %retry-too-many ~) + ;< ~ bind:m (backoff try ~m1) + ;< res=(unit result) bind:m computation + ?^ res + (pure:m u.res) + loop(try +(try)) +``` + +#### Example + +```hoon +=/ =hiss:eyre [(need (de-purl:html 'http://example.com')) %get ~ ~] +;< =httr:eyre bind:m ((retry httr:eyre) `3 (hiss-request hiss)) +``` +--- + +### `backoff` + +Wait for increasing amounts of time with each try. + +#### Accepts + +A pair of `[try=@ud limit=@dr]`, specifying the current try count and +the maximum amount of time to wait. + +#### Produces + +`~` + +#### Source + +```hoon +++ backoff + |= [try=@ud limit=@dr] + =/ m (strand ,~) + ^- form:m + ;< eny=@uvJ bind:m get-entropy + %- sleep + %+ min limit + ?: =(0 try) ~s0 + %+ add + (mul ~s1 (bex (dec try))) + (mul ~s0..0001 (~(rad og eny) 1.000)) +``` + +--- + +### `map-err` + +Rewrite a strand failure error. + +#### Accepts + +This function takes the return `mold` of the strand in question as its +argument and returns a gate that takes two arguments: + +- `f`: a gate that takes a `[term tang]` and produces a `[term tang]`. + This is the `%error-tag` and stack trace of the failure you're + rewriting. +- `computation`: the strand whose errors you're rewriting. + +See the example below for usage. + +#### Produces + +Data of the type produced by the strand in question. + +#### Source + +```hoon +++ map-err + |* computation-result=mold + =/ m (strand ,computation-result) + |= [f=$-([term tang] [term tang]) computation=form:m] + ^- form:m + |= tin=strand-input:strand + =* loop $ + =/ c-res (computation tin) + ?: ?=(%cont -.next.c-res) + c-res(self.next ..loop(computation self.next.c-res)) + ?. ?=(%fail -.next.c-res) + c-res + c-res(err.next (f err.next.c-res)) +``` + +#### Example + +```hoon +;< ~ bind:m + %+ (map-err ,~) + |= [=term =tang] + ?: =(%poke-fail term) + [%foo tang] + [term tang] + (poke-our %foo %noun !>(~)) +``` + +--- + +## HTTP + +### `send-request` + +Make an HTTP request via Iris, but don't wait for the response. + +#### Accepts + +A [`request:http`](/reference/arvo/eyre/data-types#requesthttp). + +#### Produces + +`~` + +#### Source + +```hoon +++ send-request + |= =request:http + =/ m (strand ,~) + ^- form:m + (send-raw-card %pass /request %arvo %i %request request *outbound-config:iris) +``` + +#### Example + +```hoon +;< ~ bind:m (send-request %'GET' 'http://example.com' ~ ~) +``` + +--- + +### `send-cancel-request` + +Cancel a previous Iris HTTP request. + +This sends it on the `/request` wire used by +[`send-request`](#send-request). It won't work if the original request +was on a different wire. + +#### Accepts + +Nothing. + +#### Produces + +`~` + +#### Source + +```hoon +++ send-cancel-request + =/ m (strand ,~) + ^- form:m + (send-raw-card %pass /request %arvo %i %cancel-request ~) +``` + +#### Example + +```hoon +;< ~ bind:m send-cancel-request +``` + +--- + +### `take-client-response` + +Take the HTTP response from a previous HTTP request made with +[`send-request`](#send-request). + +This listens on the `/request` wire, it won't work if you're made a +request on a different wire. + +#### Accepts + +Nothing. + +#### Produces + +A [`client-response:iris`](/reference/arvo/iris/data-types#client-response). + +#### Source + +```hoon +++ take-client-response + =/ m (strand ,client-response:iris) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + :: + [~ %sign [%request ~] %iris %http-response %cancel *] + ::NOTE iris does not (yet?) retry after cancel, so it means failure + :- ~ + :+ %fail + %http-request-cancelled + ['http request was cancelled by the runtime']~ + :: + [~ %sign [%request ~] %iris %http-response %finished *] + `[%done client-response.sign-arvo.u.in.tin] + == +``` + +#### Example + +```hoon +;< res=client-response:iris bind:m take-client-response +``` + +--- + +### `take-maybe-sigh` + +Take a unitized raw HTTP response. + +#### Accepts + +Nothing + +#### Produces + +A `(unit httr:eyre)`. The `unit` is null if we failed to receive a +response. + +#### Source + +```hoon +++ take-maybe-sigh + =/ m (strand ,(unit httr:eyre)) + ^- form:m + ;< rep=(unit client-response:iris) bind:m + take-maybe-response + ?~ rep + (pure:m ~) + :: XX s/b impossible + :: + ?. ?=(%finished -.u.rep) + (pure:m ~) + (pure:m (some (to-httr:iris +.u.rep))) +``` + +#### Example + +```hoon +;< res=(unit httr:eyre) bind:m take-maybe-sigh +``` + +--- + +### `take-maybe-response` + +Take a unitized HTTP response. + +#### Accepts + +Nothing + +#### Produces + +A `(unit client-response:iris)`. The `unit` is null if we failed to +receive a response. + +#### Source + +```hoon +++ take-maybe-response + =/ m (strand ,(unit client-response:iris)) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %sign [%request ~] %iris %http-response %cancel *] + `[%done ~] + [~ %sign [%request ~] %iris %http-response %finished *] + `[%done `client-response.sign-arvo.u.in.tin] + == +``` + +#### Example + +```hoon +;< res=(unit client-response:iris) bind:m take-maybe-response +``` + +--- + +### `extract-body` + +Extract body from an HTTP response. + +#### Accepts + +A `client-response:iris` + +#### Produces + +A `cord`. + +#### Source + +```hoon +++ extract-body + |= =client-response:iris + =/ m (strand ,cord) + ^- form:m + ?> ?=(%finished -.client-response) + %- pure:m + ?~ full-file.client-response '' + q.data.u.full-file.client-response +``` + +--- + +### `fetch-cord` + +Get the HTTP response body from a URL. + +#### Accepts + +The URL in a `tape`. + +#### Produces + +A `cord` of the response body. + +#### Source + +```hoon +++ fetch-cord + |= url=tape + =/ m (strand ,cord) + ^- form:m + =/ =request:http [%'GET' (crip url) ~ ~] + ;< ~ bind:m (send-request request) + ;< =client-response:iris bind:m take-client-response + (extract-body client-response) +``` + +#### Example + +```hoon +;< bod=@t bind:m (fetch-cord "http://example.com") +``` + +--- + +### `fetch-json` + +Get some JSON from a URL. + +#### Accepts + +The URL as a `tape`. + +#### Produces + +A `json` structure. + +#### Source + +```hoon +++ fetch-json + |= url=tape + =/ m (strand ,json) + ^- form:m + ;< =cord bind:m (fetch-cord url) + =/ json=(unit json) (de-json:html cord) + ?~ json + (strand-fail %json-parse-error ~) + (pure:m u.json) +``` + +#### Example + +```hoon +;< =json bind:m (fetch-json "http://example.com") +``` + +--- + +### `hiss-request` + +Make a raw HTTP request, take a raw response. + +#### Accepts + +`hiss:eyre` + +#### Produces + +A `(unit httr:eyre)`. The `unit` is null if we failed to receive a +response. + +#### Source + +```hoon +:: +++ hiss-request + |= =hiss:eyre + =/ m (strand ,(unit httr:eyre)) + ^- form:m + ;< ~ bind:m (send-request (hiss-to-request:html hiss)) + take-maybe-sigh +``` + +#### Example + +```hoon +=/ =hiss:eyre [(need (de-purl:html 'http://example.com')) %get ~ ~] +;< res=(unit httr:eyre) bind:m (hiss-request hiss) +``` + +--- + +## Build + +### `build-file` + +Build a source file at the specified `beam`. + +#### Accepts + +A `beam`. + +#### Produces + +A `(unit vase)`. The `vase` contains the compiled file, the `unit` is +null if it failed. + +#### Source + +```hoon +++ build-file + |= [[=ship =desk =case] =spur] + =* arg +< + =/ m (strand ,(unit vase)) + ^- form:m + ;< =riot:clay bind:m + (warp ship desk ~ %sing %a case spur) + ?~ riot + (pure:m ~) + ?> =(%vase p.r.u.riot) + (pure:m (some !<(vase q.r.u.riot))) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< res=(unit vase) bind:m (build-file [~zod %base da+now] /gen/hood/hi/hoon) +``` + +--- + +### `build-file-hard` + +Build a source file at the specified `beam`, crashing if it fails. + +#### Accepts + +A `beam`. + +#### Produces + +A `vase`. + +#### Source + +```hoon +++ build-file-hard + |= [[=ship =desk =case] =spur] + =* arg +< + =/ m (strand ,vase) + ^- form:m + ;< =riot:clay + bind:m + (warp ship desk ~ %sing %a case spur) + ?> ?=(^ riot) + ?> ?=(%vase p.r.u.riot) + (pure:m !<(vase q.r.u.riot)) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =vase bind:m (build-file-hard [~zod %base da+now] /gen/hood/hi/hoon) +``` + +--- + +### `build-mark` + +Build a dynamic mark core from file. + +#### Accepts + +A pair of `[beak mark]`. + +#### Produces + +A `dais:clay` + +#### Source + +```hoon +++ build-mark + |= [[=ship =desk =case] mak=mark] + =* arg +< + =/ m (strand ,dais:clay) + ^- form:m + ;< =riot:clay bind:m + (warp ship desk ~ %sing %b case /[mak]) + ?~ riot + (strand-fail %build-mark >arg< ~) + ?> =(%dais p.r.u.riot) + (pure:m !<(dais:clay q.r.u.riot)) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =dais:clay bind:m (build-mark [~zod %base da+now] %noun) +``` + +--- + +### `build-tube` + +Build a dynamic mark conversion gate from file. + +#### Accepts + +A pair of `[beak mars:clay]`. A `mars` is a pair of the *from* and *to* +mark. + +#### Produces + +A `tube:clay` + +#### Source + +```hoon +++ build-tube + |= [[=ship =desk =case] =mars:clay] + =* arg +< + =/ m (strand ,tube:clay) + ^- form:m + ;< =riot:clay bind:m + (warp ship desk ~ %sing %c case /[a.mars]/[b.mars]) + ?~ riot + (strand-fail %build-tube >arg< ~) + ?> =(%tube p.r.u.riot) + (pure:m !<(tube:clay q.r.u.riot)) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =tube:clay bind:m (build-tube [~zod %base da+now] %mime %txt) +``` + +--- + +### `build-nave` + +Build a static mark core from file. + +#### Accepts + +A pair of `[beak mark]`. + +#### Produces + +A `vase`. + +#### Source + +```hoon +++ build-nave + |= [[=ship =desk =case] mak=mark] + =* arg +< + =/ m (strand ,vase) + ^- form:m + ;< =riot:clay bind:m + (warp ship desk ~ %sing %e case /[mak]) + ?~ riot + (strand-fail %build-nave >arg< ~) + ?> =(%nave p.r.u.riot) + (pure:m q.r.u.riot) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =nave:clay bind:m (build-nave [~zod %base da+now] %txt) +``` + +--- + +### `build-cast` + +Build a static mark conversion gate from file. + +#### Accepts + +A pair of `[beak mars:clay]`. A `mars` is a pair of the *from* mark and +*to* mark. + +#### Source + +```hoon +++ build-cast + |= [[=ship =desk =case] =mars:clay] + =* arg +< + =/ m (strand ,vase) + ^- form:m + ;< =riot:clay bind:m + (warp ship desk ~ %sing %f case /[a.mars]/[b.mars]) + ?~ riot + (strand-fail %build-cast >arg< ~) + ?> =(%cast p.r.u.riot) + (pure:m q.r.u.riot) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =vase bind:m (build-cast [~zod %base da+now] %mime %txt) +``` + +--- + +### `eval-hoon` + +Evaluate some hoon and produce the result. + +#### Accepts + +A pair of `[gen=hoon bez=(list beam)]`. The `gen` argument is the hoon +to be evaluated. If `bez` is empty, it will be evaluated against the +standard `..zuse` subject. If a list of `beam`s are provided in `bez`, +each one will be read from Clay, build, and pinned to the head of the +subject, before `gen` is evaluated against it. + +#### Produces + +A `vase` of the result. + +#### Source + +```hoon +++ eval-hoon + |= [gen=hoon bez=(list beam)] + =/ m (strand ,vase) + ^- form:m + =/ sut=vase !>(..zuse) + |- + ?~ bez + (pure:m (slap sut gen)) + ;< vax=vase bind:m (build-file-hard i.bez) + $(bez t.bez, sut (slop vax sut)) +``` + +#### Example + +```hoon +;< =vase bind:m (eval-hoon !,(*hoon (add 1 1))) +``` + +--- + +## Clay + +### `warp` + +Raw read from Clay. + +#### Accepts + +A pair of `ship` and [`riff:clay`](/reference/arvo/clay/data-types#riff). + +#### Produces + +A [`riot:clay`](/reference/arvo/clay/data-types#riot). + +#### Source + +```hoon +++ warp + |= [=ship =riff:clay] + =/ m (strand ,riot:clay) + ;< ~ bind:m (send-raw-card %pass /warp %arvo %c %warp ship riff) + (take-writ /warp) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =riot:clay bind:m (warp %base ~ %sing %x da+now /foo/txt) +``` + +--- + +### `read-file` + +Read a file from Clay. + +#### Accepts + +A `beam`. + +#### Produces + +A `cage`. + +#### Source + +```hoon +++ read-file + |= [[=ship =desk =case] =spur] + =* arg +< + =/ m (strand ,cage) + ;< =riot:clay bind:m (warp ship desk ~ %sing %x case spur) + ?~ riot + (strand-fail %read-file >arg< ~) + (pure:m r.u.riot) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< =cage bind:m (read-file [~zod %base da+now] /foo/txt) +``` + +--- + +### `check-for-file` + +Check for the existence of a file in Clay. + +#### Accepts + +A `beam`. + +#### Produces + +A `?` which is `%.y` if the file exists, and `%.n` if not. + +#### Source + +```hoon +++ check-for-file + |= [[=ship =desk =case] =spur] + =/ m (strand ,?) + ;< =riot:clay bind:m (warp ship desk ~ %sing %x case spur) + (pure:m ?=(^ riot)) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< has=? bind:m (check-for-file [~zod %base da+now] /foo/txt) +``` + +--- + +### `list-tree` + +Get a list of all files in the given Clay directory. + +#### Accepts + +A `beam`. + +#### Produces + +A `(list path)`. + +#### Source + +```hoon +++ list-tree + |= [[=ship =desk =case] =spur] + =* arg +< + =/ m (strand ,(list path)) + ;< =riot:clay bind:m (warp ship desk ~ %sing %t case spur) + ?~ riot + (strand-fail %list-tree >arg< ~) + (pure:m !<((list path) q.r.u.riot)) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< paths=(list path) bind:m (list-tree [~zod %base da+now] /sys) +``` + +--- + +### `take-writ` + +Take a Clay read result. + +#### Accepts + +The `wire` to listen on. + +#### Produces + +A [`riot:clay`](/reference/arvo/clay/data-types#riot) + +#### Source + +```hoon +++ take-writ + |= =wire + =/ m (strand ,riot:clay) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ `[%wait ~] + [~ %sign * ?(%behn %clay) %writ *] + ?. =(wire wire.u.in.tin) + `[%skip ~] + `[%done +>.sign-arvo.u.in.tin] + == +``` + +#### Example + +```hoon +;< =riot-clay bind:m (take-writ /warp) +``` + +--- + +## Main Loop + +### `ignore` + +Try next on failure. + +This produces a failure with an `%ignore` status, which +[main-loop](#main-loop) uses to skip the strand and try the next one. +This is of little use outside the context of a `main-loop`. + +#### Accepts + +Nothing. + +#### Produces + +Nothing. + +#### Source + +```hoon +++ ignore + |= tin=strand-input:strand + `[%fail %ignore ~] +``` + +--- + +### `handle` + +Convert skips to `%ignore` failures. + +This tells [main-loop](#main-loop) to try the next strand on skips. +This would not be used outside of a `main-loop`. + +#### Accepts + +`+handle` takes a mold and produces a gate that takes another strand. + +#### Produces + +Data of the type produced by the given mold. + +#### Source + +```hoon +++ handle + |* a=mold + =/ m (strand ,a) + |= =form:m + ^- form:m + |= tin=strand-input:strand + =/ res (form tin) + =? next.res ?=(%skip -.next.res) + [%fail %ignore ~] + res +``` + +#### Example + +```hoon +;< =vase bind:m ((handle ,vase) (take-poke %foo)) +``` + +--- + +### `main-loop` + +A `main-loop` can be used for three things: + +1. create a loop. +2. try the same input against multiple strands. +3. Queue input on `%skip` and then dequeue from the beginning on `%done`. + +#### Accepts + +It first accepts a `mold`, specifying the return type, and produces a +gate. The gate produced takes a `list` of gates that take an argument of +the specified `mold`, and produce the `form` of a `strand` of that mold. + +#### Produces + +Data of the type produced by the given `mold`. + +#### Source + +```hoon {% mode="collapse" %} +++ main-loop + |* a=mold + =/ m (strand ,~) + =/ m-a (strand ,a) + =| queue=(qeu (unit input:strand)) + =| active=(unit [in=(unit input:strand) =form:m-a forms=(list $-(a form:m-a))]) + =| state=a + |= forms=(lest $-(a form:m-a)) + ^- form:m + |= tin=strand-input:strand + =* top `form:m`..$ + =. queue (~(put to queue) in.tin) + |^ (continue bowl.tin) + :: + ++ continue + |= =bowl:strand + ^- output:m + ?> =(~ active) + ?: =(~ queue) + `[%cont top] + =^ in=(unit input:strand) queue ~(get to queue) + ^- output:m + =. active `[in (i.forms state) t.forms] + ^- output:m + (run bowl in) + :: + ++ run + ^- form:m + |= tin=strand-input:strand + ^- output:m + ?> ?=(^ active) + =/ res (form.u.active tin) + =/ =output:m + ?- -.next.res + %wait `[%wait ~] + %skip `[%cont ..$(queue (~(put to queue) in.tin))] + %cont `[%cont ..$(active `[in.u.active self.next.res forms.u.active])] + %done (continue(active ~, state value.next.res) bowl.tin) + %fail + ?: &(?=(^ forms.u.active) ?=(%ignore p.err.next.res)) + %= $ + active `[in.u.active (i.forms.u.active state) t.forms.u.active] + in.tin in.u.active + == + `[%fail err.next.res] + == + [(weld cards.res cards.output) next.output] + -- +``` +#### Example + +See the [separate `main-loop` +example](/reference/arvo/threads/examples/main-loop) or the +[`echo`](#echo) example below. + +--- + +### `echo` + +Echo a given message to the terminal every 2 seconds until told to stop. + +#### Accepts + +This strand takes nothing directly, but expects a poke with a `mark` of +`%echo` and vase containing a `tape` with the message to echo. To +finish, it expects a poke with a `mark` of `%over`. + +#### Produces + +`~` + +#### Source + +```hoon +++ echo + =/ m (strand ,~) + ^- form:m + %- (main-loop ,~) + :~ |= ~ + ^- form:m + ;< =vase bind:m ((handle ,vase) (take-poke %echo)) + =/ message=tape !<(tape vase) + %- (slog leaf+"{message}..." ~) + ;< ~ bind:m (sleep ~s2) + %- (slog leaf+"{message}.." ~) + (pure:m ~) + :: + |= ~ + ^- form:m + ;< =vase bind:m ((handle ,vase) (take-poke %over)) + %- (slog leaf+"over..." ~) + (pure:m ~) + == +``` + +--- + +## Printing + +### `flog` + +Send a wrapped Dill task to Dill. + +#### Accepts + +A [`flog:dill`](/reference/arvo/dill/data-types#flog) + +#### Produces + +`~` + +#### Source + +```hoon +++ flog + |= =flog:dill + =/ m (strand ,~) + ^- form:m + (send-raw-card %pass / %arvo %d %flog flog) +``` + +#### Example + +```hoon +;< ~ bind:m (flog %text "foo") +``` + +--- + +### `flog-text` + +Print a message to the terminal via Dill. + +#### Accepts + +A `tape`. + +#### Produces + +`~` + +#### Source + +```hoon +++ flog-text + |= =tape + =/ m (strand ,~) + ^- form:m + (flog %text tape) +``` + +#### Example + +```hoon +;< ~ bind:m (flog-text "foo") +``` + +--- + +### `flog-tang` + +Print a `tang` to the terminal via Dill. + +#### Accepts + +A `tang` + +#### Produces + +`~` + +#### Source + +```hoon +++ flog-tang + |= =tang + =/ m (strand ,~) + ^- form:m + =/ =wall + (zing (turn (flop tang) (cury wash [0 80]))) + |- ^- form:m + =* loop $ + ?~ wall + (pure:m ~) + ;< ~ bind:m (flog-text i.wall) + loop(wall t.wall) +``` + +#### Example + +```hoon +;< ~ bind:m (flog-tang 'foo' 'bar' 'baz' ~) +``` + +--- + +### `trace` + +Slog a `tang` to the terminal. + +#### Accepts + +A `tang`. + +#### Produces + +`~` + +#### Source + +```hoon +++ trace + |= =tang + =/ m (strand ,~) + ^- form:m + (pure:m ((slog tang) ~)) +``` + +#### Example + +```hoon +;< ~ bind:m (trace 'foo' 'bar' 'baz' ~) +``` + +--- + +### `app-message` + +Print a message to the terminal tagged with an app name, like: + +``` +my-app: foo bar baz +``` + +Then, optionally, print a `tang`. + +#### Accepts + +A triple of `[term cord tang]`. The `term` is the app name, the `cord` +is the message, and the `tang` is any traceback. + +#### Produces + +`~` + +#### Source + +```hoon +++ app-message + |= [app=term =cord =tang] + =/ m (strand ,~) + ^- form:m + =/ msg=tape :(weld (trip app) ": " (trip cord)) + ;< ~ bind:m (flog-text msg) + (flog-tang tang) +``` + +#### Example + +```hoon +;< ~ bind:m (app-message %foo 'foo bar baz' ~) +``` + +--- + +## Threads + +### `send-thread` + +Run an inline thread via Khan. + +#### Accepts + +A triple of: + +- `bear:khan`: desk or beak. +- `shed:khan`: the thread itself. +- `wire`: the wire for responses from Khan. + +#### Produces + +`~` + +#### Source + +```hoon +++ send-thread + |= [=bear:khan =shed:khan =wire] + =/ m (strand ,~) + ^- form:m + (send-raw-card %pass wire %arvo %k %lard bear shed) +``` + +--- + +### `start-thread` + +Start a child thread. + +#### Accepts + +A `term`, the name of a thread in `/ted` of this desk. + +#### Produces + +A `tid:spider`, the ID of the child thread. + +#### Source + +```hoon +++ start-thread + |= file=term + =/ m (strand ,tid:spider) + ;< =bowl:spider bind:m get-bowl + (start-thread-with-args byk.bowl file *vase) +``` + +#### Example + +```hoon +;< ~ bind:m (start-thread %foo) +``` + +--- + +### `start-thread-with-args` + +Start a child thread with arguments. + +#### Accepts + +A triple of: + +- `beak`: the ship/desk/case where the thread is located. +- `term`: the name of the thread in `/ted` of the given desk. +- `vase`: the start argument. + +#### Produces + +A `tid:spider`, the ID of the child thread. + +#### Source + +```hoon +++ start-thread-with-args + |= [=beak file=term args=vase] + =/ m (strand ,tid:spider) + ^- form:m + ;< =bowl:spider bind:m get-bowl + =/ tid + (scot %ta (cat 3 (cat 3 'strand_' file) (scot %uv (sham file eny.bowl)))) + =/ poke-vase !>(`start-args:spider`[`tid.bowl `tid beak file args]) + ;< ~ bind:m (poke-our %spider %spider-start poke-vase) + ;< ~ bind:m (sleep ~s0) :: wait for thread to start + (pure:m tid) +``` + +#### Example + +```hoon +;< now=@da bind:m get-time +;< ~ bind:m (start-thread-with-args [~zod %base da+now] %foo !>(~)) +``` + +--- + +### `thread-result` + +Type definition of a thread result. + +#### Source + +```hoon ++$ thread-result + (each vase [term tang]) +``` + +--- + +### `await-thread` + +Start a thread with an argument, then await its result. + +#### Accepts + +A pair of `[term vase]` where `term` is the name of a thread in `/ted` +of this desk, and `vase` contains the start argument. + +#### Produces + +A [`thread-result`](#thread-result) + +#### Source + +```hoon +++ await-thread + |= [file=term args=vase] + =/ m (strand ,thread-result) + ^- form:m + ;< =bowl:spider bind:m get-bowl + =/ tid (scot %ta (cat 3 'strand_' (scot %uv (sham file eny.bowl)))) + =/ poke-vase !>(`start-args:spider`[`tid.bowl `tid byk.bowl file args]) + ;< ~ bind:m (watch-our /awaiting/[tid] %spider /thread-result/[tid]) + ;< ~ bind:m (poke-our %spider %spider-start poke-vase) + ;< ~ bind:m (sleep ~s0) :: wait for thread to start + ;< =cage bind:m (take-fact /awaiting/[tid]) + ;< ~ bind:m (take-kick /awaiting/[tid]) + ?+ p.cage ~|([%strange-thread-result p.cage file tid] !!) + %thread-done (pure:m %& q.cage) + %thread-fail (pure:m %| ;;([term tang] q.q.cage)) + == +``` + +#### Example + +```hoon +;< =thread-result bind:m (await-thread %foo !>(~)) +``` + +--- diff --git a/content/reference/arvo/tutorials/move-trace.md b/content/reference/arvo/tutorials/move-trace.md index b2f4124..fd01e17 100644 --- a/content/reference/arvo/tutorials/move-trace.md +++ b/content/reference/arvo/tutorials/move-trace.md @@ -127,10 +127,11 @@ Here is the line of code in `arvo.hoon`, found in the [section First we note that this line is executed only if the laconic bit is set to true, as we did when we input `|verb`. Here, `ovo` is the input `ovum`. Knowing that an `ovum` is a `[p=wire q=curd]`, we can then say that this is a `%unix` `move` tagged with `%belt` whose cause is a `wire` given by `//term/1`, -where the empty span `//` represents Unix and `term/1` represents the terminal -in Unix. Here we have a `wire` instead of a `duct` (i.e. a list of `wire`s) -since Unix I/O events are always the beginning and end of the Arvo event loop, -thus only a single `wire` is ever required at this initial stage. +where the empty path element `//` represents Unix and `term/1` +represents the terminal in Unix. Here we have a `wire` instead of a +`duct` (i.e. a list of `wire`s) since Unix I/O events are always the +beginning and end of the Arvo event loop, thus only a single `wire` is +ever required at this initial stage. The `""` here is a metadatum that keeps track of how many steps deep in the causal chain the event is. An event diff --git a/content/reference/azimuth/ecliptic.md b/content/reference/azimuth/ecliptic.md index 24a7dd1..101d529 100644 --- a/content/reference/azimuth/ecliptic.md +++ b/content/reference/azimuth/ecliptic.md @@ -100,7 +100,7 @@ Corresponds to the layer 2 `%spawn` action. ``` Transfer `_point` to `_target`, clearing all permissions data and keys if -`_reset` is true. `_reset` set to makes this transaction a +`_reset` is true. `_reset` set to true makes this transaction a [breach](https://urbit.org/using/id/guide-to-resets), and thus this action increments the [`continuityNumber`](/reference/azimuth/azimuth-eth#points) of `_point`, and usually the `keyRevisionNumber` as well (see [Life and diff --git a/content/reference/azimuth/flow.md b/content/reference/azimuth/flow.md index a447123..1fe6f1a 100644 --- a/content/reference/azimuth/flow.md +++ b/content/reference/azimuth/flow.md @@ -35,9 +35,9 @@ The Gall agents involved with Azimuth are summarized as follows: The transaction processing library is [`/lib/naive.hoon`](#naive). -### Gall agents +## Gall agents -#### `%azimuth` {% #azimuth %} +### `%azimuth` {% #azimuth %} `%azimuth`, located at `/app/azimuth.hoon`, is a Gall agent and thread handler responsible for finding Azimuth transactions gathered by `%eth-watcher`, @@ -79,12 +79,12 @@ Scries can be inferred from the `+on-peek` arm: == ``` -#### `%azimuth-rpc` {% #azimuth-rpc %} +### `%azimuth-rpc` {% #azimuth-rpc %} `%azimuth-rpc`, located at `app/azimuth-rpc.hoon`, is a JSON RPC-API for getting `point` and `dns` data from the Azimuth PKI state kept by `%azimuth`. -#### `%eth-watcher` {% #eth-watcher %} +### `%eth-watcher` {% #eth-watcher %} `%eth-watcher`, located at `/app/eth-watcher.hoon`, is responsible for listening to an Ethereum node and collecting event logs from it. It is general-purpose and @@ -94,7 +94,7 @@ not particular to Azimuth. It sends collected transactions to `+on-agent` in [![Eth-watcher](https://media.urbit.org/docs/layer2/roller-agents.png)](https://media.urbit.org/docs/layer2/roller-agents.png) -#### `%roller` {% #roller %} +### `%roller` {% #roller %} `%roller`, stored at `/app/roller.hoon`, is a Gall agent responsible for collecting and submitting batches of layer 2 transactions to the Ethereum @@ -142,7 +142,7 @@ This app is not responsible for communicating with Bridge via HTTP. Instead, tha handled by `%roller-rpc`. The scries are also communicated to Bridge via `%roller-rpc`. -#### `%roller-rpc` +### `%roller-rpc` `%roller-rpc`, stored at `/app/roller-rpc.hoon`, is a very simple Gall app responsible for receiving HTTP RPC-API calls, typically sent from other Urbit ID users via Bridge. It then translates @@ -152,7 +152,7 @@ does not keep any state - its only purpose is to act as an intermediary between Bridge and `%roller`. See [here](/reference/azimuth/l2/layer2-api) for more information on the JSON RPC-API. -### `naive.hoon` {% #naive %} +## `naive.hoon` {% #naive %} `/lib/naive.hoon` consists of a gate whose sample is a `verifier`, `chain-id=@ud`, `state`, and `input`, which outputs a cell of `[effects state]`. This is the diff --git a/content/reference/azimuth/hd-wallet.md b/content/reference/azimuth/hd-wallet.md index 2a4cd3f..5ba1be7 100644 --- a/content/reference/azimuth/hd-wallet.md +++ b/content/reference/azimuth/hd-wallet.md @@ -23,7 +23,7 @@ HD wallet [below](#hardware-hd-wallet). Urbit HD wallets are composed of the following items, which are each assigned to their own individual Ethereum key-pairs. -### Master Ticket +## Master Ticket Think of your master ticket like a very high-value password. The master ticket is the secret code from which all of your other keys are derived. Technically, @@ -31,14 +31,14 @@ your master ticket is a cryptographic seed. You should never share it with anyon store it very securely. This ticket can derive all of your other keys: your ownership key and all of the related proxies. -### Ownership Address +## Ownership Address An ownership address has all rights over the assets deeded to it. These rights are on-chain actions described and implemented in [Ecliptic](/reference/glossary/ecliptic), Azimuth's suite of governing smart-contracts. -### Proxies +## Proxies Each permanent Urbit ID can designate one or more [proxies](https://urbit.org/using/id/proxies), which are Ethereum addresses capable of a @@ -46,7 +46,7 @@ limited subset of Urbit ID transactions, such as spawning planets or rotating keys. The HD wallet automatically generates additional addresses utilized as proxies according to what is appropriate for your Urbit ID. -### HD wallet generation +## HD wallet generation Your Urbit HD wallet is generated from a `@q` seed called `T`, which looks something like `~sampel-ticket-bucbel-sipnem`. This is the string known as your @@ -68,7 +68,7 @@ which will be known as your ownership address. Bridge then automatically uses your ownership address to assign the other proxies to the other wallets generated. -### ERC-721 +## ERC-721 Most Ethereum tokens use the ERC-20 standard for smart contracts. Urbit identities are, however, essentially different from most Ethereum tokens, due to diff --git a/content/reference/azimuth/l2/l2-actions.md b/content/reference/azimuth/l2/l2-actions.md index d1eb8d1..ecded24 100644 --- a/content/reference/azimuth/l2/l2-actions.md +++ b/content/reference/azimuth/l2/l2-actions.md @@ -17,14 +17,14 @@ while otherwise remaining on layer 1, but it is not possible to transfer only the management proxy to layer 2; it may only happen as a side-effect of transferring ownership to layer 2. -### Moving a pre-existing ship to L2 +## Moving a pre-existing ship to L2 In order to move your ship from layer 1 to layer 2, transfer ownership of your ship to the address `0x1111111111111111111111111111111111111111`. The easiest way to accomplish this is using [Bridge](/reference/glossary/bridge). The Azimuth smart contracts interpret any ship at this address as being on layer 2. -### Dominion +## Dominion Layer 2 Azimuth data for a given ship includes which layer that ship is on. We call this the ship's _dominion_. There are three dominions: `%l1`, `l2`, and @@ -32,11 +32,11 @@ call this the ship's _dominion_. There are three dominions: `%l1`, `l2`, and of the three dominions, and galaxies may exist in dominion `%l1` or `%spawn`. We detail what this means in each case in the following. -### Planets +## Planets *Permitted dominions:* `%l1`, `%l2`. -#### `%l1` planets +### `%l1` planets *Permitted layer 2 actions:* - owner: `%escape`, `%cancel-escape` @@ -54,7 +54,7 @@ sponsorship actions. Layer 1 planets may also move to dominion `%l2` by depositing their ownership to the layer 2 deposit address. -#### `%l2` planets +### `%l2` planets *Permitted layer 2 actions:* - owner: `%transfer-point`, `%configure-keys`, @@ -71,11 +71,11 @@ will always be on layer 2. A layer 2 planet is no longer capable of performing any layer 1 actions, and cannot move to layer 1. -### Stars +## Stars *Permitted dominions:* `%l1`, `%spawn`, `%l2`. -#### `%l1` stars +### `%l1` stars *Permitted layer 2 actions:* - owner: `%escape`, `%cancel-escape`, `%adopt`, @@ -94,7 +94,7 @@ A `%l1` dominion star may move to dominion `%spawn` by depositing its spawn prox layer 2 deposit address, or may move to dominion `%l2` by depositing its ownership to the layer 2 deposit address. Both actions are irreversible. -#### `%spawn` stars +### `%spawn` stars *Permitted layer 2 actions:* - owner: `%escape`, `%cancel-escape`, `%adopt`, @@ -116,7 +116,7 @@ A star moving from `%l1` to `%spawn` has no effect on sponsorship status of any of its sponsored planets. Moving to `%spawn` from `%l1` is currently irreversible - the only further change to dominion permitted is moving to `%l2`. -#### `%l2` stars +### `%l2` stars *Permitted layer 2 actions:* - owner: `%transfer-point`, `%spawn`, `%configure-keys`, `%escape`, @@ -134,11 +134,11 @@ spawned by a `%spawn` dominion galaxy. A star in dominion `%l2` cannot perform any layer 1 actions. -### Galaxies +## Galaxies *Permitted dominions:* `%l1`, `%spawn`. -#### `%l1` galaxies +### `%l1` galaxies *Permitted layer 2 actions:* - owner: `%adopt`, `%reject`, `%detach` @@ -162,7 +162,7 @@ Layer 2 has no interactions with this contract - all stars released in this manner are `%l1` dominion stars. Moving to the `%spawn` dominion has no effect on sponsorship status. -#### `%spawn` galaxies +### `%spawn` galaxies *Permitted layer 2 actions:* - owner: `%adopt`, `%reject`, `%detach`, `%spawn`, diff --git a/content/reference/azimuth/l2/roller-tutorial.md b/content/reference/azimuth/l2/roller-tutorial.md index fb42437..53e0d2c 100644 --- a/content/reference/azimuth/l2/roller-tutorial.md +++ b/content/reference/azimuth/l2/roller-tutorial.md @@ -32,7 +32,7 @@ There are three main steps involved with setting up a roller: - starting and configuring `%roller`, - aiming your front-end at the roller -### 1. Make sure `%azimuth` state is up to date +## 1. Make sure `%azimuth` state is up to date If you are using an ordinary live ship on the network as the roller, you should already have the latest `%azimuth` state and this step should not be necessary @@ -52,7 +52,7 @@ found under the Setting page for the node on infura.io listed under `ENDPOINTS`. If you do not perform this step, you'll later see an error "roller not ready" when the first roller batch is about to be submitted. -### 2. Starting and configuring `%roller` {% #step2 %} +## 2. Starting and configuring `%roller` {% #step2 %} This step must be performed whether you're using a fakezod or a live ship. @@ -125,7 +125,7 @@ work with a front-end if you want to use it on livenet. We cover the additional settings for `%roller` at the end. -### 3. Aiming Bridge at the roller +## 3. Aiming Bridge at the roller The last step is to set up the web interface by which users can submit transactions to be batched by the roller, which we refer to as the front-end. We @@ -153,7 +153,7 @@ REACT_APP_ROLLER_HOST=https://myroller.sampel-pal.net/v1/roller npm run pilot-ma This will launch a server running Bridge that utilizes the mainnet roller you set up at `https://myroller.sampel-pal.net/v1/roller`. -### Additional `%roller` commmands +## Additional `%roller` commmands `%roller` has a few other settings and commands for managing things like the rate at which transactions are submitted and manually submitting batches. These diff --git a/content/reference/glossary/kernel.md b/content/reference/glossary/kernel.md index ab4ffa1..f7dd7aa 100644 --- a/content/reference/glossary/kernel.md +++ b/content/reference/glossary/kernel.md @@ -1,5 +1,5 @@ +++ -title = "Kelvin versioning" +title = "Kernel" [extra] category = "arvo" diff --git a/content/reference/glossary/remote-scry.md b/content/reference/glossary/remote-scry.md new file mode 100644 index 0000000..f758e13 --- /dev/null +++ b/content/reference/glossary/remote-scry.md @@ -0,0 +1,32 @@ ++++ +title = "Scry" + +[extra] +category = "hoon-nock" + +[glossaryEntry."Remote scry"] +name = "remote scry" +symbol = "" +usage = "hoon-nock" +desc = "A remote scry is a read-only request to the namespace of a remote ship." + ++++ + +A **remote scry** is a read-only request to the namespace of a remote +[vane](/reference/glossary/vane) or [agent](/reference/glossary/agent). These +differ from ordinary [local scries](/reference/glossary/scry) and are not +performed with the `.^` [rune](/reference/glossary/rune). + +Remote scries reduce event log bloat on the publishing ship, allow the +publisher's runtime to cache data, and especially improve performance when +publishing the same data for many ships to retrieve. + +At the the time of writing, Gall allows agents to bind data to remote scry +paths and perform remote scries with `task`s to +[Ames](/reference/glossary/ames). Additionally, Clay uses remote scries +internally to sync remote desks. + +#### Further Reading + +- [Remote scry guide](/guides/additional/remote-scry): developer documentation + of how remote scries work and how to use them. diff --git a/content/reference/hoon/advanced.md b/content/reference/hoon/advanced.md index 1cbc89a..d73448f 100644 --- a/content/reference/hoon/advanced.md +++ b/content/reference/hoon/advanced.md @@ -51,7 +51,7 @@ changed two parts: `%core` and `%face`. We added polymorphism to If cores never changed, we wouldn't need polymorphism. Of course, nouns are immutable and never change, but we use them as -but we use them as templates to construct new nouns around. +templates to construct new nouns around. Suppose we take a core, a cell `[battery payload]`, and replace the payload with a different noun. Then, we invoke an arm from diff --git a/content/reference/hoon/auras.md b/content/reference/hoon/auras.md index a7be634..ab706e9 100644 --- a/content/reference/hoon/auras.md +++ b/content/reference/hoon/auras.md @@ -9,7 +9,7 @@ This is used for type checking as well as pretty printing. You can learn more about auras in [Hoon school](/guides/core/hoon-school/B-syntax#nouns). -### Table of Auras +## Table of Auras ``` Aura Meaning Example Literal Syntax @@ -48,7 +48,7 @@ Aura Meaning Example Literal Syntax @ux unsigned hexadecimal 0x5f5.e138 ``` -### Bitwidth +## Bitwidth Capital letters at the end of auras indicate the bitwidth in binary powers of two, starting from A. @@ -61,7 +61,7 @@ two, starting from A. @uvJ unsigned, 512-bit integer (frequently used for entropy) ``` -### Nesting +## Nesting A given aura nests under any aura whose name is a substring or extension of the given aura: @@ -94,7 +94,7 @@ This is implicitly done by the irregular form of `^-`. 7.303.014 ``` -### Bunting +## Bunting The bunt value for all auras is 0 except for `@da`. diff --git a/content/reference/hoon/irregular.md b/content/reference/hoon/irregular.md index a2e1acc..e1332a3 100644 --- a/content/reference/hoon/irregular.md +++ b/content/reference/hoon/irregular.md @@ -3,6 +3,17 @@ title = "Irregular forms" weight = 20 +++ +While Hoon has a large amount of sugar syntax, some forms that may look irregular are +actually regular wing syntax or another language feature, such as `,`. + +When in doubt, you can use the [`!,` zapcom](/reference/hoon/rune/zap#-zapcom) rune to +determine the AST to which Hoon parses an expression. + +``` +> !,(*hoon c.b.a) +[%wing p=~[%c %b %a]] +``` + ## Quick Lookup of Irregular Forms | Form | Regular Form | @@ -366,6 +377,82 @@ See [%sand](/reference/hoon/rune/constants#warm) for other irregular definitions "~[1 2 3]" ``` +### `,` com + +`,` can serve in several capacities in Hoon programs: + +1. As sugar for the `^:` ketcol or `$;` bucmic runes, toggling structure and value mode. + (Toggling out of structure mode is uncommon.) + + ``` + > !,(*hoon ,[@t @t]) + [ %ktcl + p=[%bccl p=[i=[%base p=[%atom p=~.t]] t=[i=[%base p=[%atom p=~.t]] t=~]]] + ] + + > !,(*hoon |=(a=,[@t @t] b)) + [ %brts + p + [ %bcts + p=term=%a + q + [ %bcmc + p=[%cltr p=[i=[%base p=[%atom p=~.t]] t=[i=[%base p=[%atom p=~.t]] t=~]]] + ] + ] + q=[%cnts p=~[[%.y p=2] %a] q=~] + ] + + > !,(*hoon ,,[@t @t]) + [ %ktcl + p + [ %bcmc + p=[%cltr p=[i=[%base p=[%atom p=~.t]] t=[i=[%base p=[%atom p=~.t]] t=~]]] + ] + ] + ``` + + (`$;` bucmic, or manual value mode, allows the use of value mode syntax to + construct a mold. Concretely, it lets you build a mold out of `hoon` instead + of out of `spec`. It is not commonly used.) + + From value mode to structure mode: + + ```hoon + [%ktcl p=spec] + ``` + + From structure mode to value mode: + + ```hoon + [%bcmc p=hoon] + ``` + +2. As wing syntax for stripping a face. + + For instance, a line similar to the following is present in many Gall agents + receiving HTTP requests via Eyre: + + ``` + =/ ,request-line:server (parse-request-line:server url.request.inbound-request) + ``` + + This `,` lets you avoid using an outer face when handling the result. + + ``` + > =/ ,@ud 1 + - + 1 + > !,(*hoon =/(,@ud 1 -)) + [ %tsfs + p=[%spec spec=[%bcmc p=[%base p=[%atom p=~.ud]]] skin=[%base base=%noun]] + q=[%sand p=%ud q=1] + r=[%cnts p=~[[%.y p=2]] q=~] + ] + ``` + +3. As a separator, e.g. between pairs in an inline `%=` centis expression, `$(i +(i), j (dec j))`. + ## Commentary In our in-house examples throughout our documentation, we use irregular forms instead of regular for the sake of verbosity. But remember with irregular forms: everything is just runes! Like magic. In general, irregular forms (usually) read better, but of course regular forms provide more information about what you're doing by showing you the full rune. Of course, it's up to you, the Hoon programmer, as to whether or not you want to use these. diff --git a/content/reference/hoon/limbs/limb.md b/content/reference/hoon/limbs/limb.md index 362d69a..d3b031a 100644 --- a/content/reference/hoon/limbs/limb.md +++ b/content/reference/hoon/limbs/limb.md @@ -4,13 +4,13 @@ weight = 1 +++ A limb is an attribute of subject. -### Produces +## Produces There are two kinds of limbs: arms and legs. An **arm** is a computation of some core. A **leg** is a piece of data in the subject. If a limb expression resolves to a leg, the leg is produced. If a limb expression resolves to an arm -- in particular, by way of an arm name -- then the arm is computed with its parent core as the subject. The result of that computation is produced. -### Syntax +## Syntax Irregular: `+15` is slot `15`. The value at address `15` of the subject is produced. @@ -26,7 +26,7 @@ Irregular: `^^^abc` is the name `abc`, but which will skip the first three name Irregular: `+<-` is "take the tail, then take the head of that, then the head of that." `+` and `>` mean "tail" while `-` and `<` mean "head." This limb syntax starts on `+` or `-` and alternates with `>` or `<` for readability. -### Traverse +## Traverse Name resolution happens by way of a search through the subject. The search traverse takes a name `q` and a **skip count** `p`. @@ -47,7 +47,7 @@ If the skip count `p` is nonzero, we pretend our first `p` matches are actually mismatches. This lets the programmer "look through" an overriding label. -### Examples +## Examples The Dojo prompt gives you a subject with a decent namespace. Try: diff --git a/content/reference/hoon/limbs/wing.md b/content/reference/hoon/limbs/wing.md index 6f36361..8f5693b 100644 --- a/content/reference/hoon/limbs/wing.md +++ b/content/reference/hoon/limbs/wing.md @@ -6,15 +6,15 @@ weight = 2 A wing is a limb search path into the subject. -### Produces +## Produces A wing is a list of limbs (including a trivial list of one limb). The limbs are resolved in succession. The result of the last limb resolution is the value produced by the wing expression. -### Syntax +## Syntax Irregular: `a.b.c`. Read this as '`a` in `b` in `c`'. Finds limb `a` within limb `b` within limb `c` of the subject. -### Discussion +## Discussion Intuitively, Hoon wings are written in the opposite order from attribute dot-paths in most languages. Hoon `a.b.c` is Java's @@ -29,7 +29,7 @@ The mysterious idiom `..b` produces the leg `b` if `b` is a leg; the core exporting `b` if `b` is an arm. Since `.` is the same limb as `+`, `..b` is the same wing as `+1.foo`. -### Examples +## Examples ``` ~zod:dojo> =a [fod=3 bat=[baz=1 moo=2]] @@ -40,3 +40,26 @@ is the same limb as `+`, `..b` is the same wing as `+1.foo`. ~zod:dojo> moo.bat.a 2 ``` + +## Wing Resolution + +There are two common syntaxes used to resolve a wing path into the +current subject: `.` dot and `:` col. + +- `.` dot syntax, as `c.b.a`, resolves the wing path into the subject + at the right hand using Nock 0 (or possibly Nock 9 or Nock 10 + depending on the expression). + + ```hoon + > !,(*hoon c.b.a) + [%wing p=~[%c %b %a]] + ``` + +- The `:` col operator expands to a `=>` tisgar to resolve the wing path + against its right-hand side as the subject. This can be a Nock 7 + or possibly optimized by the compiler to a Nock 0. + + ```hoon + > !,(*hoon c:b:a) + [%tsgl p=[%wing p=~[%c]] q=[%tsgl p=[%wing p=~[%b]] q=[%wing p=~[%a]]]] + ``` diff --git a/content/reference/hoon/overview.md b/content/reference/hoon/overview.md index a56cbf1..d156638 100644 --- a/content/reference/hoon/overview.md +++ b/content/reference/hoon/overview.md @@ -11,7 +11,7 @@ language that set it apart from others. If you're looking to learn Hoon, check out our tutorial series called [Hoon School](/guides/core/hoon-school/) below. -### What can Hoon do that other languages can't? +## What can Hoon do that other languages can't? The short answer is: implement a purely functional operating system. Try to do this in a principled way in Haskell, and the problems you'll @@ -25,7 +25,7 @@ functional languages are: - Typesafe metaprogramming, and - Hot code reload and online data migration. -### What is Hoon good at? +## What is Hoon good at? Hoon is mostly good at compiling and running other Hoon code. Urbit consists of many layers of bootstrapping. Several of these layers lean @@ -34,7 +34,7 @@ build system, the Dojo shell, and the Arvo kernel itself. Even Urbit's chat application lets you run Hoon expressions and share the results with your friends. -### Why did we write the OS in Hoon? +## Why did we write the OS in Hoon? The chain of reasoning goes something like this: @@ -78,13 +78,13 @@ Urbit's solution to these design constraints. Some Lisps come close to meeting these criteria — and Nock is very Lisp-like — but no practical Lisp dialects are nearly as pure or axiomatic as Nock. -### What is special about Hoon? +## What is special about Hoon? It's a purely functional systems language. Calling it a functional analog of C is not too far off in several ways. Almost all code throughout Urbit's kernelspace and userspace is written in Hoon. -### What properties does Hoon have? What type of language is it? +## What properties does Hoon have? What type of language is it? Hoon is a statically typed, purely functional, strictly evaluated programming language. @@ -215,7 +215,7 @@ Hoon and Nock have several unusual properties: calmness of working with such inert building blocks is addictive, as many Hoon programmers will attest. -### Why is Hoon the way it is? +## Why is Hoon the way it is? Minimalism, mostly. diff --git a/content/reference/hoon/rune/cen.md b/content/reference/hoon/rune/cen.md index daca6ef..4fb5711 100644 --- a/content/reference/hoon/rune/cen.md +++ b/content/reference/hoon/rune/cen.md @@ -547,8 +547,8 @@ Three arguments, fixed. - Tall - ```hoon - %~ p q - r + %~ p=wing q=hoon + r=hoon ``` --- @@ -571,14 +571,18 @@ In the irregular form, `r` may be split into multiple parts. Multiple parts of #### Semantics -A `%~` expression evaluates the arm of a door (i.e., a core with a sample). `a` -is a wing that resolves to the arm from within the door in question. `b` is the -door itself. `c` is the sample of the door. +A `%~` expression evaluates the arm of a door (i.e., a core with a sample). `p` +is a wing that resolves to the arm from within the door in question. `q` is the +door itself. `r` is the sample of the door. #### Discussion `%~` is the general case of a function call, `%-`. In both, we replace the sample (`+6`) of a core. In `%-` the core is a gate and the `$` arm is evaluated. In `%~` the core is a door and any arm may be evaluated. You must identify the arm to be run: `%~(arm door arg)`. +Note also that `p` is a wing and can therefore be `.`, as in `~(. door +sample)`. This little idiom lets you load your sample into the door once +instead of over and over. + See also [`|_`](/reference/hoon/rune/bar#_-barcab). #### Examples @@ -587,6 +591,9 @@ See also [`|_`](/reference/hoon/rune/bar#_-barcab). > =mycore |_ a=@ ++ plus-two (add 2 a) ++ double (mul 2 a) + ++ mul-by + |= b=@ + (mul a b) -- > ~(plus-two mycore 10) @@ -594,6 +601,10 @@ See also [`|_`](/reference/hoon/rune/bar#_-barcab). > ~(double mycore 10) 20 + +> =tencore ~(. mycore 10) +> (mul-by:tencore 5) +50 ``` --- @@ -784,9 +795,9 @@ Note that `a` is a wing, not just any expression. Knowing that a function call `baz`, and then resolving to the `$` limb, you might think `(foo baz)` would mean `%=(foo +6 baz)`. -But it's actually `=+(foo =>(%=(+2 +6 baz) $))`. Even if `foo` is a wing, we +But it's actually `=+(foo =>(%=(+2 +6 baz:+3) $))`. Even if `foo` is a wing, we would just be mutating `+6` within the core that defines the `foo` arm. Instead -we want to modify the **product** of `foo` -- the gate -- so we have to pin it +we want to modify the **product** of `foo`—the gate—so we have to pin it into the subject. Here's that again in tall form: @@ -794,7 +805,7 @@ Here's that again in tall form: ```hoon =+ foo => %= +2 - +6 baz + +6 baz:+3 == $ ``` diff --git a/content/reference/hoon/rune/ket.md b/content/reference/hoon/rune/ket.md index 5eae376..e1c2081 100644 --- a/content/reference/hoon/rune/ket.md +++ b/content/reference/hoon/rune/ket.md @@ -157,9 +157,7 @@ The prettyprinter shows the core metal (`.` gold, `|` iron): ## `^:` "ketcol" -Mold gate for type `p`. - -**Note this rune is now redundant.** +Switch parser into structure mode (mold definition) and produce a gate for type `p`. (See [`,` com]() which toggles modes.) #### Syntax @@ -200,16 +198,20 @@ One argument, fixed. #### Produces -A gate that returns the sample value if it's of the correct type, but crashes +A gate that returns the sample value if it is of the correct type, but crashes otherwise. #### Discussion `^:` is used to produce a mold that crashes if its sample is of the wrong type. +In structure mode, `[a=@ b=@]` is a mold for a cell, whereas in value mode it's +a pair of molds. Sometimes you need a structure in value mode, in which you can +use `^:` or `,`. + Molds used to produced their bunt value if they couldn't mold their sample. This -is no longer the case: molds now crash if molding fails, so **this rune is -redundant**. +is no longer the case: molds now crash if molding fails, so this rune is +redundant in certain cases. One may expect that `^:(path /foo)` would result in a syntax error since `^:` only takes one child, but instead it will parse as `=< ^ %:(path /foo)`. Since @@ -233,6 +235,15 @@ syntax for `=<`. > (^:(@) [22 33]) ford: %ride failed to execute: + +> (,cord 55) +'7' + +> (ream ',@t') +[%ktcl p=[%base p=[%atom p=~.t]]] + +> (ream ',cord') +[%ktcl p=[%like p=~[%cord] q=~]] ``` --- @@ -376,7 +387,7 @@ infinite loop in the compiler). ~zod:dojo> ^-(@t (add 90 7)) 'a' -/~zod:dojo> =foo |= a=@tas +/~zod:dojo> =foo |= a=@ ^- (unit @ta) `a diff --git a/content/reference/hoon/rune/tis.md b/content/reference/hoon/rune/tis.md index 28be861..21bfe6a 100644 --- a/content/reference/hoon/rune/tis.md +++ b/content/reference/hoon/rune/tis.md @@ -533,18 +533,22 @@ Two arguments, fixed. #### Discussion `=-` is just like `=+` but its subexpressions are reversed. `=-` looks better -than `=+` when the expression you're pinning to the subject is much smaller than +than `=+` when the expression you're pinning to the subject is much larger than the expression that uses it. #### Examples ``` > =foo |= a=@ - =+ b=1 - =- (add a b c) - c=2 + =- (add a b) + :* %a-bunch + %of-stuff + %here + b=2 + %and-perhaps-more + == > (foo 5) -8 +7 ``` --- diff --git a/content/reference/hoon/stdlib/1a.md b/content/reference/hoon/stdlib/1a.md index f856748..835cf09 100644 --- a/content/reference/hoon/stdlib/1a.md +++ b/content/reference/hoon/stdlib/1a.md @@ -241,12 +241,7 @@ An [`atom`](/reference/glossary/atom). ~/ %div |: [a=`@`1 b=`@`1] ^- @ - ~_ leaf+"divide-by-zero" - ?< =(0 b) - =+ c=0 - |- - ?: (lth a b) c - $(a (sub a b), c +(c)) + -:(dvr a b) ``` #### Examples @@ -302,10 +297,14 @@ A cell of [`atoms`](/reference/glossary/atom). ```hoon ++ dvr - ~/ %dvr - |= [a=@ b=@] + |: [a=`@`1 b=`@`1] ^- [p=@ q=@] - [(div a b) (mod a b)] + ~_ leaf+"divide-by-zero" + ?< =(0 b) + =+ c=0 + |- + ?: (lth a b) [c a] + $(a (sub a b), c +(c)) ``` #### Examples @@ -728,8 +727,7 @@ An [`atom`](/reference/glossary/atom). ~/ %mod |: [a=`@`1 b=`@`1] ^- @ - ?< =(0 b) - (sub a (mul b (div a b))) + +:(dvr a b) ``` #### Examples diff --git a/content/reference/hoon/stdlib/1b.md b/content/reference/hoon/stdlib/1b.md index 7f7d816..cb631d5 100644 --- a/content/reference/hoon/stdlib/1b.md +++ b/content/reference/hoon/stdlib/1b.md @@ -269,7 +269,7 @@ An [`atom`](/reference/glossary/atom). ``` ``` -> (peg 4 (4 2)) +> (peg 4 (peg 4 2)) 32 ``` diff --git a/content/reference/hoon/stdlib/2i.md b/content/reference/hoon/stdlib/2i.md index 576bb85..5e36296 100644 --- a/content/reference/hoon/stdlib/2i.md +++ b/content/reference/hoon/stdlib/2i.md @@ -1536,7 +1536,7 @@ A `map`. Depth -Produce the depth of the tree map `a`. +Produce the size of the tree map `a`. #### Accepts diff --git a/content/reference/hoon/stdlib/3b.md b/content/reference/hoon/stdlib/3b.md index 5d34e27..ae0fd9d 100644 --- a/content/reference/hoon/stdlib/3b.md +++ b/content/reference/hoon/stdlib/3b.md @@ -2739,7 +2739,7 @@ An `fn`. ### `++sun:fl` -Signed integer to float +Unsigned integer to float Produces `a` in floating-point representation. @@ -2778,11 +2778,11 @@ An `fn`. Signed integer to float -Produces the floating-point representation of `a`, an unsigned integer. +Produces the floating-point representation of `a`, a signed integer. #### Accepts -`a` is an unsigned integer. +`a` is a signed integer. #### Produces @@ -3869,7 +3869,7 @@ Converts `a` from a signed integer to `@r`. #### Accepts -`a` is `@s`, an unsigned integer +`a` is `@s`, a signed integer #### Produces @@ -5461,9 +5461,9 @@ A `@rs`. ### `++san:rs` -Signed integer to `@rs`' +Signed integer to `@rs` -Converts `a` from an unsigned integer to `@rs`. +Converts `a` from a signed integer to `@rs`. #### Accepts @@ -6138,7 +6138,7 @@ A `@rq`. Unsigned integer to `@rq` -Converts `@` from an unsigned integer to `@rq`. +Converts `a` from an unsigned integer to `@rq`. #### Accepts @@ -6167,7 +6167,7 @@ A `@rq`, a quad-precision float. Signed integer to `rq` -Converts `@` from a signed integer to `@rq`. +Converts `a` from a signed integer to `@rq`. #### Accepts @@ -6907,7 +6907,7 @@ A `@rh`, a half-precision float. Unsigned integer to `@rh` -Converts `@` from an unsigned integer to `@rh`. +Converts `a` from an unsigned integer to `@rh`. #### Accepts @@ -6936,7 +6936,7 @@ A `@rh`, a half-precision float. Signed integer to `@rh` -Converts `@` from a signed integer to `@rh`. +Converts `a` from a signed integer to `@rh`. #### Accepts diff --git a/content/reference/hoon/stdlib/4b.md b/content/reference/hoon/stdlib/4b.md index ce0f2ce..86656c1 100644 --- a/content/reference/hoon/stdlib/4b.md +++ b/content/reference/hoon/stdlib/4b.md @@ -121,15 +121,15 @@ usage = "stdlib" slug = "#ruxat" desc = "Used in the Hoon standard library." -[glossaryEntry."Soft-cast by odor"] -name = "Soft-cast by odor" +[glossaryEntry."Soft-cast by aura"] +name = "Soft-cast by aura" symbol = "sand" usage = "stdlib" slug = "#sand" desc = "Used in the Hoon standard library." -[glossaryEntry."Check odor validity"] -name = "Check odor validity" +[glossaryEntry."Check aura validity"] +name = "Check aura validity" symbol = "sane" usage = "stdlib" slug = "#sane" diff --git a/content/reference/hoon/stdlib/4l.md b/content/reference/hoon/stdlib/4l.md index dc6943b..2ad309d 100644 --- a/content/reference/hoon/stdlib/4l.md +++ b/content/reference/hoon/stdlib/4l.md @@ -2,8 +2,8 @@ title = "4l: Atom Parsing" weight = 39 -[glossaryEntry."Parse odor-atom pair"] -name = "Parse odor-atom pair" +[glossaryEntry."Parse aura-atom pair"] +name = "Parse aura-atom pair" symbol = "bisk:so" usage = "stdlib" slug = "#biskso" diff --git a/content/reference/hoon/stdlib/4m.md b/content/reference/hoon/stdlib/4m.md index b903c98..46c7caf 100644 --- a/content/reference/hoon/stdlib/4m.md +++ b/content/reference/hoon/stdlib/4m.md @@ -23,15 +23,15 @@ usage = "stdlib" slug = "#slat" desc = "Used in the Hoon standard library." -[glossaryEntry."Demand: parse cord with input odor"] -name = "Demand: parse cord with input odor" +[glossaryEntry."Demand: parse cord with input aura"] +name = "Demand: parse cord with input aura" symbol = "slav" usage = "stdlib" slug = "#slav" desc = "Used in the Hoon standard library." -[glossaryEntry."Parse cord to input odor"] -name = "Parse cord to input odor" +[glossaryEntry."Parse cord to input aura"] +name = "Parse cord to input aura" symbol = "slaw" usage = "stdlib" slug = "#slaw" diff --git a/content/reference/hoon/stdlib/5a.md b/content/reference/hoon/stdlib/5a.md index 9c77cb3..881eccf 100644 --- a/content/reference/hoon/stdlib/5a.md +++ b/content/reference/hoon/stdlib/5a.md @@ -58,8 +58,8 @@ usage = "stdlib" slug = "#face" desc = "Used in the Hoon standard library." -[glossaryEntry."Odor compatibility (compiler utility)"] -name = "Odor compatibility (compiler utility)" +[glossaryEntry."Aura compatibility (compiler utility)"] +name = "Aura compatibility (compiler utility)" symbol = "fitz" usage = "stdlib" slug = "#fitz" diff --git a/content/reference/hoon/zuse/2d_1-5.md b/content/reference/hoon/zuse/2d_1-5.md index afc7274..05d6fcf 100644 --- a/content/reference/hoon/zuse/2d_1-5.md +++ b/content/reference/hoon/zuse/2d_1-5.md @@ -342,7 +342,7 @@ A `json` object. ``` ``` -> (crip (en-json:html (frond:enjs:format 'foo' s+'bar'))) +> (en:json:html (frond:enjs:format 'foo' s+'bar')) '{"foo":"bar"}' ``` @@ -377,7 +377,7 @@ A `json` object. > =a ~[['foo' n+~.123] ['bar' s+'abc'] ['baz' b+&]] > (pairs:enjs:format a) [%o p={[p='bar' q=[%s p='abc']] [p='baz' q=[%b p=%.y]] [p='foo' q=[%n p=~.123]]}] -> (crip (en-json:html (pairs:enjs:format a))) +> (en:json:html (pairs:enjs:format a)) '{"foo":123,"baz":true,"bar":"abc"}' ``` @@ -414,7 +414,7 @@ A `json` string. ``` ``` -> (crip (en-json:html (tape:enjs:format "foo"))) +> (en:json:html (tape:enjs:format "foo")) '"foo"' ``` @@ -451,7 +451,7 @@ A `json` string. ``` ``` -> (crip (en-json:html (wall:enjs:format ~["foo" "bar" "baz"]))) +> (en:json:html (wall:enjs:format ~["foo" "bar" "baz"])) '"foo\\nbar\\nbaz\\n"' ``` @@ -490,7 +490,7 @@ A `json` ~~string~~ number but with the `@p` rendered as text rather than a numb ``` ``` -> (crip (en-json:html (ship:enjs:format our))) +> (en:json:html (ship:enjs:format our)) '"zod"' ``` @@ -537,7 +537,7 @@ A `json` number. ``` ``` -> (crip (en-json:html (numb:enjs:format 123))) +> (en:json:html (numb:enjs:format 123)) '123' ``` @@ -573,7 +573,7 @@ A `json` number. ``` ``` -> (crip (en-json:html (sect:enjs:format now))) +> (en:json:html (sect:enjs:format now)) '1630407290' ``` @@ -609,7 +609,7 @@ A `json` number. ``` ``` -> (crip (en-json:html (time:enjs:format now))) +> (en:json:html (time:enjs:format now)) '1630407462905' ``` @@ -646,7 +646,7 @@ A `json` string. ``` ``` -> (crip (en-json:html (path:enjs:format /foo/bar/baz))) +> (en:json:html (path:enjs:format /foo/bar/baz)) '"/foo/bar/baz"' ``` @@ -681,12 +681,12 @@ A `json` array of strings. > =tan rose+[[" " "[" "]"] ~[leaf+"foo" leaf+"bar" leaf+"baz"]] > (tank:enjs:format tan) [%a p=~[[%s p='[foo bar baz]']]] -> (crip (en-json:html (tank:enjs:format tan))) +> (en:json:html (tank:enjs:format tan)) '["[foo bar baz]"]' ``` ``` > =tan rose+[[" " "[" "]"] ~[leaf+"aaaaaaaaaaaaaaaaaaaaa" leaf+"bbbbbbbbbbbbbbbbbbb" leaf+"ccccccccccccccccccc" leaf+"dddddddddddddddddd"]] -> (crip (en-json:html (tank:enjs:format tan))) +> (en:json:html (tank:enjs:format tan)) '["[ aaaaaaaaaaaaaaaaaaaaa"," bbbbbbbbbbbbbbbbbbb"," ccccccccccccccccccc"," dddddddddddddddddd","]"]' ``` diff --git a/content/reference/hoon/zuse/2d_6.md b/content/reference/hoon/zuse/2d_6.md index 19d42e9..ddcc28c 100644 --- a/content/reference/hoon/zuse/2d_6.md +++ b/content/reference/hoon/zuse/2d_6.md @@ -920,19 +920,19 @@ First, let's store some objects containing key-value pairs: ``` > =obj-1 o+(malt ['foo' s+'hello']~) -> (crip (en-json:html obj-1)) +> (en:json:html obj-1) '{"foo":"hello"}' ``` ``` > =obj-2 o+(malt ~[['bar' a+~[n+'123' n+'456']]]) -> (crip (en-json:html obj-2)) +> (en:json:html obj-2) '{"bar":[123,456]}' ``` ``` > =obj-3 o+(malt ['baz' b+&]~) -> (crip (en-json:html obj-3)) +> (en:json:html obj-3) '{"baz":true}' ``` @@ -1210,7 +1210,7 @@ A `(jug cord [type])`, where `[type]` is the type produced by `fist`. #### Examples ``` -> =jon (need (de-json:html '{"foo":["aaa","bbb","ccc"],"bar":["xxx","yyy","zzz"]}')) +> =jon (need (de:json:html '{"foo":["aaa","bbb","ccc"],"bar":["xxx","yyy","zzz"]}')) > ((oj so):dejs:format jon) {[p='bar' q={'xxx' 'zzz' 'yyy'}] [p='foo' q={'bbb' 'ccc' 'aaa'}]} ``` @@ -1248,7 +1248,7 @@ A `(map cord [type])`, where `[type]` is the type produced by `wit`. #### Examples ``` -> =jon (need (de-json:html '{"foo":"aaa","bar":"bbb"}')) +> =jon (need (de:json:html '{"foo":"aaa","bar":"bbb"}')) > ((om so):dejs:format jon) {[p='bar' q='bbb'] [p='foo' q='aaa']} ``` @@ -1290,7 +1290,7 @@ A `(map [a] [b])` where `[a]` is the type produced by `fel` and `[b]` is the typ #### Examples ``` -> =jon (need (de-json:html '{"123":"aaa","456":"bbb"}')) +> =jon (need (de:json:html '{"123":"aaa","456":"bbb"}')) > ((op dem so):dejs:format jon) {[p=456 q='bbb'] [p=123 q='aaa']} ``` diff --git a/content/reference/hoon/zuse/2d_7.md b/content/reference/hoon/zuse/2d_7.md index c78925c..d084c97 100644 --- a/content/reference/hoon/zuse/2d_7.md +++ b/content/reference/hoon/zuse/2d_7.md @@ -617,16 +617,16 @@ A `(unit tank)`. The `unit` is `~` if decoding failed. ``` > =js-txt '{"rose":{"lines":[{"leaf":"foo"},{"leaf":"bar"},{"leaf":"baz"}],"style":{"mid":" ","close":"]","open":"["}}}' -> (dank:dejs-soft:format (need (de-json:html js-txt))) +> (dank:dejs-soft:format (need (de:json:html js-txt))) [~ [%rose p=[p=" " q="[" r="]"] q=~[[%leaf p="foo"] [%leaf p="bar"] [%leaf p="baz"]]]] -> ~(ram re (need (dank:dejs-soft:format (need (de-json:html js-txt))))) +> ~(ram re (need (dank:dejs-soft:format (need (de:json:html js-txt))))) "[foo bar baz]" ``` ``` -> (dank:dejs-soft:format (need (de-json:html '{"leaf":"foo"}'))) +> (dank:dejs-soft:format (need (de:json:html '{"leaf":"foo"}'))) [~ [%leaf p="foo"]] -> ~(ram re (need (dank:dejs-soft:format (need (de-json:html '{"leaf":"foo"}'))))) +> ~(ram re (need (dank:dejs-soft:format (need (de:json:html '{"leaf":"foo"}'))))) "foo" ``` @@ -888,19 +888,19 @@ First, let's store some objects containing key-value pairs: ``` > =obj-1 o+(malt ['foo' s+'hello']~) -> (crip (en-json:html obj-1)) +> (en:json:html obj-1) '{"foo":"hello"}' ``` ``` > =obj-2 o+(malt ~[['bar' a+~[n+'123' n+'456']]]) -> (crip (en-json:html obj-2)) +> (en:json:html obj-2) '{"bar":[123,456]}' ``` ``` > =obj-3 o+(malt ['baz' b+&]~) -> (crip (en-json:html obj-3)) +> (en:json:html obj-3) '{"baz":true}' ``` @@ -1055,7 +1055,7 @@ A `(unit (map @t [type]))`, where `[type]` is the type produced by `wit`. The `u #### Examples ``` -> =jon (need (de-json:html '{"foo":"aaa","bar":"bbb"}')) +> =jon (need (de:json:html '{"foo":"aaa","bar":"bbb"}')) > ((om so):dejs-soft:format jon) [~ {[p='bar' q='bbb'] [p='foo' q='aaa']}] ``` @@ -1102,7 +1102,7 @@ A `(unit (map [a] [b]))` where `[a]` is the type produced by `fel` and `[b]` is #### Examples ``` -> =jon (need (de-json:html '{"123":"aaa","456":"bbb"}')) +> =jon (need (de:json:html '{"123":"aaa","456":"bbb"}')) > ((op dem so):dejs-soft:format jon) [~ u={[p=456 q='bbb'] [p=123 q='aaa']}] ``` diff --git a/content/reference/hoon/zuse/2e_2-3.md b/content/reference/hoon/zuse/2e_2-3.md index 6b04851..aa7398f 100644 --- a/content/reference/hoon/zuse/2e_2-3.md +++ b/content/reference/hoon/zuse/2e_2-3.md @@ -2,8 +2,29 @@ title = "2e(2-3): Print & Parse JSON" weight = 50 +[glossaryEntry."Print JSON"] +name = "Print JSON" +symbol = "en:json:html" +usage = "zuse" +slug = "#enjsonhtml" +desc = "Used in the Zuse library." + [glossaryEntry."Parse JSON"] name = "Parse JSON" +symbol = "de:json:html" +usage = "zuse" +slug = "#dejsonhtml" +desc = "Used in the Zuse library." + +[glossaryEntry."Parse JSON (deprecated)"] +name = "Parse JSON (deprecated)" +symbol = "de-json:html" +usage = "zuse" +slug = "#de-jsonhtml" +desc = "Used in the Zuse library." + +[glossaryEntry."Print JSON (deprecated)"] +name = "Print JSON (deprecated)" symbol = "en-json:html" usage = "zuse" slug = "#en-jsonhtml" @@ -11,10 +32,456 @@ desc = "Used in the Zuse library." +++ +## `++json:html` + +Container core for JSON decoding/encoding functions. + +### `++en:json:html` + +Print json + +Encode `jon`, a `json` structure, in a `cord`. + +#### Accepts + +`jon` is any `json`. + +#### Produces + +A `cord`. + +#### Source + +```hoon {% mode="collapse" %} +++ en :: encode JSON to tape + ~% %en +>+ ~ + |^ |= jon=^json + ^- cord + (rap 3 (flop (onto jon ~))) + :: :: ++onto:en:json:html + ++ onto + |= [val=^json out=(list @t)] + ^+ out + ?~ val ['null' out] + ?- -.val + %a + ?~ p.val ['[]' out] + =. out ['[' out] + !. + |- ^+ out + =. out ^$(val i.p.val) + ?~(t.p.val [']' out] $(p.val t.p.val, out [',' out])) + :: + %b + [?:(p.val 'true' 'false') out] + :: + %n + [p.val out] + :: + %s + [(scap p.val) out] + :: + %o + =/ viz ~(tap by p.val) + ?~ viz ['{}' out] + =. out ['{' out] + !. + |- ^+ out + =. out ^$(val q.i.viz, out [':' [(scap p.i.viz) out]]) + ?~(t.viz ['}' out] $(viz t.viz, out [',' out])) + == + :: :: ++scap:en:json:html + ++ scap + |= val=@t + ^- @t + =/ out=(list @t) ['"' ~] + =/ len (met 3 val) + =| [i=@ud pos=@ud] + |- ^- @t + ?: =(len i) + (rap 3 (flop ['"' (rsh [3 pos] val) out])) + =/ car (cut 3 [i 1] val) + ?: ?& (gth car 0x1f) + !=(car 0x22) + !=(car 0x5C) + !=(car 0x7F) + == + $(i +(i)) + =/ cap + ?+ car (crip '\\' 'u' ((x-co 4):co car)) + %10 '\\n' + %'"' '\\"' + %'\\' '\\\\' + == + $(i +(i), pos +(i), out [cap (cut 3 [pos (sub i pos)] val) out]) + -- ::en +``` + +#### Examples + +``` +> (en:json:html s+'foo') +'"foo"' +``` + +``` +> (en:json:html o+(malt (limo ~[['foo' s+'xxx'] ['bar' n+'123'] ['baz' b+&]]))) +'{"foo":"xxx","baz":true,"bar":123}' +``` + + +--- + +### `++de:json:html` + +Parse json + +Decode `txt`, a `cord` of stringified JSON, to a `(unit json)` which is null if +parsing failed. + +#### Accepts + +`txt` is a `cord`. + +#### Produces + +A `(unit json)`. + +#### Source + +```hoon {% mode="collapse" %} +++ de :: parse cord to JSON + ~% %de +>+ ~ + |^ |= txt=cord + ^- (unit ^json) + (rush txt apex) + :: :: ++abox:de-json:html + ++ abox :: array + %+ stag %a + (ifix [sel (wish ser)] (more (wish com) apex)) + :: :: ++apex:de-json:html + ++ apex :: any value + %+ knee *^json |. ~+ + %+ ifix [spac spac] + ;~ pose + (cold ~ (jest 'null')) + (stag %b bool) + (stag %s stri) + (cook |=(s=tape [%n p=(rap 3 s)]) numb) + abox + obox + == + :: :: ++bool:de-json:html + ++ bool :: boolean + ;~ pose + (cold & (jest 'true')) + (cold | (jest 'false')) + == + :: :: ++esca:de-json:html + ++ esca :: escaped character + ;~ pfix bas + =* loo + =* lip + ^- (list (pair @t @)) + [b+8 t+9 n+10 f+12 r+13 ~] + =* wow + ^~ + ^- (map @t @) + (malt lip) + (sear ~(get by wow) low) + ;~(pose doq fas bas loo unic) + == + :: :: ++expo:de-json:html + ++ expo :: exponent + ;~ (comp weld) + (piec (mask "eE")) + (mayb (piec (mask "+-"))) + (plus nud) + == + :: :: ++frac:de-json:html + ++ frac :: fraction + ;~(plug dot (plus nud)) + :: :: ++jcha:de-json:html + ++ jcha :: string character + ;~(pose ;~(less doq bas (shim 32 255)) esca) + :: :: ++mayb:de-json:html + ++ mayb :: optional + |*(bus=rule ;~(pose bus (easy ~))) + :: :: ++numb:de-json:html + ++ numb :: number + ;~ (comp weld) + (mayb (piec hep)) + ;~ pose + (piec (just '0')) + ;~(plug (shim '1' '9') (star nud)) + == + (mayb frac) + (mayb expo) + == + :: :: ++obje:de-json:html + ++ obje :: object list + %+ ifix [(wish kel) (wish ker)] + (more (wish com) pear) + :: :: ++obox:de-json:html + ++ obox :: object + (stag %o (cook malt obje)) + :: :: ++pear:de-json:html + ++ pear :: key-value + ;~(plug ;~(sfix (wish stri) (wish col)) apex) + :: :: ++piec:de-json:html + ++ piec :: listify + |* bus=rule + (cook |=(a=@ [a ~]) bus) + :: :: ++stri:de-json:html + ++ stri :: string + %+ sear + |= a=cord + ?. (sune a) ~ + (some a) + (cook crip (ifix [doq doq] (star jcha))) + :: :: ++spac:de-json:html + ++ spac :: whitespace + (star (mask [`@`9 `@`10 `@`13 ' ' ~])) + :: :: ++unic:de-json:html + ++ unic :: escaped UTF16 + =* lob 0x0 + =* hsb 0xd800 + =* lsb 0xdc00 + =* hib 0xe000 + =* hil 0x1.0000 + |^ + %+ cook + |= a=@ + ^- @t + (tuft a) + ;~ pfix (just 'u') + ;~(pose solo pair) + == + ++ quad :: parse num from 4 hex + (bass 16 (stun [4 4] hit)) + ++ meat :: gen gate for sear: + |= [bot=@ux top=@ux flp=?] :: accept num in range, + |= sur=@ux :: optionally reduce + ^- (unit @) + ?. &((gte sur bot) (lth sur top)) + ~ + %- some + ?. flp sur + (sub sur bot) + ++ solo :: single valid UTF16 + ;~ pose + (sear (meat lob hsb |) quad) + (sear (meat hib hil |) quad) + == + ++ pair :: UTF16 surrogate pair + %+ cook + |= [hig=@ low=@] + ^- @t + :(add hil low (lsh [1 5] hig)) + ;~ plug + (sear (meat hsb lsb &) quad) + ;~ pfix (jest '\\u') + (sear (meat lsb hib &) quad) + == + == + -- + :: :: ++utfe:de-json:html + ++ utfe :: UTF-8 sequence + ;~ less doq bas + =* qua + %+ cook + |= [a=@ b=@ c=@ d=@] + (rap 3 a b c d ~) + ;~ pose + ;~ plug + (shim 241 243) + (shim 128 191) + (shim 128 191) + (shim 128 191) + == + ;~ plug + (just '\F0') + (shim 144 191) + (shim 128 191) + (shim 128 191) + == + ;~ plug + (just '\F4') + (shim 128 143) + (shim 128 191) + (shim 128 191) + == + == + =* tre + %+ cook + |= [a=@ b=@ c=@] + (rap 3 a b c ~) + ;~ pose + ;~ plug + ;~ pose + (shim 225 236) + (shim 238 239) + == + (shim 128 191) + (shim 128 191) + == + ;~ plug + (just '\E0') + (shim 160 191) + (shim 128 191) + == + ;~ plug + (just '\ED') + (shim 128 159) + (shim 128 191) + == + == + =* dos + %+ cook + |= [a=@ b=@] + (cat 3 a b) + ;~ plug + (shim 194 223) + (shim 128 191) + == + ;~(pose qua tre dos) + == + :: :: ++wish:de-json:html + ++ wish :: with whitespace + |*(sef=rule ;~(pfix spac sef)) + :: XX: These gates should be moved to hoon.hoon + :: :: ++sune:de-json:html + ++ sune :: cord UTF-8 sanity + |= b=@t + ^- ? + ?: =(0 b) & + ?. (sung b) | + $(b (rsh [3 (teff b)] b)) + :: :: ++sung:de-json:html + ++ sung :: char UTF-8 sanity + |^ |= b=@t + ^- ? + =+ len=(teff b) + ?: =(4 len) (quad b) + ?: =(3 len) (tres b) + ?: =(2 len) (dos b) + (lte (end 3 b) 127) + :: + ++ dos + |= b=@t + ^- ? + =+ :- one=(cut 3 [0 1] b) + two=(cut 3 [1 1] b) + ?& (rang one 194 223) + (cont two) + == + :: + ++ tres + |= b=@t + ^- ? + =+ :+ one=(cut 3 [0 1] b) + two=(cut 3 [1 1] b) + tre=(cut 3 [2 1] b) + ?& + ?| + ?& |((rang one 225 236) (rang one 238 239)) + (cont two) + == + :: + ?& =(224 one) + (rang two 160 191) + == + :: + ?& =(237 one) + (rang two 128 159) + == + == + :: + (cont tre) + == + :: + ++ quad + |= b=@t + ^- ? + =+ :^ one=(cut 3 [0 1] b) + two=(cut 3 [1 1] b) + tre=(cut 3 [2 1] b) + for=(cut 3 [3 1] b) + ?& + ?| + ?& (rang one 241 243) + (cont two) + == + :: + ?& =(240 one) + (rang two 144 191) + == + :: + ?& =(244 one) + (rang two 128 143) + == + == + :: + (cont tre) + (cont for) + == + :: + ++ cont + |= a=@ + ^- ? + (rang a 128 191) + :: + ++ rang + |= [a=@ bot=@ top=@] + ^- ? + ?> (lte bot top) + &((gte a bot) (lte a top)) + -- + :: XX: This +teff should overwrite the existing +teff + :: :: ++teff:de-json:html + ++ teff :: UTF-8 length + |= a=@t + ^- @ + =+ b=(end 3 a) + ?: =(0 b) + ?> =(`@`0 a) 0 + ?: (lte b 127) 1 + ?: (lte b 223) 2 + ?: (lte b 239) 3 + 4 + -- ::de +``` + +#### Examples + +``` +> (de:json:html '"foo"') +[~ [%s p='foo']] +``` + +``` +> (de:json:html '{"foo":"xxx","baz":true,"bar":123}') +[ ~ + [ %o + p + [ n=[p='baz' q=[%b p=%.y]] + l=[n=[p='bar' q=[%n p=~.123]] l={} r={}] + r=[n=[p='foo' q=[%s p='xxx']] l={} r={}] + ] + ] +] +``` + +--- + ## `++en-json:html` Print json +{% callout %} +**Deprecated:** use [`++en:json:html`](#enjsonhtml) instead. +{% callout %} + Encode `val`, a `json` structure, in a `tape`. #### Accepts @@ -27,7 +494,12 @@ A `tape`. #### Source -This is a very large function, refer to `zuse.hoon` for the source code. +```hoon +++ en-json + |= jon=^json + ^- tape + (trip (en:json jon)) +``` #### Examples @@ -47,6 +519,10 @@ This is a very large function, refer to `zuse.hoon` for the source code. Parse json +{% callout %} +**Deprecated:** use [`++de:json:html`](#dejsonhtml) instead. +{% callout %} + Parse cord `a` to a `json` structure. The result is wrapped in a unit which is null if parsing failed. #### Accepts @@ -59,7 +535,12 @@ A `(unit json)`. The `unit` is `~` if parsing failed. #### Source -This is a very large function, refer to `zuse.hoon` for the source code. +```hoon +++ en-json + |= jon=^json + ^- tape + (trip (en:json jon)) +``` #### Examples diff --git a/content/reference/nock/explanation.md b/content/reference/nock/explanation.md index 4fdb5a1..e595444 100644 --- a/content/reference/nock/explanation.md +++ b/content/reference/nock/explanation.md @@ -29,8 +29,8 @@ For instance, it's common to represent strings (or even whole text files) as atoms, arranging them LSB first - so "foo" becomes `0x6f6f66`. How do we know to print this as "foo", not `0x6f6f66`? We need external information - such as a Hoon type. Similarly, -other common atomic types - signed integers, floating point, etc -- are all straightforward to map into atoms. +other common atomic types - signed integers, floating point, etc - +are all straightforward to map into atoms. It's also important to note that, unlike Lisp, Nock cannot create cyclical data structures. It is normal and common for nouns in a diff --git a/content/reference/runtime/api.md b/content/reference/runtime/api.md index 1760ba4..946112f 100644 --- a/content/reference/runtime/api.md +++ b/content/reference/runtime/api.md @@ -5,7 +5,7 @@ weight = 4 Let's run through the `u3` modules one by one. All public functions are commented, but the comments may be cryptic. -### u3m: main control +## u3m: main control To start `u3`, run @@ -77,7 +77,7 @@ and asserts if it finds any leaks or incorrect refcounts. This tool is for debugging and long-term maintenance only; refcounts should never err. -### u3j: jets +## u3j: jets The jet system, `u3j`, is what makes `u3` and `nock` in any sense a useful computing environment. Except perhaps `u3a` (there is @@ -94,7 +94,7 @@ Indeed such a coupling would be wholly wrongtious and un-Urbit. But the jet system is not Hoon-specific. It is specific to nock runtime systems that use a design pattern we call a `core`. -#### u3j: core structure +### u3j: core structure A core is no more than a cell `[code data]`, in which a `code` is either a Nock formula or a cell of `code`s, and `data` is anything. @@ -186,7 +186,7 @@ payload to be `[sample static-core]`, or even `[sample core]`. Any such constraint would not be rich enough to handle Hoon, let alone other languages. -#### u3j: jet state +### u3j: jet state There are two fundamental rules of computer science: (1) every system is best understood through its state; (2) less state is @@ -303,7 +303,7 @@ the dying road. Reaping promotes anything we've learned about any battery that either (a) already existed in the outer road, or (b) is being saved to the outer road. -#### u3j: jet binding +### u3j: jet binding Jet binding starts with a `%fast` hint. (In Hoon, this is produced by the runes `~%`, for the general case, or `~/` @@ -589,7 +589,7 @@ For historical reasons, all internal jet code in `j/[a-f]` do not do this in new `g` jets! The new standard protocol is to transfer both arguments and results. -### u3a: allocation functions +## u3a: allocation functions `u3a` allocates on the current road (u3R). Its internal structures are uninteresting and typical of a naive allocator. @@ -677,7 +677,7 @@ threads within its own synchronization primitives - for this to work with `u3a_malloc()`, we'd have to introduce our own locks on the surface-level road (which might be a viable solution). -### u3n: nock execution +## u3n: nock execution The `u3n` routines execute Nock itself. On the inside, they have a surprising resemblance to the spec proper (the only interesting @@ -780,7 +780,7 @@ to a `fly` is a `++path`, just a list of text `span`. (Note that `tank` is overdesigned and due for replacement.) -What does a `toon` mean? Either your computation succeded (`[0 +What does a `toon` mean? Either your computation succeeded (`[0 noun]`, or could not finish because it blocked on one or more global paths (`[1 (list path)]`), or it exited with a stack trace (`[2 (list tank)]`). @@ -823,7 +823,7 @@ caller's exception layer. (Maintaining this illusion is slightly nontrivial.) Finally, `u3n_nock_an()` is a sandbox with a null namespace. -### u3e: persistence +## u3e: persistence The only `u3e` function you should need to call is `u3e_save()`, which saves the loom. As it can be restored on any platform, @@ -831,7 +831,7 @@ please make sure you don't have any state in the loom that is bound to your process or architecture - except for exceptions like the warm jet state, which is actively purged on reboot. -### u3r: reading nouns (weak) +## u3r: reading nouns (weak) As befits accessors they don't make anything, `u3r` noun reading functions always retain their arguments and their returns. They @@ -869,14 +869,14 @@ It's important to remember that `u3r_mug()`, which produces a noun as a lazy cache. There are a number of variants of `u3r_mug()` that can get you out of building unneeded nouns. -### u3x: reading nouns (bail) +## u3x: reading nouns (bail) `u3x` functions are like `u3r` functions, but instead of returning `u3_none` when (for instance) we try to take the head of an atom, they bail with `%exit`. In other words, they do what the same operation would do in Nock. -### u3h: hash tables. +## u3h: hash tables. We can of course use the Hoon `map` structure as an associative array. This is a balanced treap and reasonably fast. However, @@ -898,7 +898,7 @@ The only funky function is `u3h_gut()`, which unifies keys with `u3r_sung()`. As with all cases of `u3r_sung()`, this must be used with extreme caution. -### u3z: memoization +## u3z: memoization Connected to the `~+` rune in Hoon, via the Nock `%memo` hint, the memoization facility is a general-purpose cache. @@ -947,11 +947,11 @@ road, and goes away when it goes away. (In future, we may wish to promote keys/values which outlive the road, as we do with jet state.) There is no cache reclamation at present, so be careful. -### u3t: tracing and profiling. +## u3t: tracing and profiling. TBD. -### u3v: the Arvo kernel +## u3v: the Arvo kernel An Arvo kernel - or at least, a core that compiles with the Arvo interface - is part of the global `u3` state. What is an Arvo diff --git a/content/reference/runtime/c.md b/content/reference/runtime/c.md index 4b78154..d0358c5 100644 --- a/content/reference/runtime/c.md +++ b/content/reference/runtime/c.md @@ -13,7 +13,7 @@ says nothing interesting. But some of our idiosyncrasies go beyond convention. Yes, we've done awful things to C. Here's what we did and why we did. -### c3: integer types +## c3: integer types First, it's generally acknowledged that underspecified integer types are C's worst disaster. C99 fixed this, but the `stdint` @@ -64,7 +64,7 @@ An enormous number of motes are defined in `i/c/motes.h`. There is no reason to delete motes that aren't being used, or even to modularize the definitions. Keep them alphabetical, though. -### c3: variables and variable naming +## c3: variables and variable naming The C3 style uses Hoon style TLV variable names, with a quasi Hungarian syntax. This is weird, but works really well, as long @@ -93,7 +93,7 @@ c3_w wor_w; // 32-bit word Unlike in standard Hungarian, there is no change for pointer variables. C structure variables take a `_u` suffix. -### c3: loobeans +## c3: loobeans The code (from `defs.h`) tells the story: diff --git a/content/reference/runtime/cryptography.md b/content/reference/runtime/cryptography.md index b9b7e76..54078c1 100644 --- a/content/reference/runtime/cryptography.md +++ b/content/reference/runtime/cryptography.md @@ -16,7 +16,7 @@ moment, only libraries directly related to Ames are documented here, though we note that there are jets for other cryptographic functions such as the [SHA Hash Family](/reference/hoon/stdlib/3d) as well. -### Ed25519 {% #ed %} +## Ed25519 {% #ed %} Urbit implements [Ed25519](http://ed25519.cr.yp.to/) based on the SUPERCOP "ref10" implementation. Additionally there is key exchanging and scalar addition @@ -27,7 +27,7 @@ All code is pure ANSI C without any dependencies, except for the random seed generation which uses standard OS cryptography APIs (CryptGenRandom on Windows, `/dev/urandom` on nix). -### AES-SIV {% #aes %} +## AES-SIV {% #aes %} The library we utilize for AES-SIV is an [RFC5297](https://tools.ietf.org/html/rfc5297)-compliant C implementation of diff --git a/content/reference/runtime/nouns.md b/content/reference/runtime/nouns.md index 7a8e554..22512e8 100644 --- a/content/reference/runtime/nouns.md +++ b/content/reference/runtime/nouns.md @@ -6,7 +6,7 @@ The division between `c3` and `u3` is that you could theoretically imagine using `c3` as just a generic C environment. Anything to do with nouns is in `u3`. -### u3: a map of the system +## u3: a map of the system These are the symbols you'll need to know about to program in `u3`. All files listed below are found in the @@ -39,7 +39,7 @@ u3w[a-g] jets (retain, nock core) jets/w.h jets/[a-g]/*.c Additionally, various noun type definition are found in `pkg/noun/types.h`. -### u3: noun internals +## u3: noun internals A noun is a `u3_noun` - currently defined as a 32-bit `c3_w`. (This is zero-indexed so bit `31` is the high bit.) @@ -78,7 +78,7 @@ Also, the value `0xffffffff` is `u3_none`, which is never a valid noun. Use the type `u3_weak` to express that a noun variable may be `u3_none`. -### u3: reference counts +## u3: reference counts The only really essential thing you need to know about `u3` is how to handle reference counts. Everything else, you can skip @@ -108,7 +108,7 @@ look over your code again.) (You can gain or lose a direct atom. It does nothing.) -### u3: reference protocols +## u3: reference protocols **THIS IS THE MOST CRITICAL SECTION IN THE `u3` DOCUMENTATION.** @@ -178,7 +178,7 @@ In general, though, in most places it's not worth thinking about what your function does. There is a convention for it, which depends on where it is, not what it does. Follow the convention. -### u3: reference conventions +## u3: reference conventions The `u3` convention is that, unless otherwise specified, **all functions have transfer semantics** - with the exception of the @@ -190,7 +190,7 @@ If functions outside this set have retain semantics, they need to be commented, both in the `.h` and `.c` file, with `RETAIN` in all caps. Yes, it's this important. -### u3: system architecture +## u3: system architecture If you just want to tinker with some existing code, it might be enough to understand the above. If not, it's probably worth @@ -212,7 +212,7 @@ of a computer that never loses state and never fails, we: - can abort any event without damaging the permanent state. - snapshot the permanent state periodically, and/or prune logs. -### u3: the road model +## u3: the road model `u3` uses a memory design which I'm sure someone has invented somewhere before, because it's not very clever, but I've never @@ -305,7 +305,7 @@ roads - see below - this will become a thread-local variable.) Relative to `u3R`, `+` memory is called `junior` memory; `-` memory is `normal` memory; `~` is `senior` memory. -### u3: explaining the road model +## u3: explaining the road model But... why? @@ -344,13 +344,13 @@ should be discarded in one step by copying the results. Then, within the procedure, we can switch the allocator into `sand` mode, and stop tracking references at all. -### u3: rules for C programming +## u3: rules for C programming There are two levels at which we program in C: (1) above the interpreter; (2) within the interpreter or jets. These have separate rules which need to be respected. -### u3: rules above the interpreter +## u3: rules above the interpreter In its relations with Unix, Urbit follows a strict rule of "call me, I won't call you." We do of course call Unix system calls, @@ -368,7 +368,7 @@ You'd need to make the global road pointer, `u3R`, a thread-local variable instead. This seems perfectly practical, but we haven't done it because we haven't needed to. -### u3: rules within the interpreter +## u3: rules within the interpreter Within the interpreter, your code can run either in the surface road or in a deep road. You can test this by testing @@ -404,7 +404,7 @@ In deep execution, `c3_assert()` will issue an exception that queues an error event, complete with trace stack, on the Arvo event queue. Let's see how this happens. -### u3: exceptions +## u3: exceptions You produce an exception with @@ -456,7 +456,7 @@ remote node, render the stacktrace as a consequence of the user's action - even if its its direct cause was (for instance) a Unix SIGINT or SIGALRM. -### u3: C structures on the loom +## u3: C structures on the loom Normally, all data on the loom is nouns. Sometimes we break this rule just a little, though - eg, in the `u3h` hashtables. diff --git a/pages/hackathon.js b/pages/hackathon.js new file mode 100644 index 0000000..c5d8856 --- /dev/null +++ b/pages/hackathon.js @@ -0,0 +1,167 @@ +import Head from "next/head"; +import Link from "next/link"; +import React from "react"; +import { + Container, + Section, + SingleColumn, + TwoUp, + getPostBySlug, +} from "@urbit/foundation-design-system"; +import Meta from "../components/Meta"; +import Header from "../components/Header"; +import Footer from "../components/Footer"; + +// Probably should make a core component, just pulling from operators.urbit.org for this one +function GuideCard({ title, description, href, className }) { + return ( +
+
+
+

{title}

+ {description} +
+ + + Get Started + + +
+
+ ); +} + +export default function Hackathon({ search, featuredGroups }) { + const post = { + title: "Urbit Assembly Hackathon", + description: + "WIll you be the one who builds Urbit’s killer app? This is your chance.", + image: + "https://storage.googleapis.com/media.urbit.org/developers/hackathon/hackathon_logo.svg", + }; + const pairedGroups = pair(featuredGroups); + return ( + + + Urbit Assembly Hackathon 2023 + {Meta(post)} + + +
+
+
+

Urbit Foundation presents

+

Assembly Hackathon 2023

+ + Sponsored by Zorp + +
+ + +
+

+ Unleash your creativity and join the quest to build Urbit's killer app. This year’s Assembly Hackathon has the most substantial prize pool in Urbit history. We invite you to rise to the challenge, showcase your skills, and claim victory. +

+

+ In the first week you’ll enter as an individual and form a team, or register an existing team. Each team will get a mentor who guides you through the Hackathon, helps you solve issues, and keeps you on track. As you progress, they will provide regular meetings to ensure your product reaches its full potential. You'll have access to a network of UF team members, valuable resources, and experts from within the Urbit ecosystem. With a dedicated mentor by your side, workshops from our best developers, and a powerhouse team, all that's left for you to provide is the talent and determination. +

+

+ +

The Hackathon will start on August 28th and culminate on Demo Day at {" "} + + Urbit Week Lisboa + . +

+
+
+ + {/* Features */} +

Prizes

+ This year's Hackathon has the most substantial prize pool in Urbit history, with even more TBA. +
+ {/* Overall */} +
+
+

Best overall – 3 Stars + 4 unreleased Native Planet devices

+

+ On Demo Day the committee of judges will decide which product has the highest score overall. The criteria they will take into account are: innovation, UI design, user value, usage of Urbit, and completeness. +

+
+
+ {/* Zorp */} +
+
+

Best use of Zorp ZKVM – 2 Stars

+

+ This prize will be awarded to the most unique, compelling, and fleshed out application built that makes use of our zkvm as a core component. For this category you will get early access to Zorps ZKVM and hands-on help from their developers. +

+
+
+ {/* UI */} +
+
+

Best UI – 2 Stars + 3 unreleased Native Planet devices

+

+ The Best UI winner will be chosen based on visual appeal, user experience, clarity, responsiveness, and interactivity. +

+
+
+ {/* Composability */} +
+
+

Best Composability – 2 Stars + 3 unreleased Native Planet devices

+

+ A huge advantage of an Urbit personal server is app composability. Apps can be built to seamlessly interact with each other while keeping data local and private. This prize will be awarded to the product that makes best use of this by showing an innovative and useful integration with other Urbit services. +

+
+
+ {/* Hooner */} +
+
+

Rising Stars – 4 unreleased Native Planet devices

+

+ This prize will be given to the best product built by a team composed of at least 50% graduates of Hoon School since Assembly 2022. +

+
+
+ {/* Audience */} +
+
+

Audience Vote – 1 Star

+

+ The Demo Day audience will vote on their favorite. +

+
+
+ + + Sign up for the Assembly Hackathon + + +
+ +