diff --git a/pkg/arvo/neo/cod/std/src/fil/counter.txt b/pkg/arvo/neo/cod/std/src/fil/counter.txt index 1f6647fcfa..a3767a2ecf 100644 --- a/pkg/arvo/neo/cod/std/src/fil/counter.txt +++ b/pkg/arvo/neo/cod/std/src/fil/counter.txt @@ -125,14 +125,6 @@ Let's look at the structure of `/imp/counter`. These lines import two types from our `/pro` folder: `number` and `counter-diff`. To import from `/pro` we use `/@` as a new Ford-style rune. -``` -,@ud -``` - -``` -,[%inc ~] -``` - A shrub is a five-arm `|%` core — called a `kook:neo` — with an inner two-arm core called a `form:neo`. The `kook` defines type information about the shrub, and the inner `form` contains business logic. At first glance the `kook` might look familiar to Gall developers, but this is all new logic defining 1) what's stored at this node in the namespace 2) what can be stored below this node, and 3) what we expect to be stored at existing nodes we declare as dependencies. diff --git a/pkg/arvo/neo/cod/std/src/fil/messenger.md b/pkg/arvo/neo/cod/std/src/fil/messenger.txt similarity index 99% rename from pkg/arvo/neo/cod/std/src/fil/messenger.md rename to pkg/arvo/neo/cod/std/src/fil/messenger.txt index ec8a340191..5e2c6502c4 100644 --- a/pkg/arvo/neo/cod/std/src/fil/messenger.md +++ b/pkg/arvo/neo/cod/std/src/fil/messenger.txt @@ -1,4 +1,5 @@ # Chapter 3: Messenger + The last major aspect of shrubbery that we need to cover is the dependency system. This consists of the `+deps` arm and the `%rely` poke. By the end of this tutorial you’ll understand how dependencies work and how to use them. You should also start to see how you can design functionality that involves multiple shrubs interacting across multiple ships. @@ -16,7 +17,8 @@ One motivation behind this design is to split off functionality into simple, reu We'll skip covering the Messenger frontend. While there are new ideas here, it's very similar to the Tasks tutorial which is the better context for more frontend. This tutorial will focus on several shrubs interoperating to form one tool. Let's look at `/imp/message-sub`, then the whole system that Messenger uses to manage chats. ## /imp/message-sub -```hoon + +``` /@ message ^- kook:neo |% @@ -71,9 +73,10 @@ We'll skip covering the Messenger frontend. While there are new ideas here, it's ``` ## The +deps arm + The only part of this system that needs to define its dependencies is `/imp/message-sub`. -```hoon +``` :: :: define dependencies ++ deps @@ -103,7 +106,7 @@ The only part of this system that needs to define its dependencies is `/imp/mess With regards to the lifecycle of the shrub, the `+deps` arm types the shrubs whose names and locations are passed in as an argument in the `%make` card when this shrub is created by `/imp/groupchat`. -```hoon +``` [%make %message-sub ~ (malt ~[[%pub (snoc host.poke %pub)]])] ``` @@ -118,11 +121,12 @@ The last “new” idea here is `%sig`. This is a special case of `stud:neo` whi If you look at `/imp/sig.hoon`, it’s just a `~` stub like the `%txt` implementation. `%sig` imps are not special and are not treated differently to any other stub, they’re just a stylistic convention to say that we don’t care about the state of the shrub in question; it could be anything, we won’t constrain it at all. There’s also a `/pro/sig.hoon` which lets us do the same thing for pokes. ## Handling state changes in our dependencies + Unlike a Gall agent, a shrub does not send out `%facts` to subscribers in the event of changes to its state, all of which has to be manually implemented by that agent’s developer. Instead, when its state changes `/app/neo` automatically sends a `%rely` poke to shrubs that have declared the shrub as a dependency. The developer of the listener shrub is the only one who has to write the logic to handle this. In its `+deps` arm, the `/imp/message-sub` shrub declares the type and behaviour of the shrubs it will accept as dependencies. Shrubs that conform to that type, like `/imp/message-pub`, can be passed in through the `%make` card via the `conf` and `/imp/message-sub` will listen to those shrubs for state changes. -```hoon +``` ++ deps %- ~(gas by *deps:neo) :~ :* %pub @@ -147,9 +151,10 @@ In `+deps`, the `%y` care declares that we’re only listening for state changes The other `care:neo` you’ll commonly see is `%x`, which refers to a single shrub. You wouldn’t use this in `+kids`, but you might use it in `+deps`. ### Handling %rely pokes + Let’s see how `/imp/message-sub` handles the `%rely` pokes it recevives from dependencies. -```hoon +``` ++ poke :: :: we receive a stud and vase from the publisher shrub when @@ -180,7 +185,7 @@ Let’s see how `/imp/message-sub` handles the `%rely` pokes it recevives from d The above is mostly self-explanatory, but it’s worth expanding on `stem:neo` and `mode:neo`. -```hoon +``` +$ stem $~ [*ever %x %stud *vase] %+ pair ever @@ -199,15 +204,18 @@ Stems tagged with `%x` come with a `pail` with the new state of the dependency. The `mode` of these kids is either `%add`, `%dif`, or `%del`. If it’s `%add`, the dependency is telling us it’s a new kid. If `%dif`, the kid isn’t new but its state has changed. If `%del`, it’s telling us the kid was deleted and giving us its final state. ## Messenger: Overview + There are several shrubs working in tandem here to provide groupchat and DM functionality. Now that we know how `/imp/message-sub` works, let's look at the overall structure. ### /imp/message-pub + The only part of the Messenger backend left to consider is `/imp/message-pub`. This only imports `/pro/txt` and `/pro/message`. #### kook:neo + `/imp/message-pub` only has one job, and that's to `%make` `%message`s as kids. Shrubs don't know anything about the shrubs above them that they weren't explicitly told in their `%make` card, so `/imp/message-pub` doesn't know or care whether it's being used to publish DMs or groupchat messages. -```hoon +``` ++ state [%pro %sig] ++ poke (sy %message %txt ~) ++ kids @@ -223,9 +231,10 @@ The only part of the Messenger backend left to consider is `/imp/message-pub`. T ``` #### +init + Like `/imp/messenger`, `/imp/message-pub` takes no state. -```hoon +``` ++ init |= old=(unit pail:neo) ^- (quip card:neo pail:neo) @@ -233,9 +242,10 @@ Like `/imp/messenger`, `/imp/message-pub` takes no state. ``` #### +poke + All that happens in the `+poke` arm is this shrub creating `%message`s below it in the namespace. The `%txt` is not actually necessary, but as a primitive it might be nice for `/imp/message-pub` to be able to construct messages without the developer having to specify the metadata. -```hoon +``` ++ poke |= [=stud:neo vax=vase] ^- (quip card:neo pail:neo) @@ -256,11 +266,12 @@ All that happens in the `+poke` arm is this shrub creating `%message`s below it ``` ### /imp/dm + If you wanted to implement 1-on-1 DMs in your own shrub, you could just `%make` an `/imp/dm`. If that doesn't do waht you need, you could base your own DM functionality on this. #### kook:neo -```hoon +``` ++ state [%pro %ship] :: who I'm chatting with ++ poke (sy %dm-diff ~) ++ kids @@ -279,7 +290,7 @@ If you wanted to implement 1-on-1 DMs in your own shrub, you could just `%make` A DM shrub only stores one ship, the `@p` of whoever you're chatting with. It only has two kids: `/path/to/this/dm/theirs` and `/path/to/this/dm/mine`. At these two paths it uses the `/imp/message-pub` and `/imp/message-sub` primitives to store the state of `/theirs` and `/mine`. -```hoon +``` $% [%initiate partner=ship provider=pith] [%invited partner=ship dm=pith] :: @@ -296,9 +307,10 @@ $% [%initiate partner=ship provider=pith] - `%post`: send a DM. #### curb:neo + Cells like `[%pro %ship]` and `[%pro %message-pub]` are examples of `$curb:neo`. This is a powerful type that's beyond the remit of these tutorials, but it's worth clarifying what these cells mean. -```hoon +``` +$ curb $~ [%pro %$] $% [%or p=(list curb)] @@ -313,9 +325,10 @@ Cells like `[%pro %ship]` and `[%pro %message-pub]` are examples of `$curb:neo`. In all of the shrubs we've looked at in these tutorials we could replace every `%pro` curb with the likes of `[%only %ship]` and `[%only %message-pub]` and lose none of the functionality we've looked at. The `[%only %ship]` curb just declares that the state is exclusively a `ship`. However, the `[%pro ship]` curb says that the state can be any type *which can be converted to a `ship` through an available `/con` file*. This has implications for interoperability and state transitions we have not yet fully explored. #### form:neo + When `/imp/dm` is first created with a `%make` card, it needs to be created with some pre-defined state. The intial state it accepts has to be a `%dm-diff`. Taking a poke type as the initial state type is an unusual choice that was done as an experiment, but the result is essentially the same as a Gall agent sending a poke to itself `+on-init`. -```hoon +``` ++ init |= old=(unit pail:neo) ^- (quip card:neo pail:neo) @@ -354,7 +367,7 @@ The `+poke` arm handles `%acked` and `%post` pokes. DM state is symmetrical: both ships are publishing to each other and subscribed to each other. When we receive an `%acked` poke, we create an `/imp/message-sub` to subscribe to DMs from the "publisher", which is whoever we're going to talk to. When we receive a `%post` poke, we add that new post to our `/pub` shrub and the other ship's `/imp/message-sub` will mirror it in its own state. -```hoon +``` ++ poke |= [=stud:neo vax=vase] ^- (quip card:neo pail:neo) @@ -381,12 +394,14 @@ DM state is symmetrical: both ships are publishing to each other and subscribed ``` ### /imp/groupchat + `/imp/groupchat` uses exactly the same primitives as `/imp/dm` for publishing and subscribing to messages. The only difference is that it's negotiating state between several ships using a one-to-many flow, rather than mirroring state between two ships. #### kook:neo + Like `/imp/dm`, `/imp/groupchat` just defines two kids at `/.../pub` and `/.../sub` to do most of the heavy lifting for it. -```hoon +``` ++ state [%pro %groupchat] ++ poke (sy %groupchat-diff ~) ++ kids @@ -405,7 +420,7 @@ Like `/imp/dm`, `/imp/groupchat` just defines two kids at `/.../pub` and `/.../s The state is a `groupchat`, which is just a set of members, pending members, and a `pith` for the location of this chat in the namespace. -```hoon +``` $: members=(set ship) pending=(set ship) host=pith @@ -420,7 +435,7 @@ $: members=(set ship) - `%post-to-host`: Send a post to the groupchat's host. - `%host-to-pub`: Send a post from the groupchat's host to their publisher shrub. -```hoon +``` $% [%invite =ship provider=pith] [%remove =ship] [%invited host=pith] @@ -431,11 +446,12 @@ $% [%invite =ship provider=pith] ``` #### +init + When it's initialized, `/imp/groupchat` has either been created by a host on their own ship, or it's been created in response to an invitation from the host. If it has no state, it creates the new chat with `%message-pub` and `%message-sub` providers. If it does have some initial state, it assumes it's being created by a foreign host ship and takes that state to be the chat history. It only needs to create a `%message-sub` to receive new messages from the publisher. -```hoon +``` ++ init |= old=(unit pail:neo) ^- (quip card:neo pail:neo) @@ -467,13 +483,14 @@ If it has no state, it creates the new chat with `%message-pub` and `%message-su ``` #### +poke + In the `+poke` arm we handle `%invite`, `%remove`, `%acked`, and `%post-to-host`, which is mostly a wrapper around `%host-to-pub`. Even though it doesn't handle messages — just access control — `/imp/groupchat` has some state to manage. Moreso than anything we've seen before, the below should look a lot like a Gall agent. Now is a good time to address when developers should store data inside a shrub's state vs. storing it as a kid. There's no right answer, but a good rule of thumb would be "would any other shrub care about this piece of data?" If so, it's more readily available at its own node in the namespace; if not, there's no downside to storing it in the shrub's internal state. You don't want to have to handle kids' state change just to do basic bookkeeping. -```hoon +``` ++ poke |= [=stud:neo vax=vase] ^- (quip card:neo pail:neo) @@ -531,14 +548,16 @@ Now is a good time to address when developers should store data inside a shrub's ``` ### /imp/messenger + `/imp/messenger` is the top-level interface through which users can create, post in, and manage groupchats and DMs. This is the shrub that corresponds to the main "Messenger" UI within Sky. This is a nice way to handle groupchats and DMs all in one place, but it's also a requirement of the way this system is built. There's a chicken-and-egg problem with DMs where ~tex can't invite ~mex to a DM chat unless ~mex already has a DM chat (`/imp/dm`) with ~tex in which to receieve that poke, so DMs rely on `/imp/messenger` to negotiate that with the `%new-dm` poke. #### kook:neo + Messenger has no state. This shrub is just an interface for creating groupchats and DMs, which are its kids. If those kids are `/imp/dm`s they take `%dm-diff`s, and if they're `/imp/groupchat`s they take `%groupchat-diff`s. -```hoon +``` ++ state [%pro %sig] ++ poke (sy %dm-diff %groupchat-diff %messenger-diff ~) ++ kids @@ -557,7 +576,7 @@ Messenger has no state. This shrub is just an interface for creating groupchats Messenger only supports three user actions: creating a new DM, creating a new groupchat, and inviting someone to a groupchat. Everything else is handled by this shrub's kids. -```hoon +``` $% [%new-dm partner=ship] [%new-groupchat name=@t invites=(set ship)] [%invite-to-groupchat name=@t invites=(set ship)] @@ -565,9 +584,10 @@ $% [%new-dm partner=ship] ``` #### +init + Messenger does nothing much on `+init`. Here's `%sig` again, this time head-tagging an empty `pail:neo`. This shrub stores no state, so we can't inject some when we initialize it. -```hoon +``` ++ init |= old=(unit pail:neo) ^- (quip card:neo pail:neo) @@ -575,9 +595,10 @@ Messenger does nothing much on `+init`. Here's `%sig` again, this time head-tagg ``` #### +poke + `/imp/messenger` takes `%messenger-diff`s, but it also takes `%dm-diff`s and `%groupchat-diff`s and, if they're invites to those chats, `%make`s the chat at the right location in the namespace. -```hoon +``` ++ poke |= [=stud:neo vax=vase] ^- (quip card:neo pail:neo)