From 1e0cd5ecc2e48641c5bda8de98a424988abd291e Mon Sep 17 00:00:00 2001 From: "James D. Amberger" Date: Tue, 21 Mar 2023 18:13:22 -0400 Subject: [PATCH 001/142] Correct discussion and example of `tishep` The existing example doesn't compile because it contains (add a b c) There's a change that I'm the one that wrote the broken example back in like 2017 but anyway, the example provided in this commit 1) compiles and 2) illustrates why we have these kinds of "inverted" runes. The discussion also needed a fix: > `=-` is just like `=+` but its subexpressions are reversed. `=-` looks > better than `=+` when the expression you're pinning to the subject is much > *larger* than the expression that uses it. --- content/reference/hoon/rune/tis.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 ``` --- From 1a89be869b65d86f92440d853c5dbce5396b81f2 Mon Sep 17 00:00:00 2001 From: "James D. Amberger" Date: Tue, 21 Mar 2023 20:04:23 -0400 Subject: [PATCH 002/142] Discussion of `censig` with first leg as `.` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As seen in various gall agents, you can write `~(. core sample)` to produce a "pre-slammed" version of a door. This is basically not documented in the rune reference—hence the present commit. --- content/reference/hoon/rune/cen.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/content/reference/hoon/rune/cen.md b/content/reference/hoon/rune/cen.md index daca6ef..6d0dc32 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 ``` --- @@ -579,6 +579,10 @@ door itself. `c` is the sample of the door. `%~` 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 ``` --- From 26b52f692bc509f4222e2ae72fbd0dd35fcec13e Mon Sep 17 00:00:00 2001 From: Sidnym Ladrut Date: Fri, 10 Mar 2023 20:56:54 +0000 Subject: [PATCH 003/142] chat-guide: revise to use 'create-landscape-app' and new %hut source snippets --- content/guides/quickstart/chat-guide.md | 408 ++++++++++++------------ 1 file changed, 208 insertions(+), 200 deletions(-) diff --git a/content/guides/quickstart/chat-guide.md b/content/guides/quickstart/chat-guide.md index 8689ecc..6382df2 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`) @@ -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 :: @@ -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 +result 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 415]" > sys.kelvin ``` We also need to specify which agents to start when our desk is installed. We do @@ -1644,7 +1652,7 @@ delete those files and copy in 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/ ``` From fe1b346493adae0eda7512a1c7196b13abfe46ee Mon Sep 17 00:00:00 2001 From: Sidnym Ladrut Date: Wed, 15 Mar 2023 01:27:48 +0000 Subject: [PATCH 004/142] app-school-ii: revise to use 'create-landscape-app' and new %journal source snippets; add notes on modern react --- .../app-school-full-stack/6-react-setup.md | 172 ++++----- .../core/app-school-full-stack/7-app-logic.md | 358 ++++++++++-------- 2 files changed, 273 insertions(+), 257 deletions(-) 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..c72350c 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,143 +10,116 @@ 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. - -## Additional tweaks - -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`: - -```json -"homepage": "/apps/journal/", -``` - -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: - -```js -window.ship = "sampel-palnet"; -``` - -`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`: - -``` - -``` +This command will install the Urbit interface package (i.e. `@urbit/http-api`) +and a handful of other packages for the UI components (e.g. `react-bootstrap`, +`react-bottom-scroll-listener`, `react-day-picker`). The remainder of this +tutorial will focus primarily on how the former is used to communicate with a +live ship from within a React application. ## 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`: +With everything now set up, we can begin work on the app itself. In this case +we'll just edit the `src/app.jsx` file. The first thing is to clear the content +of the file and then add the following import statements for the React +framework and the `Urbit` class: -```js +```javascript {% copy=true %} +import React, { useState, useEffect } from "react"; 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"; +```javascript {% copy=true %} 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 { + Modal, Card, Stack, Tab, Tabs, Toast, ToastContainer, + Button, Spinner, CloseButton, +} from "react-bootstrap"; import DayPickerInput from "react-day-picker/DayPickerInput"; -import endOfDay from "date-fns/endOfDay"; -import startOfDay from "date-fns/startOfDay"; +import { startOfDay, endOfDay } from "date-fns"; import { BottomScrollListener } from "react-bottom-scroll-listener"; ``` -Inside the existing `App` class: +Now we'll begin defining our components. For the purposes of this tutorial, +we'll focus on the primary `App` component, which is defined as follows: -```js -class App extends Component { +```javascript {% copy=true %} +export default function App() { + /* remainder of the source goes here */ +} ``` -...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`. +The first thing we'll define in our `App` component is its state. In modern +React, component state is defined using the +[`useState()`](https://beta.reactjs.org/reference/react/useState) hook, which +returns a pair of `[stateVariable, setStateVariableFunction]`. For now, we'll +just consider the `status` state variable: -```js -state = { - // ..... - status: null, - // ..... -}; +```javascript {% copy=true %} +const [status, setStatus] = useState(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: +Next, we'll set up the `Urbit` API object in a +[`useEffect()`](https://beta.reactjs.org/reference/react/useEffect) call, which +allows the connection to be established *exactly once* after the initial +content of the page is rendered. Since the connection itself is independent of +the component state, we could do this outside of the `App` component; however, +in this case, we choose to put it in a component `useEffect()` so all the setup +code is together: -```js -componentDidMount() { +```javascript {% copy=true %} +useEffect(() => { 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(); -}; + + window.urbit.onOpen = () => setStatus("con"); + window.urbit.onRetry = () => setStatus("try"); + window.urbit.onError = () => setStatus("err"); + + init(); +}, []); ``` The first thing we do is create a new instance of the `Urbit` class we imported @@ -164,16 +137,17 @@ is mandatory. Therefore, we call the class contructor with just the empty `url` string: -```js +```javascript 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: +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 `window.urbit.ship` to this value: -```js +```javascript window.urbit.ship = window.ship; ``` @@ -195,8 +169,8 @@ leaving connection problems unhandled is usually a bad idea. The last thing we do is call: -```js -this.init(); +```javascript +init(); ``` This function will fetch initial entries and subscribe for updates. We'll look 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..7e3c1fa 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 @@ -12,23 +12,27 @@ object we previously setup, and ignore UI components and other helper functions. In the previous section we just mentioned the connection `status` field of our state. Here's the full state of our App: -```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. @@ -37,39 +41,40 @@ We'll see how these are used subsequently. The first thing our app does is call `init()`: -```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 -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. +entries; then, if that succeeded, it publishes this update with `setSubEvent()` +and `setLatestUpdate()` invocations 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. ## Getting entries ![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 +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 +98,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 +129,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 +143,119 @@ 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. 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) { + setEntries(entries.concat(subEvent.entries)); + } 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 +278,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 +321,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 +398,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,11 +415,11 @@ 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, +`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. @@ -403,3 +437,11 @@ 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. + +- [Modern React + Tutorial](https://beta.reactjs.org/learn/tutorial-tic-tac-toe) - A tutorial + walking through the basics of writing a modern React application. + + +[`usestate()`]: https://beta.reactjs.org/reference/react/useState +[`useeffect()`]: https://beta.reactjs.org/reference/react/useEffect From d1286f23ccfc54b1b5e912b2c22172e7c4ab6a74 Mon Sep 17 00:00:00 2001 From: Sidnym Ladrut Date: Wed, 22 Mar 2023 19:46:32 +0000 Subject: [PATCH 005/142] app-school-ii: change p6 to a simple react review and p7 to an urbit/http-api review --- .../app-school-full-stack/6-react-setup.md | 243 +++++++++++------- .../core/app-school-full-stack/7-app-logic.md | 52 ++-- 2 files changed, 177 insertions(+), 118 deletions(-) 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 c72350c..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 @@ -50,109 +50,73 @@ npm i ``` This command will install the Urbit interface package (i.e. `@urbit/http-api`) -and a handful of other packages for the UI components (e.g. `react-bootstrap`, -`react-bottom-scroll-listener`, `react-day-picker`). The remainder of this -tutorial will focus primarily on how the former is used to communicate with a -live ship from within a React application. +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. -## Basic API setup +## Basic app setup -With everything now set up, we can begin work on the app itself. In this case -we'll just edit the `src/app.jsx` file. The first thing is to clear the content -of the file and then add the following import statements for the React -framework and the `Urbit` class: +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. -```javascript {% copy=true %} -import React, { useState, useEffect } from "react"; -import Urbit from "@urbit/http-api"; +### 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'; ``` -We also need to import a few other things, mostly relating to UI components (but -these aren't important for our purposes here): +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. -```javascript {% copy=true %} -import "bootstrap/dist/css/bootstrap.min.css"; -import "react-day-picker/lib/style.css"; -import { - Modal, Card, Stack, Tab, Tabs, Toast, ToastContainer, - Button, Spinner, CloseButton, -} from "react-bootstrap"; -import DayPickerInput from "react-day-picker/DayPickerInput"; -import { startOfDay, endOfDay } from "date-fns"; -import { BottomScrollListener } from "react-bottom-scroll-listener"; +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; ``` -Now we'll begin defining our components. For the purposes of this tutorial, -we'll focus on the primary `App` component, which is defined as follows: - -```javascript {% copy=true %} -export default function App() { - /* remainder of the source goes here */ -} -``` - -The first thing we'll define in our `App` component is its state. In modern -React, component state is defined using the -[`useState()`](https://beta.reactjs.org/reference/react/useState) hook, which -returns a pair of `[stateVariable, setStateVariableFunction]`. For now, we'll -just consider the `status` state variable: - -```javascript {% copy=true %} -const [status, setStatus] = useState(null); -``` - -Next, we'll set up the `Urbit` API object in a -[`useEffect()`](https://beta.reactjs.org/reference/react/useEffect) call, which -allows the connection to be established *exactly once* after the initial -content of the page is rendered. Since the connection itself is independent of -the component state, we could do this outside of the `App` component; however, -in this case, we choose to put it in a component `useEffect()` so all the setup -code is together: - -```javascript {% copy=true %} -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"); - - 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. -```javascript -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; 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 `window.urbit.ship` to this value: - -```javascript -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 @@ -162,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 + +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 -init(); +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`. @@ -188,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 7e3c1fa..df55b59 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,14 +3,21 @@ 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: ```javascript // Control/Meta State // @@ -39,7 +46,9 @@ 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: ```javascript const init = () => { @@ -59,17 +68,17 @@ const init = () => { This function just calls `getEntries()` to retrieve the initial list of journal entries; then, if that succeeded, it publishes this update with `setSubEvent()` -and `setLatestUpdate()` invocations 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. +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. ## Getting entries ![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 +before the oldest we currently have. We call this initially and then each time the user scrolls to the bottom of the list. ```javascript @@ -164,8 +173,10 @@ major parts: ``` The key piece of this architecture is the [`useEffect()`] trigger, which is -called whenever an event comes in on the subscription wire. In our application, -this hook is also triggered by calls to [`getEntries()`](#getting-entries) and +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. @@ -420,13 +431,16 @@ expression, where the success case passes each update retrieved to the 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. +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`. @@ -438,10 +452,6 @@ the needs of your app. code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The source code for the `@urbit/http-api` NPM package. -- [Modern React - Tutorial](https://beta.reactjs.org/learn/tutorial-tic-tac-toe) - A tutorial - walking through the basics of writing a modern React application. - -[`usestate()`]: https://beta.reactjs.org/reference/react/useState -[`useeffect()`]: https://beta.reactjs.org/reference/react/useEffect +[`usestate()`]: https://react.dev/reference/react/useState +[`useeffect()`]: https://react.dev/reference/react/useEffect From 74da887048b18e188c44ab57bc5ff5cae18d6d37 Mon Sep 17 00:00:00 2001 From: Sidnym Ladrut Date: Wed, 22 Mar 2023 20:18:56 +0000 Subject: [PATCH 006/142] app-school-ii: add source link for react files; update terminal to use copy overlays and /path/to/ over ~ --- .../core/app-school-full-stack/8-desk.md | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) 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..447799e 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: -``` +``` {% copy=true %} [%zuse 417] ``` The `desk.docket-0` file is slightly more complicated: -``` +``` {% copy=true %} :~ title+'Journal' info+'Dear diary...' @@ -107,24 +107,22 @@ 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: -```sh -rm -r . +```sh {% copy=true %} +rm -rI /path/to/zod/journal/* ``` Apart from the kernel and standard library, desks need to be totally @@ -134,28 +132,28 @@ For example, since our app contains a number of `.hoon` files, we need the 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: -```sh +```sh {% copy=true %} git clone https://github.com/urbit/urbit.git urbit-git ``` If we navigate to the `pkg` directory in the cloned 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: -```sh +```sh {% copy=true %} ./symbolic-merge.sh base-dev journal ./symbolic-merge.sh garden-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 @@ -163,22 +161,25 @@ contain symlinks to files in the actual `arvo` and `garden` folders. 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 +187,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 +209,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 ``` From 1acb55f6564d0a583b7be1f6f43f48abdd7c9587 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Thu, 23 Mar 2023 10:21:24 -0500 Subject: [PATCH 007/142] Typo --- content/reference/arvo/clay/using.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/arvo/clay/using.md b/content/reference/arvo/clay/using.md index cf531e4..54d649d 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 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. From 319bdaf75caea2b149e4b7d266e8a2dcab516477 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Thu, 23 Mar 2023 10:21:39 -0500 Subject: [PATCH 008/142] Typo --- content/reference/arvo/clay/using.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/arvo/clay/using.md b/content/reference/arvo/clay/using.md index 54d649d..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 revision. 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. From d53df92f0bd8d3a902b8b4d90b16b89bb2dc1ebf Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Tue, 28 Mar 2023 20:59:35 +1300 Subject: [PATCH 009/142] add %fake scry to jael docs --- content/reference/arvo/jael/scry.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) 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`. From 4d49cccd1af2e541df161b6e69bc71a6e32f18d1 Mon Sep 17 00:00:00 2001 From: Sidnym Ladrut Date: Tue, 28 Mar 2023 18:25:03 +0000 Subject: [PATCH 010/142] chat-guide: fix grammatical error, 'result files' -> 'resulting files' --- content/guides/quickstart/chat-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/quickstart/chat-guide.md b/content/guides/quickstart/chat-guide.md index 6382df2..6bcd038 100644 --- a/content/guides/quickstart/chat-guide.md +++ b/content/guides/quickstart/chat-guide.md @@ -1395,7 +1395,7 @@ export function App() { ``` After we've finished writing our React app, we can build it and view the -result files in the `dist` directory: +resulting files in the `dist` directory: ```shell npm run build From 3cb76a8ca6ad29054c95f08091618c22e6a2cda3 Mon Sep 17 00:00:00 2001 From: djoio <47397180+djoio@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:15:19 -0400 Subject: [PATCH 011/142] Update ket.md fixed example, old one didn't run --- content/reference/hoon/rune/ket.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/hoon/rune/ket.md b/content/reference/hoon/rune/ket.md index 5eae376..b4ab114 100644 --- a/content/reference/hoon/rune/ket.md +++ b/content/reference/hoon/rune/ket.md @@ -376,7 +376,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 From a65a69968e561a9fac1462ac474c667a60eea9ac Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sun, 2 Apr 2023 20:01:28 +1200 Subject: [PATCH 012/142] add udon guide --- content/guides/additional/udon.md | 179 ++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 content/guides/additional/udon.md 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. From 2a6cda1279d5db3ab3513f983e2364052d0b9858 Mon Sep 17 00:00:00 2001 From: Sidnym Ladrut Date: Mon, 3 Apr 2023 18:00:56 +0000 Subject: [PATCH 013/142] app-school-ii: fix source refs to reflect final code after last-second bug fixes --- .../guides/core/app-school-full-stack/7-app-logic.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 df55b59..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 @@ -208,7 +208,16 @@ useEffect(() => { if (subEvent.time !== latestUpdate) { if ("entries" in subEvent) { - setEntries(entries.concat(subEvent.entries)); + // 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); From 341233c3c03cccd0173dc76ebc7cc46ea5616617 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 3 Apr 2023 16:50:47 -0500 Subject: [PATCH 014/142] Clarify about `~[]` list format. --- content/guides/core/hoon-school/F-cores.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/core/hoon-school/F-cores.md b/content/guides/core/hoon-school/F-cores.md index 0904685..6f2926f 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`.) Two tools that may help: From add9dd17995ba97300e0148076731de6534e15ed Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 3 Apr 2023 16:52:08 -0500 Subject: [PATCH 015/142] Clarify ~[] syntax. --- content/guides/core/hoon-school/F-cores.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/core/hoon-school/F-cores.md b/content/guides/core/hoon-school/F-cores.md index 6f2926f..a9b5fd5 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 ~]`. (A list is structurally a null-terminated tuple, or rightwards-branching cell ending in `~` or `0`.) +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: From e5c0145472591bfda2dc1adf6d876052ddb3816b Mon Sep 17 00:00:00 2001 From: Leo Winiecki Date: Tue, 4 Apr 2023 13:09:13 -0700 Subject: [PATCH 016/142] typo --- content/reference/nock/explanation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 12f02fd04f556a5414db2011c2006050227e9e34 Mon Sep 17 00:00:00 2001 From: "James D. Amberger" Date: Wed, 5 Apr 2023 22:01:52 -0400 Subject: [PATCH 017/142] Minor usage edit --- content/reference/hoon/rune/cen.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/reference/hoon/rune/cen.md b/content/reference/hoon/rune/cen.md index 6d0dc32..a97de46 100644 --- a/content/reference/hoon/rune/cen.md +++ b/content/reference/hoon/rune/cen.md @@ -795,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: From db5ab703de53e0c59942e22959a904656bea2a4c Mon Sep 17 00:00:00 2001 From: "James D. Amberger" Date: Wed, 5 Apr 2023 21:42:43 -0400 Subject: [PATCH 018/142] Improve fake expansion of `%-` found in `%=` docs The discussion of `%-` in the `%=` section is very valuable but the macro expansion given there has two deficiencies: 1) it is fake, and more importantly 2) someone relying on it could get bitten by the following: ```hoon =+ a=5 =+ add => %= +2 +6 [a 9] == $ ``` This twig produces 9, not 14, because the `a` there resolves to the head of the sample of `+add`, whereas the supposedly equivalent `(add a 9):[a=5 .]` does produce the expected result of 14. --- content/reference/hoon/rune/cen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/hoon/rune/cen.md b/content/reference/hoon/rune/cen.md index a97de46..a396705 100644 --- a/content/reference/hoon/rune/cen.md +++ b/content/reference/hoon/rune/cen.md @@ -805,7 +805,7 @@ Here's that again in tall form: ```hoon =+ foo => %= +2 - +6 baz + +6 baz:+3 == $ ``` From 78a93aa9f41a5b4cb7824d522dc95f82af7240db Mon Sep 17 00:00:00 2001 From: Gordon Date: Fri, 7 Apr 2023 10:56:40 -0700 Subject: [PATCH 019/142] update hoon school course page and hsl c2a --- content/courses/hsl.md | 5 ++--- content/highlights/highlight-1.md | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/content/courses/hsl.md b/content/courses/hsl.md index ee90ef9..efe8268 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 = "June, 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,8 +19,7 @@ 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 next cohort will begin in June 2023. - [Complete this form](https://forms.gle/kgiPckuHaVtJng9r5) to sign up for the next Hoon School Live. diff --git a/content/highlights/highlight-1.md b/content/highlights/highlight-1.md index 253135f..2061e87 100644 --- a/content/highlights/highlight-1.md +++ b/content/highlights/highlight-1.md @@ -1,6 +1,6 @@ +++ -title = "Hoon School Live Spring '23" +title = "Hoon School Live Summer '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." +description = "The next session of Hoon School Live begins in June. Sign up to learn the fundamentals of programming on Urbit." +++ \ No newline at end of file From 6079ca87973e22501f33af8504bdfb89960f8111 Mon Sep 17 00:00:00 2001 From: Gordon Date: Fri, 7 Apr 2023 11:33:59 -0700 Subject: [PATCH 020/142] updating highlight image --- content/highlights/highlight-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/highlights/highlight-1.md b/content/highlights/highlight-1.md index 2061e87..ece8422 100644 --- a/content/highlights/highlight-1.md +++ b/content/highlights/highlight-1.md @@ -1,6 +1,6 @@ +++ title = "Hoon School Live Summer '23" -image = "https://storage.googleapis.com/media.urbit.org/site/featured/hoon-school-march-23.png" +image = "https://storage.googleapis.com/media.urbit.org/site/featured/hsl-june-2023.png" url="https://developers.urbit.org/courses/hsl" description = "The next session of Hoon School Live begins in June. Sign up to learn the fundamentals of programming on Urbit." +++ \ No newline at end of file From 4e267d3c7e352025554f4008b4fced475c725735 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:14:25 -0500 Subject: [PATCH 021/142] Update hsl.md --- content/courses/hsl.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/content/courses/hsl.md b/content/courses/hsl.md index efe8268..6aaa388 100644 --- a/content/courses/hsl.md +++ b/content/courses/hsl.md @@ -1,7 +1,7 @@ +++ title = "Hoon School Live" weight = 100 -next_cohort = "June, 2023" +next_cohort = "June 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." +++ @@ -21,10 +21,9 @@ lessons. The next cohort will begin in June 2023. -- [Complete this form](https://forms.gle/kgiPckuHaVtJng9r5) to sign up for the next Hoon School Live. +- [Complete this form](https://forms.gle/9tBfJCB4D9MSHCmC9) 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? From 6775fd001907dbc5583775b6a4cad5acae764549 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Wed, 12 Apr 2023 15:16:29 +1200 Subject: [PATCH 022/142] clay: update %info nori comment & fix various links --- content/reference/arvo/clay/tasks.md | 55 +++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/content/reference/arvo/clay/tasks.md b/content/reference/arvo/clay/tasks.md index 2da03b2..3963d8b 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#riffclay) is null to cancel an existing +subscription, otherwise the [rave](/reference/arvo/clay/data-types#raveclay) 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#riotclay) 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#rantclay) 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#moodclay) will +determine what you can read and what type of data will be returned. See the +[care](/reference/arvo/clay/data-types#careclay) 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#caseclay) 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#moodclay) 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#moatclay) 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#noriclay) `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#aeonclay) (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#sobaclay) 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#misoclay) 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:clay`](/reference/arvo/clay/data-types#reinclay) 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. From cff36f4400bb57ad30053cf81cdd6fff23e5bc20 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Wed, 12 Apr 2023 17:57:35 +1200 Subject: [PATCH 023/142] ames: document %snub task and /ax/snubbed scry --- content/reference/arvo/ames/scry.md | 18 ++++++++++++++++++ content/reference/arvo/ames/tasks.md | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) 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..8ff15d9 100644 --- a/content/reference/arvo/ames/tasks.md +++ b/content/reference/arvo/ames/tasks.md @@ -148,6 +148,33 @@ 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 %} + ### `%spew` ```hoon From 7528591768afacd20ec7f63eabdb8c11b5ce4f0b Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Wed, 12 Apr 2023 19:01:44 +1200 Subject: [PATCH 024/142] update quickstart & full-stack desk setup instructions --- .../guides/core/app-school-full-stack/8-desk.md | 17 ++++++++--------- content/guides/quickstart/chat-guide.md | 8 ++++---- content/guides/quickstart/groups-guide.md | 9 ++++----- content/guides/quickstart/voting-guide.md | 9 ++++----- 4 files changed, 20 insertions(+), 23 deletions(-) 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 447799e..9ae1ee3 100644 --- a/content/guides/core/app-school-full-stack/8-desk.md +++ b/content/guides/core/app-school-full-stack/8-desk.md @@ -118,8 +118,7 @@ Now we can browse to it in the unix terminal: 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 {% copy=true %} rm -rI /path/to/zod/journal/* @@ -130,24 +129,25 @@ 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 {% 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 {% 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 {% 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: @@ -156,8 +156,7 @@ Now, we copy the contents of the new `journal` folder into our empty desk: 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: diff --git a/content/guides/quickstart/chat-guide.md b/content/guides/quickstart/chat-guide.md index 6bcd038..c2c03a9 100644 --- a/content/guides/quickstart/chat-guide.md +++ b/content/guides/quickstart/chat-guide.md @@ -1635,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: @@ -1647,8 +1647,8 @@ 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 %} diff --git a/content/guides/quickstart/groups-guide.md b/content/guides/quickstart/groups-guide.md index a898df2..117f37b 100644 --- a/content/guides/quickstart/groups-guide.md +++ b/content/guides/quickstart/groups-guide.md @@ -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..2eaeb57 100644 --- a/content/guides/quickstart/voting-guide.md +++ b/content/guides/quickstart/voting-guide.md @@ -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,9 +1945,8 @@ 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/* From e5e7f23904d57f96ef9b7b08ea435693ba1a1450 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Wed, 12 Apr 2023 19:04:26 +1200 Subject: [PATCH 025/142] bump kelvin in quickstart and fullstack guides --- content/guides/core/app-school-full-stack/8-desk.md | 2 +- content/guides/quickstart/chat-guide.md | 2 +- content/guides/quickstart/groups-guide.md | 2 +- content/guides/quickstart/voting-guide.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 9ae1ee3..0b77839 100644 --- a/content/guides/core/app-school-full-stack/8-desk.md +++ b/content/guides/core/app-school-full-stack/8-desk.md @@ -42,7 +42,7 @@ We only have one agent to start, so `desk.bill` is very simple: Likewise, `sys.kelvin` just contains: ``` {% copy=true %} -[%zuse 417] +[%zuse 414] ``` The `desk.docket-0` file is slightly more complicated: diff --git a/content/guides/quickstart/chat-guide.md b/content/guides/quickstart/chat-guide.md index c2c03a9..0f7a9ec 100644 --- a/content/guides/quickstart/chat-guide.md +++ b/content/guides/quickstart/chat-guide.md @@ -1595,7 +1595,7 @@ this by adding a `sys.kelvin` file to the root of our `hut` directory: ```shell {% copy=true %} cd hut -echo "[%zuse 415]" > sys.kelvin +echo "[%zuse 414]" > sys.kelvin ``` We also need to specify which agents to start when our desk is installed. We do diff --git a/content/guides/quickstart/groups-guide.md b/content/guides/quickstart/groups-guide.md index 117f37b..75f82a8 100644 --- a/content/guides/quickstart/groups-guide.md +++ b/content/guides/quickstart/groups-guide.md @@ -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 diff --git a/content/guides/quickstart/voting-guide.md b/content/guides/quickstart/voting-guide.md index 2eaeb57..6cc4263 100644 --- a/content/guides/quickstart/voting-guide.md +++ b/content/guides/quickstart/voting-guide.md @@ -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 From bb01d4b5a4de0327993d10fc8130f8cdb65b0408 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:38:54 -0500 Subject: [PATCH 026/142] Hotfix typo in Hoon code. --- content/guides/additional/cli-tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/cli-tutorial.md b/content/guides/additional/cli-tutorial.md index ccc6866..55799c0 100644 --- a/content/guides/additional/cli-tutorial.md +++ b/content/guides/additional/cli-tutorial.md @@ -78,7 +78,7 @@ The additional arms are described below. The Hoon code shows their expected type ```hoon ++ command-parser - |~ =sole-id + |= =sole-id |~(nail *(like [? command-type])) ``` From 9fc20104f0d7d4f6183f56db5464ae9007998d7c Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:47:37 -0500 Subject: [PATCH 027/142] Revert to iron gate expression. --- content/guides/additional/cli-tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/cli-tutorial.md b/content/guides/additional/cli-tutorial.md index 55799c0..ccc6866 100644 --- a/content/guides/additional/cli-tutorial.md +++ b/content/guides/additional/cli-tutorial.md @@ -78,7 +78,7 @@ The additional arms are described below. The Hoon code shows their expected type ```hoon ++ command-parser - |= =sole-id + |~ =sole-id |~(nail *(like [? command-type])) ``` From bfd382ab7fa84f229a93070200d75553b8ad57fe Mon Sep 17 00:00:00 2001 From: Yaseen <9275716+ynx0@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:14:44 -0400 Subject: [PATCH 028/142] Update nested-core-pattern.md Fix typo --- content/blog/nested-core-pattern.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/nested-core-pattern.md b/content/blog/nested-core-pattern.md index 745850a..9256a98 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 From f59559d999e9404ebf4773f2423b6c38e6d4a94c Mon Sep 17 00:00:00 2001 From: Yaseen <9275716+ynx0@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:15:25 -0400 Subject: [PATCH 029/142] Update api.md Fix typo --- content/reference/runtime/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/runtime/api.md b/content/reference/runtime/api.md index 1760ba4..d6e89d6 100644 --- a/content/reference/runtime/api.md +++ b/content/reference/runtime/api.md @@ -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)]`). From a73c41f1670a430759c420843447dbd30af3d4b5 Mon Sep 17 00:00:00 2001 From: Yaseen <9275716+ynx0@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:22:44 -0400 Subject: [PATCH 030/142] Update nested-core-pattern.md to use superior orthography --- content/blog/nested-core-pattern.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/nested-core-pattern.md b/content/blog/nested-core-pattern.md index 9256a98..7cc60e5 100644 --- a/content/blog/nested-core-pattern.md +++ b/content/blog/nested-core-pattern.md @@ -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] From a5db390aa4650de43ea0d441ad7ae309b63c609b Mon Sep 17 00:00:00 2001 From: Yaseen <9275716+ynx0@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:22:55 -0400 Subject: [PATCH 031/142] Update environment.md to use superior orthography --- content/guides/core/environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From 72608485d97de6f9d6c02aa0684468ad1d3a295e Mon Sep 17 00:00:00 2001 From: John Franklin Date: Sun, 23 Apr 2023 06:55:10 -0500 Subject: [PATCH 032/142] Fix endpoints in App Workbook %feature Pokes commentary --- content/guides/additional/app-workbook/feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/app-workbook/feature.md b/content/guides/additional/app-workbook/feature.md index c788c27..cd6281b 100644 --- a/content/guides/additional/app-workbook/feature.md +++ b/content/guides/additional/app-workbook/feature.md @@ -219,7 +219,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` From 0c08e489c069e93d3679ea199f135b37a0e3c01d Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:12:58 -0500 Subject: [PATCH 033/142] Hotfix Hoon School --- content/guides/core/hoon-school/D-gates.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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. From 0be6ae8a34f09ba6bbc61b01d8d287a160dee223 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:55:31 -0500 Subject: [PATCH 034/142] Create track7 demo agent for sole-effect. --- .../guides/additional/app-workbook/track7.md | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 content/guides/additional/app-workbook/track7.md diff --git a/content/guides/additional/app-workbook/track7.md b/content/guides/additional/app-workbook/track7.md new file mode 100644 index 0000000..57daf54 --- /dev/null +++ b/content/guides/additional/app-workbook/track7.md @@ -0,0 +1,169 @@ +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 +-- +``` From b1d6af7d520835ed3e21f98c4b73e9929cd25a2f Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:57:37 -0500 Subject: [PATCH 035/142] Create rpn.md --- content/guides/additional/app-workbook/rpn.md | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 content/guides/additional/app-workbook/rpn.md diff --git a/content/guides/additional/app-workbook/rpn.md b/content/guides/additional/app-workbook/rpn.md new file mode 100644 index 0000000..41ea86d --- /dev/null +++ b/content/guides/additional/app-workbook/rpn.md @@ -0,0 +1,186 @@ +## Tutorial: Building a CLI App + +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. From 7927b9b6859bb3c10ec1f90543516e63bceb26ba Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:58:30 -0500 Subject: [PATCH 036/142] Add TOML. --- content/guides/additional/app-workbook/rpn.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content/guides/additional/app-workbook/rpn.md b/content/guides/additional/app-workbook/rpn.md index 41ea86d..657e9ed 100644 --- a/content/guides/additional/app-workbook/rpn.md +++ b/content/guides/additional/app-workbook/rpn.md @@ -1,4 +1,7 @@ -## Tutorial: Building a CLI App ++++ +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: From 8851afe73b3e9e66d848c65ef3bd6606cf4cd8f7 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:59:23 -0500 Subject: [PATCH 037/142] Add TOML. --- content/guides/additional/app-workbook/track7.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/guides/additional/app-workbook/track7.md b/content/guides/additional/app-workbook/track7.md index 57daf54..17fdee7 100644 --- a/content/guides/additional/app-workbook/track7.md +++ b/content/guides/additional/app-workbook/track7.md @@ -1,3 +1,8 @@ ++++ +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 From 75ccae10d617c5ceee9c3755540e81a2430fc108 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:00:27 -0500 Subject: [PATCH 038/142] Rename w/o agent names --- content/guides/additional/app-workbook/ahoy.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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. From 3531cb47df606cb6adbef89b963c8917c6b026b7 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:00:56 -0500 Subject: [PATCH 039/142] Rename w/o agent names --- content/guides/additional/app-workbook/dbug.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 +++ From 8630c7be56b1b1a97a5da0ecba32438dde15abfd Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:01:43 -0500 Subject: [PATCH 040/142] Rename w/o agent names --- content/guides/additional/app-workbook/flap.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/content/guides/additional/app-workbook/flap.md b/content/guides/additional/app-workbook/flap.md index 10b709d..f2cb687 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. From 757ea1728372d0d587f99fa656a92b08a5f1547d Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:02:19 -0500 Subject: [PATCH 041/142] Rename w/o agent names --- content/guides/additional/app-workbook/feature.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/content/guides/additional/app-workbook/feature.md b/content/guides/additional/app-workbook/feature.md index c788c27..7e5716d 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. From 64618155347104e836430aa8a395bb7c901d8261 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:04:25 -0500 Subject: [PATCH 042/142] Rename index. --- content/guides/additional/app-workbook/_index.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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) From c0128abcc92a399c3961698490cb6316cfd1c6d8 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:30:14 -0500 Subject: [PATCH 043/142] Update wyt:by to say size instead of depth --- content/reference/hoon/stdlib/2i.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 2f672240fc4cb435b3842910ca32bee971555569 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 13:47:38 -0500 Subject: [PATCH 044/142] Create generators.md --- content/guides/additional/generators.md | 240 ++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 content/guides/additional/generators.md diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md new file mode 100644 index 0000000..32aea4a --- /dev/null +++ b/content/guides/additional/generators.md @@ -0,0 +1,240 @@ ++++ +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 argumetns + +`/gen/vats.hoon` is commonly used to check on the status of installed desks. It can be invoked with optional arguments: + +```hoon +> +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 Hood/Helm tooling like `|install` are generators automatically routed by Dojo to the correct agent. + +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). From a389ff1b40cc19adc7757982d065cd8707e60b21 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:04:25 -0500 Subject: [PATCH 045/142] Document wing resolution. --- content/reference/hoon/limbs/wing.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/content/reference/hoon/limbs/wing.md b/content/reference/hoon/limbs/wing.md index 6f36361..260abc6 100644 --- a/content/reference/hoon/limbs/wing.md +++ b/content/reference/hoon/limbs/wing.md @@ -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]]]] + ``` From 51a87fa71246db74b05bd4203b52ab799fab4964 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:11:12 -0500 Subject: [PATCH 046/142] Hotfix typo in bit width --- content/blog/literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From 8bdbbaf85ea13baae140100929f3b6b72c86e75b Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:17:22 -0500 Subject: [PATCH 047/142] Update content/guides/additional/generators.md Co-authored-by: tinnus-napbus <77721746+tinnus-napbus@users.noreply.github.com> --- content/guides/additional/generators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md index 32aea4a..ba256a3 100644 --- a/content/guides/additional/generators.md +++ b/content/guides/additional/generators.md @@ -105,7 +105,7 @@ The gate sample follows this pattern, with undesired elements stubbed out by `*` 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 argumetns +### Zero arguments `/gen/vats.hoon` is commonly used to check on the status of installed desks. It can be invoked with optional arguments: From 06be2992b8d1d5baab6f2f4ad2ce349179dbc1f8 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:17:39 -0500 Subject: [PATCH 048/142] Update content/guides/additional/generators.md Co-authored-by: tinnus-napbus <77721746+tinnus-napbus@users.noreply.github.com> --- content/guides/additional/generators.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md index ba256a3..a3058ca 100644 --- a/content/guides/additional/generators.md +++ b/content/guides/additional/generators.md @@ -223,8 +223,7 @@ 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 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. From f13591cb9f03f0d39213535bcdf645de5fd667d9 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:17:47 -0500 Subject: [PATCH 049/142] Update content/guides/additional/generators.md Co-authored-by: tinnus-napbus <77721746+tinnus-napbus@users.noreply.github.com> --- content/guides/additional/generators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md index a3058ca..7614257 100644 --- a/content/guides/additional/generators.md +++ b/content/guides/additional/generators.md @@ -109,7 +109,7 @@ the input arguments into a list (either the unnamed/required argument list or th `/gen/vats.hoon` is commonly used to check on the status of installed desks. It can be invoked with optional arguments: -```hoon +``` > +vats %base /sys/kelvin: [%zuse 414] From d891ad1e3efa6a2a8b274a4901d62003771445f5 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sat, 29 Apr 2023 19:01:50 +1200 Subject: [PATCH 050/142] fix header levels so ToC is correct --- .../guides/additional/app-workbook/ahoy.md | 2 -- .../guides/additional/app-workbook/feature.md | 2 -- .../guides/additional/app-workbook/flap.md | 2 -- content/guides/additional/aqua.md | 6 ++-- .../guides/additional/workbook/competitive.md | 28 +++++++++---------- .../workbook/gleichniszahlenreihe.md | 4 +-- content/guides/additional/workbook/rhonda.md | 2 +- content/guides/additional/workbook/roman.md | 6 ++-- .../guides/additional/workbook/solitaire.md | 6 ++-- .../core/app-school-full-stack/1-types.md | 8 +++--- .../core/app-school-full-stack/9-final.md | 14 +++++----- content/reference/additional/cryptography.md | 10 +++---- content/reference/arvo/clay/architecture.md | 6 ++-- content/reference/arvo/clay/clay.md | 4 +-- content/reference/arvo/concepts/scry.md | 18 ++++++------ content/reference/azimuth/flow.md | 14 +++++----- content/reference/azimuth/hd-wallet.md | 10 +++---- content/reference/azimuth/l2/l2-actions.md | 24 ++++++++-------- .../reference/azimuth/l2/roller-tutorial.md | 8 +++--- content/reference/hoon/auras.md | 8 +++--- content/reference/hoon/limbs/limb.md | 8 +++--- content/reference/hoon/limbs/wing.md | 8 +++--- content/reference/hoon/overview.md | 12 ++++---- content/reference/runtime/api.md | 28 +++++++++---------- content/reference/runtime/c.md | 6 ++-- content/reference/runtime/cryptography.md | 4 +-- content/reference/runtime/nouns.md | 26 ++++++++--------- 27 files changed, 132 insertions(+), 142 deletions(-) diff --git a/content/guides/additional/app-workbook/ahoy.md b/content/guides/additional/app-workbook/ahoy.md index 5d625be..7ee8496 100644 --- a/content/guides/additional/app-workbook/ahoy.md +++ b/content/guides/additional/app-workbook/ahoy.md @@ -3,8 +3,6 @@ title = "%ahoy Ship Monitoring" weight = 10 +++ -# `%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/feature.md b/content/guides/additional/app-workbook/feature.md index cd6281b..92b71f4 100644 --- a/content/guides/additional/app-workbook/feature.md +++ b/content/guides/additional/app-workbook/feature.md @@ -3,8 +3,6 @@ title = "%feature Page Hosting" weight = 160 +++ -# `%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. diff --git a/content/guides/additional/app-workbook/flap.md b/content/guides/additional/app-workbook/flap.md index 10b709d..1d1ccc9 100644 --- a/content/guides/additional/app-workbook/flap.md +++ b/content/guides/additional/app-workbook/flap.md @@ -3,8 +3,6 @@ title = "%flap JS Client" weight = 60 +++ -# `%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. 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/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/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/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/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/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/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/concepts/scry.md b/content/reference/arvo/concepts/scry.md index fa85b47..aee0c77 100644 --- a/content/reference/arvo/concepts/scry.md +++ b/content/reference/arvo/concepts/scry.md @@ -3,17 +3,17 @@ title = "Scries" weight = 40 +++ -### What is a 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 +25,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 +43,13 @@ 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? +## 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 +69,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/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/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/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..ebcffdf 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]] 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/runtime/api.md b/content/reference/runtime/api.md index d6e89d6..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 @@ -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. From d23725912ba749e1b0a0f4391c5315c6812b2e5d Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sat, 29 Apr 2023 22:58:20 +1200 Subject: [PATCH 051/142] fix csv example lib name conflict in marks guide --- content/reference/arvo/clay/marks/examples.md | 10 +++++----- .../reference/arvo/clay/marks/writing-marks.md | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) 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 |% From 55b160c8dfe14a82cfb38f85211a8a0d354bb310 Mon Sep 17 00:00:00 2001 From: jpfeiffer16 Date: Sun, 30 Apr 2023 07:48:57 -0400 Subject: [PATCH 052/142] Typo fix --- content/guides/core/hoon-school/K-doors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/core/hoon-school/K-doors.md b/content/guides/core/hoon-school/K-doors.md index 76a78a9..4fe761a 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? From 0d96a7ce1422319ffa27369fb36dc3991ac95bfe Mon Sep 17 00:00:00 2001 From: John Franklin Date: Sun, 30 Apr 2023 08:23:38 -0500 Subject: [PATCH 053/142] Correct signed/unsigned for parameters in stdlib 3b --- content/reference/hoon/stdlib/3b.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 From 1cd84908c3c3226618735b3407a0393004c8954f Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Mon, 1 May 2023 17:09:39 +1200 Subject: [PATCH 054/142] update de-json/en-json to en:json/de:json in all relevant places --- .../guides/additional/app-workbook/feature.md | 4 +- .../guides/additional/app-workbook/flap.md | 6 +- content/guides/additional/json-guide.md | 26 +- .../core/app-school-full-stack/3-json.md | 6 +- content/reference/arvo/threads/http-api.md | 4 +- content/reference/hoon/zuse/2d_1-5.md | 22 +- content/reference/hoon/zuse/2d_6.md | 12 +- content/reference/hoon/zuse/2d_7.md | 18 +- content/reference/hoon/zuse/2e_2-3.md | 485 +++++++++++++++++- 9 files changed, 532 insertions(+), 51 deletions(-) diff --git a/content/guides/additional/app-workbook/feature.md b/content/guides/additional/app-workbook/feature.md index 94da170..979986b 100644 --- a/content/guides/additional/app-workbook/feature.md +++ b/content/guides/additional/app-workbook/feature.md @@ -98,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) :: @@ -183,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) :: diff --git a/content/guides/additional/app-workbook/flap.md b/content/guides/additional/app-workbook/flap.md index f2cb687..b024b5d 100644 --- a/content/guides/additional/app-workbook/flap.md +++ b/content/guides/additional/app-workbook/flap.md @@ -335,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)) :: @@ -848,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)) :: @@ -1107,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/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/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/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/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 From 5b7ffd07583ddb05121d6c79a9aaac553763fa3f Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 1 May 2023 12:27:57 -0500 Subject: [PATCH 055/142] Update ket.md --- content/reference/hoon/rune/ket.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/content/reference/hoon/rune/ket.md b/content/reference/hoon/rune/ket.md index b4ab114..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=~]] ``` --- From d18178ee46d201190253edd64f279df5f63c4a78 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 1 May 2023 12:33:00 -0500 Subject: [PATCH 056/142] Update irregular.md --- content/reference/hoon/irregular.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/content/reference/hoon/irregular.md b/content/reference/hoon/irregular.md index a2e1acc..91cb90f 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 | From 807efff13731bf1e63408741802369cddd515285 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 1 May 2023 12:44:31 -0500 Subject: [PATCH 057/142] Update irregular.md --- content/reference/hoon/irregular.md | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/content/reference/hoon/irregular.md b/content/reference/hoon/irregular.md index 91cb90f..3e9f8a6 100644 --- a/content/reference/hoon/irregular.md +++ b/content/reference/hoon/irregular.md @@ -377,6 +377,69 @@ 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, lets you "borrow the sample" from a gate, + for instance. It is not commonly used.) + +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. From 0305c9b12f3cc01c590ae4c0a071d9113ee776a6 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 1 May 2023 12:46:02 -0500 Subject: [PATCH 058/142] Update irregular.md --- content/reference/hoon/irregular.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/content/reference/hoon/irregular.md b/content/reference/hoon/irregular.md index 3e9f8a6..f1e1d9d 100644 --- a/content/reference/hoon/irregular.md +++ b/content/reference/hoon/irregular.md @@ -414,6 +414,18 @@ See [%sand](/reference/hoon/rune/constants#warm) for other irregular definitions (`$;` bucmic, or manual value mode, lets you "borrow the sample" from a gate, for instance. 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. From 2499d87446a0f04a68b2c6c7ac36a531e9075a9c Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 1 May 2023 14:26:50 -0500 Subject: [PATCH 059/142] Per Joe Bryan --- content/reference/hoon/irregular.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/content/reference/hoon/irregular.md b/content/reference/hoon/irregular.md index f1e1d9d..e1332a3 100644 --- a/content/reference/hoon/irregular.md +++ b/content/reference/hoon/irregular.md @@ -412,8 +412,9 @@ See [%sand](/reference/hoon/rune/constants#warm) for other irregular definitions ] ``` - (`$;` bucmic, or manual value mode, lets you "borrow the sample" from a gate, - for instance. It is not commonly used.) + (`$;` 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: From 25cacb8ca7790a34ae7e5a8bb1c07a715fe6ccc9 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Wed, 3 May 2023 08:13:12 -0500 Subject: [PATCH 060/142] Update B-syntax.md --- content/guides/core/hoon-school/B-syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From fa04307343e24365fdab9e6d0ddbba0bc529c258 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Thu, 4 May 2023 01:20:32 +1200 Subject: [PATCH 061/142] document remote scries & add gall scry reference --- content/guides/additional/remote-scry.md | 192 ++++++++++++++++++ content/guides/core/app-school/10-scry.md | 82 ++------ content/reference/arvo/ames/tasks.md | 102 +++++++++- content/reference/arvo/concepts/scry.md | 4 + content/reference/arvo/gall/data-types.md | 7 + content/reference/arvo/gall/scry.md | 230 ++++++++++++++++++++++ content/reference/glossary/remote-scry.md | 32 +++ 7 files changed, 586 insertions(+), 63 deletions(-) create mode 100644 content/guides/additional/remote-scry.md create mode 100644 content/reference/arvo/gall/scry.md create mode 100644 content/reference/glossary/remote-scry.md diff --git a/content/guides/additional/remote-scry.md b/content/guides/additional/remote-scry.md new file mode 100644 index 0000000..df586d9 --- /dev/null +++ b/content/guides/additional/remote-scry.md @@ -0,0 +1,192 @@ ++++ +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. + +## 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 =path sign=@ux data=(unit page)] :: peek result + [%miss =path] :: peek dropped + ... + == +``` + +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 `[%tune =path sign=@ux data=(unit page)]` to the original + requester(s) when it knows the value on `path`. It includes a signature + `sign`. If `data` is `~`, then the path will *never* have a value. + +- Ames gives `[%miss =path]` if it wasn't able to produce the value on `path`. + This can happen if the publisher doesn't know the answer, or if the signature + verification fails. This does *not* imply that the same request will fail in + the future! + +## `-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 thread's own source code out of `~sampel`'s `%kids` desk, try it! + +``` +-keen [~sampel /c/x/1/kids/ted/keen/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/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/reference/arvo/ames/tasks.md b/content/reference/arvo/ames/tasks.md index 8ff15d9..6d2febd 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,8 @@ The `ships` field specifies the ships for which debug output is desired. This `task` returns no `gift`s. +--- + ### `%snub` ```hoon @@ -175,6 +189,12 @@ whole modified list and form in a new `%snub` `task`. {% /callout %} +#### Returns + +This `task` returns no `gift`s. + +--- + ### `%spew` ```hoon @@ -191,6 +211,8 @@ Sets verbosity toggles on debug output. This `task` is used internally when the This `task` returns no `gift`s. +--- + ### `%stir` ```hoon @@ -205,6 +227,8 @@ The `arg` field is unused. This `task` returns no `gift`s. +--- + ### `%vega` ```hoon @@ -217,3 +241,77 @@ 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 + +Either a `%tune` or `%miss` gift. A `%tune` gift looks like: + +```hoon +[%tune =path sign=@ux data=(unit page)] +``` + +It represents a *result*. The `sign` is a signature from the publisher and the +`data` is the result itself, which will be null if the requested path at the +given revision doesn't exist and will never exist (equivalent to the `[~ ~]` +case of an ordinary scry). + +A `%miss` gift looks like: + +```hoon +[%miss =path] +``` + +It represents a failure to produce the value at the given path. This can +happen if the publisher doesn't know the answer, or if the signature +verification fails. This does *not* imply the same request will fail in the +future, unlike a `%tune` with null `data`. This is equivalent to the `~` case +of an ordinary 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/concepts/scry.md b/content/reference/arvo/concepts/scry.md index aee0c77..a73d313 100644 --- a/content/reference/arvo/concepts/scry.md +++ b/content/reference/arvo/concepts/scry.md @@ -3,6 +3,10 @@ title = "Scries" weight = 40 +++ +{% callout %} + +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. diff --git a/content/reference/arvo/gall/data-types.md b/content/reference/arvo/gall/data-types.md index b85020f..0a9c694 100644 --- a/content/reference/arvo/gall/data-types.md +++ b/content/reference/arvo/gall/data-types.md @@ -218,6 +218,10 @@ types. $% [%agent [=ship name=term] =task] [%arvo note-arvo] [%pyre =tang] + :: + [%grow =spur =page] + [%tomb =case =spur] + [%cull =case =spur] == ``` @@ -251,6 +255,9 @@ vanes, or for sending out updates to subscribers. The three cases are: 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). diff --git a/content/reference/arvo/gall/scry.md b/content/reference/arvo/gall/scry.md new file mode 100644 index 0000000..1da8821 --- /dev/null +++ b/content/reference/arvo/gall/scry.md @@ -0,0 +1,230 @@ ++++ +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. + +--- + +### `%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/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. From 5def6f01f77b9c4081257b8366ab12c46ccfb438 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Mon, 8 May 2023 22:40:49 +1200 Subject: [PATCH 062/142] add %set-response task and cache-entry type to eyre ref --- content/reference/arvo/eyre/data-types.md | 62 +++++++++++++++++++++++ content/reference/arvo/eyre/tasks.md | 60 ++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/content/reference/arvo/eyre/data-types.md b/content/reference/arvo/eyre/data-types.md index 25d5ba3..a5ce531 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,6 +130,8 @@ 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 @@ -114,6 +148,8 @@ An unacknowledged event in the channel system. 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). +--- + ### `$binding` ```hoon @@ -125,6 +161,8 @@ 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 @@ -141,6 +179,8 @@ A `binding` is a rule to match a URL `path` and optional `site` domain which can The action to take when a [$binding](#binding) matches an incoming HTTP request. +--- + ### `$generator` ```hoon @@ -153,6 +193,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 +208,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 +221,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 +234,8 @@ This is for updating the server configuration. In the case of `%cert`, a `cert` A client IP address. +--- + ### `$inbound-request` ```hoon @@ -201,6 +249,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 +261,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 +280,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 +295,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 +308,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 +333,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 +346,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/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). + +--- From 50210017afc9d921cf2e0ffa75df8fc99402b563 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Tue, 9 May 2023 02:02:40 +1200 Subject: [PATCH 063/142] add short, low-level overview of eyre noun channels --- content/guides/additional/noun-channels.md | 102 +++++++++++++++++++++ content/reference/arvo/eyre/data-types.md | 56 +++++++---- 2 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 content/guides/additional/noun-channels.md 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/reference/arvo/eyre/data-types.md b/content/reference/arvo/eyre/data-types.md index 25d5ba3..e921f91 100644 --- a/content/reference/arvo/eyre/data-types.md +++ b/content/reference/arvo/eyre/data-types.md @@ -101,18 +101,35 @@ 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` @@ -128,15 +145,16 @@ A `binding` is a rule to match a URL `path` and optional `site` domain which can ### `$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. From 0fa0faca32425c989aa8da29ba288a3c61aea434 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Tue, 9 May 2023 03:02:32 +1200 Subject: [PATCH 064/142] remove %miss and update data types --- content/guides/additional/remote-scry.md | 22 ++-- content/reference/arvo/ames/data-types.md | 146 ++++++++++++++++++++++ content/reference/arvo/ames/tasks.md | 26 ++-- 3 files changed, 166 insertions(+), 28 deletions(-) diff --git a/content/guides/additional/remote-scry.md b/content/guides/additional/remote-scry.md index df586d9..5e24b21 100644 --- a/content/guides/additional/remote-scry.md +++ b/content/guides/additional/remote-scry.md @@ -135,8 +135,7 @@ With this, we're ready to look at all the new tasks to, and gifts from, Ames: :: +$ gift $% ... - [%tune =path sign=@ux data=(unit page)] :: peek result - [%miss =path] :: peek dropped + [%tune spar roar=(unit roar)] ... == ``` @@ -161,14 +160,17 @@ At this point, most of these should be very clear, but briefly: [%pass /call/back/path %arvo %a %wham ~sampel /g/x/4/test//foo] ``` -- Ames gives `[%tune =path sign=@ux data=(unit page)]` to the original - requester(s) when it knows the value on `path`. It includes a signature - `sign`. If `data` is `~`, then the path will *never* have a value. - -- Ames gives `[%miss =path]` if it wasn't able to produce the value on `path`. - This can happen if the publisher doesn't know the answer, or if the signature - verification fails. This does *not* imply that the same request will fail in - the future! +- 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` 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/tasks.md b/content/reference/arvo/ames/tasks.md index 6d2febd..497afc4 100644 --- a/content/reference/arvo/ames/tasks.md +++ b/content/reference/arvo/ames/tasks.md @@ -260,28 +260,18 @@ between the agent and the rest of the path. #### Returns -Either a `%tune` or `%miss` gift. A `%tune` gift looks like: +A `%tune` gift. A `%tune` gift looks like: ```hoon -[%tune =path sign=@ux data=(unit page)] +[%tune spar roar=(unit roar)] ``` -It represents a *result*. The `sign` is a signature from the publisher and the -`data` is the result itself, which will be null if the requested path at the -given revision doesn't exist and will never exist (equivalent to the `[~ ~]` -case of an ordinary scry). - -A `%miss` gift looks like: - -```hoon -[%miss =path] -``` - -It represents a failure to produce the value at the given path. This can -happen if the publisher doesn't know the answer, or if the signature -verification fails. This does *not* imply the same request will fail in the -future, unlike a `%tune` with null `data`. This is equivalent to the `~` case -of an ordinary scry. +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. --- From f2cd4ec8ec4992e3d8ba5cdd5c82c9fbb1175b6d Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 9 May 2023 09:42:14 -0500 Subject: [PATCH 065/142] Stop using the now-deprecated +vat generator. --- .../guides/core/hoon-school/H-libraries.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) 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: ~ ``` From 21ee8c3ed545c81cd6931e662c4eb0c7a9e1e73b Mon Sep 17 00:00:00 2001 From: hosted-fornet <79381743+hosted-fornet@users.noreply.github.com> Date: Wed, 10 May 2023 14:04:33 -0700 Subject: [PATCH 066/142] eyre: fix %connections scry description typo --- content/reference/arvo/eyre/scry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/reference/arvo/eyre/scry.md b/content/reference/arvo/eyre/scry.md index 4580e2b..d48007a 100644 --- a/content/reference/arvo/eyre/scry.md +++ b/content/reference/arvo/eyre/scry.md @@ -133,7 +133,7 @@ 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 %} From de55be5684426308702f7747f85591f5d30b56cc Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 12 May 2023 09:10:56 -0500 Subject: [PATCH 067/142] Document agent generators a bit more. h/t ~tinnus-napbus --- content/guides/additional/generators.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md index 7614257..5af938a 100644 --- a/content/guides/additional/generators.md +++ b/content/guides/additional/generators.md @@ -232,8 +232,10 @@ Aaaaagh! Generators can furthermore interact specifically with agents. -The Hood/Helm tooling like `|install` are generators automatically routed by Dojo to the correct agent. - 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. From 7ce9fca3923a196f0ee9b2be7186c569a1b3b170 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 15 May 2023 10:03:05 -0500 Subject: [PATCH 068/142] Correct output typ --- content/guides/additional/generators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/generators.md b/content/guides/additional/generators.md index 5af938a..9575377 100644 --- a/content/guides/additional/generators.md +++ b/content/guides/additional/generators.md @@ -116,7 +116,7 @@ the input arguments into a list (either the unnamed/required argument list or th base hash ends in: drceb %cz hash ends in: drceb app status: running - pending updates: ~:: + pending updates: ~ > +vats, =verb %.n %base From baf4c3fe16de60cc6145703240e6916c81c7578d Mon Sep 17 00:00:00 2001 From: thomasurbit <99678438+thomasurbit@users.noreply.github.com> Date: Wed, 17 May 2023 17:20:13 +0100 Subject: [PATCH 069/142] update link --- content/courses/asl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/courses/asl.md b/content/courses/asl.md index a96bf7f..b921a20 100644 --- a/content/courses/asl.md +++ b/content/courses/asl.md @@ -24,7 +24,7 @@ contribute at the level of basic Urbit userspace bounties. 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 upcomming class. Until then, feel free to work through the [App School docs](/guides/core/app-school). From cc201c8423a30290cf6c59b4d3cb75792bbd38e4 Mon Sep 17 00:00:00 2001 From: thomasurbit <99678438+thomasurbit@users.noreply.github.com> Date: Fri, 19 May 2023 09:58:00 +0100 Subject: [PATCH 070/142] link update --- content/courses/hsl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/courses/hsl.md b/content/courses/hsl.md index 6aaa388..0b9aaea 100644 --- a/content/courses/hsl.md +++ b/content/courses/hsl.md @@ -21,7 +21,7 @@ lessons. The next cohort will begin in June 2023. -- [Complete this form](https://forms.gle/9tBfJCB4D9MSHCmC9) to sign up for the next Hoon School Live. +- [Complete this form](https://airtable.com/shrdu1ExiGxcRvhZ5) to sign up for the next Hoon School Live. Until then, feel free to work through the [Hoon School docs](/guides/core/hoon-school). From 024379b091816bcffcbc159ea1c836801fa6e433 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Mon, 22 May 2023 13:02:05 -0500 Subject: [PATCH 071/142] Fix typo in ++peg --- content/reference/hoon/stdlib/1b.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ``` From 5fb044c30e0f865ac1c10d2b272a1b2c619a5b04 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 23 May 2023 19:44:14 -0500 Subject: [PATCH 072/142] Update F-cores.md --- content/guides/core/hoon-school/F-cores.md | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/content/guides/core/hoon-school/F-cores.md b/content/guides/core/hoon-school/F-cores.md index a9b5fd5..07cb6a1 100644 --- a/content/guides/core/hoon-school/F-cores.md +++ b/content/guides/core/hoon-school/F-cores.md @@ -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. From 8b24cf135a1fa9997fa1fbb9da59fc2049645edd Mon Sep 17 00:00:00 2001 From: hosted-fornet <79381743+hosted-fornet@users.noreply.github.com> Date: Wed, 24 May 2023 20:23:37 -0600 Subject: [PATCH 073/142] clay api: note only %sing works for remote ships --- content/reference/arvo/clay/tasks.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/reference/arvo/clay/tasks.md b/content/reference/arvo/clay/tasks.md index 3963d8b..d33958f 100644 --- a/content/reference/arvo/clay/tasks.md +++ b/content/reference/arvo/clay/tasks.md @@ -748,6 +748,8 @@ 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` requests -- not `%next`, `%mult`, or `%many`. To be informed when a desk on a foreign ship is updated, first `%sing` the current revision number using `%w` and case `da+now` and 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. From ca629e0b418bee6135a6b9775487ebb9c1aaacd6 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Thu, 25 May 2023 17:50:18 -0500 Subject: [PATCH 074/142] Hotfix Hoon School --- content/guides/core/hoon-school/M-typecheck.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 4aadc0fad30571453bca59544f88af8edc1608e9 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Fri, 26 May 2023 21:55:50 +1200 Subject: [PATCH 075/142] clay: clarify %many works over network --- content/reference/arvo/clay/tasks.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content/reference/arvo/clay/tasks.md b/content/reference/arvo/clay/tasks.md index d33958f..03fe947 100644 --- a/content/reference/arvo/clay/tasks.md +++ b/content/reference/arvo/clay/tasks.md @@ -748,7 +748,10 @@ 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` requests -- not `%next`, `%mult`, or `%many`. To be informed when a desk on a foreign ship is updated, first `%sing` the current revision number using `%w` and case `da+now` and then `%sing` again with the next revision number using case `ud+`. +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. From 4a7e4b9463d84890648dde44cc6769bba0a93cf7 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 26 May 2023 08:50:57 -0500 Subject: [PATCH 076/142] Caveat ASCII in knot --- content/guides/core/hoon-school/E-types.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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: From b7f0be8585961cb53ee232609287db70b6456b71 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 30 May 2023 15:08:57 -0500 Subject: [PATCH 077/142] Fix link --- content/overview/hoon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From 07fbce06e0bbea59b94697e1e9fe9193dde27aeb Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Thu, 1 Jun 2023 21:19:45 +1200 Subject: [PATCH 078/142] remote scries guide: add warning message --- content/guides/additional/remote-scry.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/content/guides/additional/remote-scry.md b/content/guides/additional/remote-scry.md index 5e24b21..0b1cf89 100644 --- a/content/guides/additional/remote-scry.md +++ b/content/guides/additional/remote-scry.md @@ -10,6 +10,21 @@ referentially transparent namespace. In other words, it's a function from a 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** + +Remote scries are still a new, experimental feature. + +As of `[%zuse 413]`, there is a known bug that causes subscribers to often +fail. A fix is currently planned for `[%zuse 412]`, and it is not recommended +to use remote scries in production userspace code until then. + +It should also be noted that, while responses are signed, encryption has not +yet been implemented. + +{% /callout %} + ## Lifecycle of a scry When you think of scry, you probably think of [`.^` From 78a2e7efceba5c1eb15c9e01184f3a93c36b20f6 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Fri, 2 Jun 2023 19:01:49 +1200 Subject: [PATCH 079/142] rework remote scry guide warning --- content/guides/additional/remote-scry.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/content/guides/additional/remote-scry.md b/content/guides/additional/remote-scry.md index 0b1cf89..ad2ef60 100644 --- a/content/guides/additional/remote-scry.md +++ b/content/guides/additional/remote-scry.md @@ -14,14 +14,21 @@ from Kernel version `[%zuse 413]`, it is possible to scry from *other* ships. **Warning** -Remote scries are still a new, experimental feature. +1. It should also be noted that, while responses are signed, encryption has not + yet been implemented. -As of `[%zuse 413]`, there is a known bug that causes subscribers to often -fail. A fix is currently planned for `[%zuse 412]`, and it is not recommended -to use remote scries in production userspace code until then. +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. -It should also be noted that, while responses are signed, encryption has not -yet been implemented. + 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 %} From 1301a44771500c2b2770fd3babd86d536235f3a3 Mon Sep 17 00:00:00 2001 From: Sean Maher Date: Sun, 4 Jun 2023 18:29:45 -0400 Subject: [PATCH 080/142] fix typo in censig docs --- content/reference/hoon/rune/cen.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/reference/hoon/rune/cen.md b/content/reference/hoon/rune/cen.md index a396705..4fb5711 100644 --- a/content/reference/hoon/rune/cen.md +++ b/content/reference/hoon/rune/cen.md @@ -571,9 +571,9 @@ 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 From 784d3c8c1744e1e385886e2da1dac3dcffd207b4 Mon Sep 17 00:00:00 2001 From: thomasurbit <99678438+thomasurbit@users.noreply.github.com> Date: Mon, 12 Jun 2023 17:03:52 +0400 Subject: [PATCH 081/142] add form link --- vercel.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index 077a6c7..de6befd 100644 --- a/vercel.json +++ b/vercel.json @@ -6,5 +6,10 @@ "source": "/guides/quickstart", "destination": "/guides/quickstart/chat-guide", "permanent": false - }] + }, + { + "source": "/hackathon", + "destination": "https://docs.google.com/forms/d/e/1FAIpQLSdsyFqt66YZAlVmgHAIZeaSnbDuH-q7vLl1aiU8xzwY79cawA/viewform?usp=sf_link", + "permanent": false + }] } \ No newline at end of file From 159efc564bfa88844ef19d638ee8d26bf899ba9d Mon Sep 17 00:00:00 2001 From: Matthias Schaub <83658582+matthiasschaub@users.noreply.github.com> Date: Thu, 15 Jun 2023 12:15:58 +0200 Subject: [PATCH 082/142] correct link destination in hoon school guide --- content/guides/core/hoon-school/K-doors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/core/hoon-school/K-doors.md b/content/guides/core/hoon-school/K-doors.md index 4fe761a..97f3537 100644 --- a/content/guides/core/hoon-school/K-doors.md +++ b/content/guides/core/hoon-school/K-doors.md @@ -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-doors). ```hoon > (~(get by colors) %brown) From 6a337b1bc75ae334a3c513ef8aaf118668afee0d Mon Sep 17 00:00:00 2001 From: thomasurbit <99678438+thomasurbit@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:47:04 +0400 Subject: [PATCH 083/142] new link --- content/courses/hsl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/courses/hsl.md b/content/courses/hsl.md index 0b9aaea..1fbaeb9 100644 --- a/content/courses/hsl.md +++ b/content/courses/hsl.md @@ -21,7 +21,7 @@ lessons. The next cohort will begin in June 2023. -- [Complete this form](https://airtable.com/shrdu1ExiGxcRvhZ5) 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). From c99956c18e8a27340035f9d46806e28805d6d639 Mon Sep 17 00:00:00 2001 From: Tom Nash Date: Thu, 15 Jun 2023 22:14:14 +0200 Subject: [PATCH 084/142] docs: mention lowercase filenames in http glob generation --- content/reference/additional/dist/glob.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From a4a50eb0706a1f7b867575593390116d1d15302c Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:27:25 -0500 Subject: [PATCH 085/142] Update K-doors.md --- content/guides/core/hoon-school/K-doors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/core/hoon-school/K-doors.md b/content/guides/core/hoon-school/K-doors.md index 97f3537..a6d7644 100644 --- a/content/guides/core/hoon-school/K-doors.md +++ b/content/guides/core/hoon-school/K-doors.md @@ -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/L-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) From 724973137416248b9f2d3454d104717b37539c75 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:43:18 -0500 Subject: [PATCH 086/142] Document +less in parsing tutorial. --- content/guides/additional/parsing.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 From f7dada3a448af1234a4c7b3b536f5563ea4bf6ec Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sun, 25 Jun 2023 15:13:52 +1200 Subject: [PATCH 087/142] gall types ref: update bowl /w sky --- content/reference/arvo/gall/data-types.md | 30 ++++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/content/reference/arvo/gall/data-types.md b/content/reference/arvo/gall/data-types.md index 0a9c694..5779d97 100644 --- a/content/reference/arvo/gall/data-types.md +++ b/content/reference/arvo/gall/data-types.md @@ -42,18 +42,21 @@ which our agent has subscribed. ```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. @@ -68,6 +71,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. From 3906434ee5ff8cfdd13a55a3785170a048d5c122 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sun, 25 Jun 2023 15:44:40 +1200 Subject: [PATCH 088/142] clay ref: correct %x scry description --- content/reference/arvo/clay/scry.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/content/reference/arvo/clay/scry.md b/content/reference/arvo/clay/scry.md index 4e7f3ab..1ea2d24 100644 --- a/content/reference/arvo/clay/scry.md +++ b/content/reference/arvo/clay/scry.md @@ -530,19 +530,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] ``` ``` From e118605a6f1f1654edacffd7cf8eae17b5f83fc5 Mon Sep 17 00:00:00 2001 From: jack-z-wang <81378423+jack-z-wang@users.noreply.github.com> Date: Tue, 27 Jun 2023 13:37:55 -0400 Subject: [PATCH 089/142] added hsl-23-1 coding challenges to hoon wb --- .../guides/additional/workbook/abc-blocks.md | 574 ++++++++++++++++++ .../guides/additional/workbook/luhn-number.md | 574 ++++++++++++++++++ .../additional/workbook/water-towers.md | 381 ++++++++++++ 3 files changed, 1529 insertions(+) create mode 100644 content/guides/additional/workbook/abc-blocks.md create mode 100644 content/guides/additional/workbook/luhn-number.md create mode 100644 content/guides/additional/workbook/water-towers.md diff --git a/content/guides/additional/workbook/abc-blocks.md b/content/guides/additional/workbook/abc-blocks.md new file mode 100644 index 0000000..80f4e46 --- /dev/null +++ b/content/guides/additional/workbook/abc-blocks.md @@ -0,0 +1,574 @@ ++++ +title = "ABC Blocks" +weight = 48 ++++ + +## 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. + +``` +/+ *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 GNU License. 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._ + +``` +:: +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_ + +``` +|= [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_ + +``` +!: +|= [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 +-- +``` \ No newline at end of file diff --git a/content/guides/additional/workbook/luhn-number.md b/content/guides/additional/workbook/luhn-number.md new file mode 100644 index 0000000..c52cfb2 --- /dev/null +++ b/content/guides/additional/workbook/luhn-number.md @@ -0,0 +1,574 @@ ++++ +title = "Luhn Number" +weight = 48 ++++ + +## 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. + +``` +/+ *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. We ask you to acknowledge authorship should you utilize these elsewhere._ + +### Solution #1 + +_By ~dozreg-toplud._ + +`lib/luhn-number.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` +``` +:: 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. Our speed winner_ + +`lib/luhn-number.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` + +``` +/+ 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` + +``` +:: 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` + +``` +:: 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) +-- +``` \ No newline at end of file diff --git a/content/guides/additional/workbook/water-towers.md b/content/guides/additional/workbook/water-towers.md new file mode 100644 index 0000000..ba6a2b9 --- /dev/null +++ b/content/guides/additional/workbook/water-towers.md @@ -0,0 +1,381 @@ ++++ +title = "Water Towers" +weight = 48 ++++ + +## 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. + +``` +/+ *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 GNU License. We ask you to acknowledge authorship should you utilize these elsewhere._ + +### Solution #1 + +_Solution by ~dannul-bortux. A model for literate programming._ + +``` +:: +:: 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. Short and elegant. Also the speed winner, clocking in at an incredible 29 minutes._ + +``` +=> +|% +++ 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._ + + +``` +:: +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)) + == +:: +-- +``` \ No newline at end of file From b1f3da4156977f8cc58dc3ad83f869da018d0e1c Mon Sep 17 00:00:00 2001 From: jack-z-wang <81378423+jack-z-wang@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:33:07 -0400 Subject: [PATCH 090/142] added hsl-23-1 coding challenges to hoon wb --- content/guides/additional/workbook/water-towers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/workbook/water-towers.md b/content/guides/additional/workbook/water-towers.md index ba6a2b9..22b3dc6 100644 --- a/content/guides/additional/workbook/water-towers.md +++ b/content/guides/additional/workbook/water-towers.md @@ -92,7 +92,7 @@ _These solutions were submitted by the Urbit community as part of a competition ### Solution #1 -_Solution by ~dannul-bortux. A model for literate programming._ +_By ~dannul-bortux. A model for literate programming._ ``` :: From 8d2e4d5309df189459bbd2fb28b5aaee7eadf028 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:01:40 -0500 Subject: [PATCH 091/142] Update abc-blocks.md --- content/guides/additional/workbook/abc-blocks.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/guides/additional/workbook/abc-blocks.md b/content/guides/additional/workbook/abc-blocks.md index 80f4e46..e6b5a97 100644 --- a/content/guides/additional/workbook/abc-blocks.md +++ b/content/guides/additional/workbook/abc-blocks.md @@ -1,6 +1,6 @@ +++ title = "ABC Blocks" -weight = 48 +weight = 11 +++ ## Challenge: ABC Blocks @@ -178,11 +178,11 @@ Following a principle of test-driven development, we compose a series of tests w ## Solutions -_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the GNU License. We ask you to acknowledge authorship should you utilize these elsewhere._ +_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._ +_By ~dozreg-toplud. In the process, he found and fixed a bug in the implementation of `++curr`._ ``` :: +abc-blocks: a solution to the HSL challenge #2 @@ -571,4 +571,4 @@ _By ~dannul-bortux_ :: blocks :: %.n -- -``` \ No newline at end of file +``` From 9535b17c057e2f8f2e5aa25bfa9444dd577616b6 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:02:35 -0500 Subject: [PATCH 092/142] Update luhn-number.md --- content/guides/additional/workbook/luhn-number.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/guides/additional/workbook/luhn-number.md b/content/guides/additional/workbook/luhn-number.md index c52cfb2..7dea772 100644 --- a/content/guides/additional/workbook/luhn-number.md +++ b/content/guides/additional/workbook/luhn-number.md @@ -1,6 +1,6 @@ +++ title = "Luhn Number" -weight = 48 +weight = 128 +++ ## Challenge: Luhn Number @@ -208,7 +208,7 @@ Following a principle of test-driven development, we compose a series of tests w ## 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. We ask you to acknowledge authorship should you utilize these elsewhere._ +_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 @@ -291,7 +291,7 @@ _By ~dozreg-toplud._ ### Solution #2 -_By ~pardun-nollev. Our speed winner_ +_By ~pardun-nollev._ `lib/luhn-number.hoon` ``` @@ -571,4 +571,4 @@ $(count (add 1 count)) `(list tape)`[a ~] (testnewnum a) -- -``` \ No newline at end of file +``` From dd6ffdc6fda1944ec6e8e8d06d9d057c3caccd3f Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:03:31 -0500 Subject: [PATCH 093/142] Update water-towers.md --- content/guides/additional/workbook/water-towers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/guides/additional/workbook/water-towers.md b/content/guides/additional/workbook/water-towers.md index 22b3dc6..4826e40 100644 --- a/content/guides/additional/workbook/water-towers.md +++ b/content/guides/additional/workbook/water-towers.md @@ -88,11 +88,11 @@ Following a principle of test-driven development, we compose a series of tests w ## Solutions -_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the GNU License. We ask you to acknowledge authorship should you utilize these elsewhere._ +_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._ +_By ~dannul-bortux. A model for literate programming in Hoon._ ``` :: @@ -260,7 +260,7 @@ _By ~dannul-bortux. A model for literate programming._ ### Solution #2 -_By ~racfer-hattes. Short and elegant. Also the speed winner, clocking in at an incredible 29 minutes._ +_By ~racfer-hattes. A short and elegant solution._ ``` => @@ -378,4 +378,4 @@ $(x +(x)) == :: -- -``` \ No newline at end of file +``` From 3ea9716fc99bba9fade1332f9d572016c0213e23 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:03:50 -0500 Subject: [PATCH 094/142] Update abc-blocks.md --- content/guides/additional/workbook/abc-blocks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/workbook/abc-blocks.md b/content/guides/additional/workbook/abc-blocks.md index e6b5a97..08d9982 100644 --- a/content/guides/additional/workbook/abc-blocks.md +++ b/content/guides/additional/workbook/abc-blocks.md @@ -178,7 +178,7 @@ Following a principle of test-driven development, we compose a series of tests w ## 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._ +_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 From f5c8f99717a7dc45e80b55383b4856cf9de53d05 Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:04:16 -0500 Subject: [PATCH 095/142] Update water-towers.md --- content/guides/additional/workbook/water-towers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/additional/workbook/water-towers.md b/content/guides/additional/workbook/water-towers.md index 4826e40..5446329 100644 --- a/content/guides/additional/workbook/water-towers.md +++ b/content/guides/additional/workbook/water-towers.md @@ -1,6 +1,6 @@ +++ title = "Water Towers" -weight = 48 +weight = 230 +++ ## Challenge: Water between Towers From 334a07fb3c219768c442ccb9a3642e874d37b3e5 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Thu, 29 Jun 2023 00:40:39 +1200 Subject: [PATCH 096/142] dill: bring all reference docs up-to-date - update scries - update and restructure tasks - update data types - update overview in lieu of dill now supporting multiple sessions --- content/reference/arvo/dill/data-types.md | 313 +++++++----- content/reference/arvo/dill/dill.md | 61 ++- content/reference/arvo/dill/scry.md | 45 +- content/reference/arvo/dill/tasks.md | 585 +++++++++++++--------- 4 files changed, 625 insertions(+), 379 deletions(-) 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`. + +--- + From dcf4919f5b51aa31074eaca0725481d249542250 Mon Sep 17 00:00:00 2001 From: jack-z-wang <81378423+jack-z-wang@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:33:53 -0400 Subject: [PATCH 097/142] added links to hoon workbook index --- content/guides/additional/workbook/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) 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 From 8e049d6c1fa6a6c2aa6bd0bc1cbdc097497562f3 Mon Sep 17 00:00:00 2001 From: John Franklin Date: Thu, 29 Jun 2023 05:02:12 -0500 Subject: [PATCH 098/142] Fix glob location in Build a Chat App --- content/guides/quickstart/chat-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/quickstart/chat-guide.md b/content/guides/quickstart/chat-guide.md index 0f7a9ec..5d686ee 100644 --- a/content/guides/quickstart/chat-guide.md +++ b/content/guides/quickstart/chat-guide.md @@ -1668,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 From 7e8b0db03dfb3c6c16c00cca9ac138a08dcc4525 Mon Sep 17 00:00:00 2001 From: Vinney Cavallo ~sogrum-savluc Date: Fri, 30 Jun 2023 08:45:40 -0600 Subject: [PATCH 099/142] ecliptic: add missing word to transferPoint docs --- content/reference/azimuth/ecliptic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 5cd538040b356dbfdb0e900f40842da7fddf0d61 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sat, 1 Jul 2023 19:33:02 +1200 Subject: [PATCH 100/142] gall: clarify empty fact path only works in on-watch --- content/reference/arvo/gall/gall-api.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/content/reference/arvo/gall/gall-api.md b/content/reference/arvo/gall/gall-api.md index ff6fbc6..e32768d 100644 --- a/content/reference/arvo/gall/gall-api.md +++ b/content/reference/arvo/gall/gall-api.md @@ -816,13 +816,14 @@ subscription content for all subscribers on a given path. #### Structure ```hoon -[%fact (list path) =cage] +[%fact paths=(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. +`paths` is a list of the 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` is a cage of the subscription update. From 85739d07796a95d3ca0e6c112e33230d10517aaa Mon Sep 17 00:00:00 2001 From: Sigilante <57601680+sigilante@users.noreply.github.com> Date: Sun, 2 Jul 2023 15:33:28 -0500 Subject: [PATCH 101/142] Update I-testing.md --- content/guides/core/hoon-school/I-testing.md | 2 -- 1 file changed, 2 deletions(-) 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). From 207cc8ba88f0430fb110b10222212ed745975c18 Mon Sep 17 00:00:00 2001 From: Amadeo Bellotti Date: Thu, 6 Jul 2023 09:48:38 -0400 Subject: [PATCH 102/142] lick changes --- content/reference/arvo/_index.md | 4 + content/reference/arvo/lick/_index.md | 22 +++++ content/reference/arvo/lick/examples.md | 126 ++++++++++++++++++++++++ content/reference/arvo/lick/lick.md | 16 +++ content/reference/arvo/lick/scry.md | 35 +++++++ content/reference/arvo/lick/tasks.md | 106 ++++++++++++++++++++ content/reference/glossary/arvo.md | 1 + content/reference/glossary/lick.md | 24 +++++ content/reference/glossary/vane.md | 1 + 9 files changed, 335 insertions(+) create mode 100644 content/reference/arvo/lick/_index.md create mode 100644 content/reference/arvo/lick/examples.md create mode 100644 content/reference/arvo/lick/lick.md create mode 100644 content/reference/arvo/lick/scry.md create mode 100644 content/reference/arvo/lick/tasks.md create mode 100644 content/reference/glossary/lick.md diff --git a/content/reference/arvo/_index.md b/content/reference/arvo/_index.md index 4a0ae41..3a9a4e5 100644 --- a/content/reference/arvo/_index.md +++ b/content/reference/arvo/_index.md @@ -46,6 +46,10 @@ Jael manages keys and Azimuth state. Khan is our thread dispatcher. +## [Lick](/reference/arvo/lick/lick) + +Lick is our interprocess communication (IPC) vane. Unix process can communicate to applications on Urbit through IPC. + ## [Concepts](/reference/arvo/concepts/) Explanations of design decisions that are pervasive throughout Arvo. diff --git a/content/reference/arvo/lick/_index.md b/content/reference/arvo/lick/_index.md new file mode 100644 index 0000000..f7ef471 --- /dev/null +++ b/content/reference/arvo/lick/_index.md @@ -0,0 +1,22 @@ ++++ +title = "Lick" +weight = 20 +sort_by = "weight" +insert_anchor_links = "right" ++++ + +## [Overview](/reference/arvo/lick/lick) + +An overview of Lick, Urbit's timer vane. + +## [API Reference](/reference/arvo/lick/tasks) + +The `task`s Lick takes and the `gift`s it returns. + +## [Scry Reference](/reference/arvo/lick/scry) + +The scry endpoints of Lick. + +## [Examples](/reference/arvo/lick/examples) + +Practical examples of using Lick's `task`s. diff --git a/content/reference/arvo/lick/examples.md b/content/reference/arvo/lick/examples.md new file mode 100644 index 0000000..d41f977 --- /dev/null +++ b/content/reference/arvo/lick/examples.md @@ -0,0 +1,126 @@ ++++ +title = "Examples" +weight = 4 ++++ + +## %receipt + +Below is an example of an application that spins up a lick port on its init call. When it recieves a `%connected` soak is subscribes to %rumors. whenever a %rumor is recieved it will spit it out +to the Lick port and which will forward it to a reciept printer. When the receipt printer disconnects the application will unsubscribe to %rumors and wait. + +`receipt.hoon` +```hoon +/+ default-agent, dbug +!: +|% ++$ versioned-state + $% state-0 + == ++$ state-0 + $: [%0 name=@tas] + == ++$ card card:agent:gall +-- +%- agent:dbug +=| state-0 +=* state - +^- agent:gall +|_ =bowl:gall ++* this . + default ~(. (default-agent this %|) bowl) +++ on-init + ^- (quip card _this) + :_ this + [%pass / %arvo %l %spin /rumors/uart]~ +++ on-save !>(state) +++ on-load + |= old=vase + ^- (quip card _this) + `this(state !<(state-0 old)) +++ on-poke + |= [=mark =vase] + ^- (quip card _this) + ?> ?=(%uart-action mark) + =/ act !<(action vase) + ?- -.act + %spin + :_ this + [%pass / %arvo %l %spin name.act]~ + :: + %shut + :_ this + [%pass /shut %arvo %l %shut name.act]~ + :: + %spit + :_ this + [%pass /spit %arvo %l %spit name.act mark.act data.act]~ + == +:: +++ on-peek + |= =path + ^- (unit (unit cage)) + ~& >> path + =/ dev `@tas`(snag 2 path) + ?+ path (on-peek:default path) + [%s %agents *] ``noun+!>(dev) + == +++ on-arvo + |= [=wire =sign-arvo] + ^- (quip card _this) + =/ cad +.sign-arvo + ~& > ['wire' wire] + ~& > ['sign-arvo' sign-arvo] + ?+ sign-arvo (on-arvo:default wire sign-arvo) + [%lick %soak *] + ?+ mark.sign-arvo [~ this] + :: + %connect + :_ this + :~ [%pass /subscribe %agent [our.bowl %rumors] %watch /rumors] + == + :: + %disconnect + :_ this + :~ [%pass /subscribe %agent [our.bowl %rumors] %leave ~] + == + :: + %error + ~& > ['Error: ' ;;(@tas noun.sign-arvo)] + `this + == + == +++ on-watch on-watch:default +++ on-leave on-leave:default +++ on-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + ?+ wire (on-agent:default wire sign) + [%subscribe ~] + ?+ -.sign (on-agent:default wire sign) + %watch-ack + ?~ p.sign + ((slog '%rumors-watcher: Subscribe succeeded!' ~) `this) + ((slog '%rumors-watcher: Subscribe failed!' ~) `this) + :: + %kick + %- (slog '%rumors-watcher: Got kick, resubscribing...' ~) + :_ this + :~ [%pass /subscribe %agent [our.bowl %rumors] %watch /rumors] + == + :: + %fact + ?+ p.cage.sign (on-agent:default wire sign) + %rumor + =/ rumor !<([when=@da what=@t] q.cage.sign) + =/ print :(weld (scow %da when.rumor) ": " (trip what.rumor)) + ~& (crip print) + :_ this + [%pass /spit %arvo %l %spit /rumors/uart %print (crip print)]~ + == + == + == +:: +++ on-fail on-fail:default +-- +``` + diff --git a/content/reference/arvo/lick/lick.md b/content/reference/arvo/lick/lick.md new file mode 100644 index 0000000..2a90a4f --- /dev/null +++ b/content/reference/arvo/lick/lick.md @@ -0,0 +1,16 @@ ++++ +title = "Overview" +weight = 1 ++++ + +Urbit's Interprocess Communication vane. + +Lick manages IPC ports and the communication between Urbit applications and POSIX applciations. Other vanes and applications ask Lick to open an IPC port, notify it when something is connected or disconnected, and transfer data between itself and the Unix application. + +## Sections + +[API Reference](/reference/arvo/lick/tasks) - The `task`s Lick takes and the `gift`s it returns. + +[Scry Reference](/reference/arvo/lick/scry) - The scry endpoints of Lick. + +[Examples](/reference/arvo/lick/examples) - Practical examples of using Lick's `task`s. diff --git a/content/reference/arvo/lick/scry.md b/content/reference/arvo/lick/scry.md new file mode 100644 index 0000000..4926bcc --- /dev/null +++ b/content/reference/arvo/lick/scry.md @@ -0,0 +1,35 @@ ++++ +title = "Scry Reference" +weight = 3 ++++ + +Here are Lick's scry endpoints. There's only a few and they're mostly just useful for debugging. All of Lick's scries are specified by a `care`, which is a single character corresponding to a command. +The only `beam` it will respond to is the local identify, current timestamp, and any additonal informaiton a `care` needs. + +## `%a` - Read ports. + +A scry with a `care` of `%a` will return a list of all registered IPC ports. + +``` +.^((list path) %la /=//=/ports) +~[/hood/reciept/control /slick/control] +``` + +## `%d` - Port Owner + +A scry with a `care` of `%d` will return the duct of the IPC Port Owner. The `beam` will have to in clude the port identifier + +``` +.^((unit duct) %ld /===/[port-name]) +[~ [i=/gall/use/slick/0w3.IZWEn/~nec t=[i=/dill t=~[//term/1]]]] +``` + +## `%u` - Port Existance + +A scry with a `care` of `%u` will return if Lick has a Port registered in its state. + +``` +.^(@ %lu /===/slick/control) +0 +``` + diff --git a/content/reference/arvo/lick/tasks.md b/content/reference/arvo/lick/tasks.md new file mode 100644 index 0000000..740dc28 --- /dev/null +++ b/content/reference/arvo/lick/tasks.md @@ -0,0 +1,106 @@ ++++ +title = "API Reference" +weight = 2 ++++ + +In this document we describe the public interface for Lick. Namely, we describe each `task` that Lick can be `pass`ed, and which `gift`(s) Lick can `give` in return. + + +## `%born` + +```hoon +[%born ~] +``` + +Each time you start your urbit, the Arvo kernel calls the `%born` task for Lick. When called, Lick will send every IPC port in it state to `vere` and send a `%disconnect` `%soak` to each IPC port owner. + +#### Returns + +Lick returns a `%spin` `gift` to `vere` in response to a `%born` `task`. + +## `%spin` + +```hoon +[%spin name=path] +``` +Open an IPC port. + +Lick takes in a `path` and saves the `duct` that sent it as the owner then forwards the call to `vere`. `vere` will open an IPC port on the host OS and wait for something to connect to it. + +#### Returns + +Lick sends a `gift` of a `%spin` to `vere` in response to a `%spin` `task`. + +#### Example + +See the [%spin](/reference/arvo/lick/examples#spin) section of the Examples document. + +## `%shut` + +```hoon +[%shut name=path] +``` +Close an IPC port. + +Lick takes a `path` and removes it from its state. It also forwards the `path` to `vere` which inturn disconnects the IPC port from anything connected to it and closes it. + +#### Returns + +Lick sends a `gift` of a `%shut` to `vere` in response to a `%shut` `task`. + +#### Example + +See the [%shut](/reference/arvo/lick/examples#shut) section of the Examples document. + +## `%spit` + +```hoon +[%spit name=path mark=mark noun=noun] +``` + +Sends a `[mark noun]` cell to the IPC vane. + +Lick will send a newt-jammed `[mark noun]` cell to the IPC port if something is connected to it. If nothing is connected to the port, Lick will send an `%error` `%soak` to the port's owner. + +#### Returns + +Lick forwards the contents of the task as a gift to vere. + +## `%trim` + +```hoon +[%trim ~] +``` + +This `task` is sent by the interpreter in order to free up memory. Lick does not do anything with this `task`, since it is not a good idea to forget your IPC ports. + +You would not use this `task` from userspace. + +#### Returns + +Lick does not return any `gift` in response to a `%trim` `task`. + +## `%vega` + +```hoon +[%vega ~] +``` + +This `task` informs the vane that the kernel has been upgraded. Lick does not do anything in response to this. + +You would not use this `task` from userspace. + +#### Returns + +Lick does not return any `gift` in response to a `%vega` `task`. + +## `%soak` + +```hoon +[%soak name=path mark=mark noun=noun] +``` + +Receives data from outside. This `task` is sent to Lick by the runtime, you would not use it manually. + +The `name` is associated with the `duct` that registered it and the `%soak` is forwarded to it. + diff --git a/content/reference/glossary/arvo.md b/content/reference/glossary/arvo.md index 44d7ec3..32a0717 100644 --- a/content/reference/glossary/arvo.md +++ b/content/reference/glossary/arvo.md @@ -46,6 +46,7 @@ terminal driving, [Eyre](/reference/glossary/eyre) for the web server, [Iris](/reference/glossary/iris) for the HTTP client, [Jael](/reference/glossary/jael) for PKI management, and [Khan](/reference/glossary/khan) for external control and thread running. +[Lick](/reference/glossary/lick) for external communication. Vanes and other programs for Arvo are written in [Hoon](/reference/glossary/hoon). diff --git a/content/reference/glossary/lick.md b/content/reference/glossary/lick.md new file mode 100644 index 0000000..f3da842 --- /dev/null +++ b/content/reference/glossary/lick.md @@ -0,0 +1,24 @@ ++++ +title = "Lick" + +[extra] +category = "arvo" + +[glossaryEntry.lick] +name = "lick" +symbol = "" +usage = "arvo" +desc = "IPC vane Arvo. Allows for applications to communicate with earth." + ++++ +**Lick** is the interprocess communication (IPC) [vane](/reference/glossary/vane) (kernel module). +It allows for applications to open and close IPC ports to earth. For +example, a [Gall](/reference/glossary/gall) agent can send a `%spin` +card to open an IPC port and allow for an earth process to communicate +with it through `%spit` and `%soak` cards. + +Lick is located in `/base/sys/vane/lick.hoon` within [Arvo](/reference/glossary/arvo). + +### Further Reading + +- [The Lick reference](/reference/arvo/lick/lick): A technical guide to the Lick vane. diff --git a/content/reference/glossary/vane.md b/content/reference/glossary/vane.md index 76c63f5..af9b41d 100644 --- a/content/reference/glossary/vane.md +++ b/content/reference/glossary/vane.md @@ -25,6 +25,7 @@ A **vane** is an [Arvo](/reference/glossary/arvo) kernel module that performs es - [Iris](/reference/glossary/iris), the server HTTP vane. - [Jael](/reference/glossary/jael), the security vane. - [Khan](/reference/glossary/khan), the control vane. +- [Lick](/reference/glossary/Lick), the interprocess communication (IPC) vane. ### Further Reading From 5ce841a90a1b96179f9933e0fffe5e02d65d6fc9 Mon Sep 17 00:00:00 2001 From: thomasurbit <99678438+thomasurbit@users.noreply.github.com> Date: Fri, 7 Jul 2023 00:35:01 +0200 Subject: [PATCH 103/142] first go on new hackathon page --- content/highlights/highlight-1.md | 8 +- pages/hackathon.js | 165 ++++++++++++++++++++++++++++++ vercel.json | 8 +- 3 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 pages/hackathon.js diff --git a/content/highlights/highlight-1.md b/content/highlights/highlight-1.md index ece8422..ecc429c 100644 --- a/content/highlights/highlight-1.md +++ b/content/highlights/highlight-1.md @@ -1,6 +1,6 @@ +++ -title = "Hoon School Live Summer '23" -image = "https://storage.googleapis.com/media.urbit.org/site/featured/hsl-june-2023.png" -url="https://developers.urbit.org/courses/hsl" -description = "The next session of Hoon School Live begins in June. 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="https://developers.urbit.org/hackathon" +description = "Show your Urbit killer app in Lisbon and compete for the biggest prize pool ever on Urbit." +++ \ No newline at end of file diff --git a/pages/hackathon.js b/pages/hackathon.js new file mode 100644 index 0000000..d5461ac --- /dev/null +++ b/pages/hackathon.js @@ -0,0 +1,165 @@ +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: + "Want o build the killer Urbit 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 years Assembly Hackathon has the most substantial prize pool in Urbit history. Are you prepared to rise to the challenge, showcase your skills, and claim victory? +

+

+ In the first week you enter as an individual and form a team or register your existing one. 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. Access 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, winning the Hackathon is a matter of determination. +

+

+ +

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

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

Prizes

+ This years Hackathon has the most substantial prize pool in Urbit history and there is even more tba +
+ {/* Overall */} +
+
+

Best overall – 3 Stars

+

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

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

Zorp Category – Prize TBA

+

+ The best Zorp/zk integration, more details TBA. +

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

Best UI – 2 Stars

+

+ The judges will decide on the Best UI on the criteria of: visual appeal, UX, responsiveness, consistency, innovation and interactivity. +

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

Best Composability – 2 Stars

+

+ The product should seamlessly integrate or interact with other systems, platforms, or APIs. Judges will evaluate how effectively the product can communicate and collaborate with external services or technologies, enabling smooth interoperability and data exchange. +

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

Best new Hooner team – Prize TBA

+

+ This prize will be given out to the best product that consists of minimal 50% members that have graduated Hoon School since last Assembly. +

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

Audience Vote – 1 Star

+

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

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