wip: implement feedback

This commit is contained in:
bonbud-macryg 2024-06-13 12:03:26 +01:00
parent faf07c7039
commit 74e9c00d4f

View File

@ -209,19 +209,72 @@ The above is mostly self-explanatory, but its worth expanding on `stem:neo` a
==
```
The `stem` is head-tagged with a `care:neo`. If the shrubs dependent is listening with a `%z` care, it can still distinguish between `stem`s relating to the dependency, its kids, or its other descendants.
The `stem` is head-tagged with a `care:neo`. Even if we're listening to a shrub with a `%z` care, we can still distinguish between stems relating to the dependency, its kids, or other descendants.
All stems come with an `ever`, which contains version numbers for the dependency and its descendants. `%x` `stem`s come with a `pail` with the new state of the dependency shrub. The `%y` and `%z` `stem`s come with a `pail` for the dependency shrub, and a map containing all the relevant descendants of that dependency shrub. The `pith` keys in that map give us the locations of the descendants, and the `[=ever =mode =pail]` values give us their own version numbers, their `mode`, and their current state.
Stems tagged with a `%y` or `%z` care come with an `ever`, which contains version numbers for the dependency and its descendants.
Stems tagged with `%x` come with a `pail` with the new state of the dependency. Stems tagged with `%y` and `%z` come with a `pail:neo` for the dependency, and a map containing all state changes for the descendants of that dependency. The `pith` keys in that map give us the location of each descendant that has changed, and the `[=ever =mode =pail]` values give us those descendants' version numbers, their `mode`, and their current state.
The `mode` of these kids is either `%add`, `%dif`, or `%del`. If its `%add`, the dependency is telling us its a new kid. If `%dif`, the kid isnt new but its state has changed. If `%del`, its 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.
### DMs in shrubbery
If you wanted to implement 1-on-1 DMs in your shrub, you could just include `/imp/dm` in your `+deps` arm and send `%make` and `%poke` cards to it. If that doesn't do what you need, you could base your own DM functionality on this.
### /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`.
`/imp/dm` is short and simple enough that it might be nice to show an equivalent `/app/dm` Gall agent, but there isn't one. `/imp/dm` delegates a lot of functionality to service providers in a way that Gall apps theoretically could, but in practice never do. With the constraints they can set in the `+kids` and `+deps` arms, shrubs can make guarantees about the functionality of third-party service providers in other shrubs and/or other desks. Those service providers can make breaking changes to that functionality, but as long as they have some way of converting data to the type that their parents or dependents accept, those shrubs will still be able to interoperate.
#### 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
:+ ~ %y
%- ~(gas by *lads:neo)
:~ :- [|/%da |]
[pro/%message (sy %sig ~)]
==
++ deps *deps:neo
++ form
...
```
#### +init
Like `/imp/messenger`, `/imp/message-pub` takes no state.
```hoon
++ init
|= old=(unit pail:neo)
^- (quip card:neo pail:neo)
[~ sig/!>(~)]
```
#### +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)
?> =(our ship.src):bowl
:_ state
?+ stud !!
%message
=/ msg !<(message vax)
:~ :- (welp here.bowl ~[da/now.msg])
[%make %message `message/vax ~]
==
%txt
=/ contents=@t !<(txt vax)
:~ :- (welp here.bowl ~[da/now.bowl])
[%make %message `message/!>([ship.src.bowl now.bowl contents]) ~]
==
==
```
### /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
@ -243,7 +296,7 @@ If you wanted to implement 1-on-1 DMs in your shrub, you could just include `/im
...
```
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`. It chooses the service providers at `/imp/message-pub` and `/imp/message-sub` to store the state of `/theirs` and `/mine`, while setting no constraints on the types of poke those service providers can take. In other words, it delegates the functionality to two new instances of `/imp/message-pub` and `/imp/message-sub` rather than subscribing to existing Gall agents that may or may not exist.
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]
@ -256,8 +309,8 @@ $% [%initiate partner=ship provider=pith]
```
`/imp/dm` takes four pokes: `%initiate`, `%invited`, `%acked`, and `%post`.
- `%initiate`: we initiate a DM, specifying the other ship and the service provider we want to use.
- `%invited`: someone invites us to a DM, specifying their `@p` and the `pith` for us to send DMs to them.
- `%initiate`: we initiate a DM, specifying the other ship and the `/message-pub` primitive we want to use.
- `%invited`: someone invites us to a DM, giving their `@p` and the `pith` for us to send DMs to them.
- `%acked`: acknowledge creation of a DM.
- `%post`: send a DM.
@ -279,7 +332,9 @@ 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`. This is a little unusual, but it's essentially the same as a Gall agent sending a poke to itself `+on-init`, except the poke is defined by the external service provider. Since the service provider could be on our ship or someone else's, the `+init` arm takes both `%initiate` and `%invited` pokes. The `+init` arm is the only context in which these pokes are handles.
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`.
{Key point to draw attention to is the chicken-and-egg problem where we need a higher-level thing (aka "service provider" aka messenger) in order to start a DM with someone, because their DM doesn't exist yet and needs to be spawned, and its sub can only be created after our pub has been, etc}
```hoon
++ init
@ -344,11 +399,11 @@ The `+poke` arm handles `%acked` and `%post` pokes. When we receive an `%acked`,
==
```
### Group chats in shrubbery
`/imp/groupchat` uses exactly the same service providers 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 carefully mirroring state between two ships.
### /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` for service providers.
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]
@ -395,7 +450,7 @@ $% [%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 on a new member's ship by the host.
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.
@ -435,9 +490,9 @@ 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 antyhing we've seen before, the below should look a lot like a Gall agent.
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.
{what's the heuristic for "should I store this data in "top-level" shrub state or in the namespace below it?}
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
@ -497,7 +552,7 @@ Even though it doesn't handle messages — just access control — `/imp/groupch
```
### Messenger shrub
`/imp/messenger` is the top-level interface through which users can create, post in, and manage groupchats and DMs. This is the only part of the system that has a UI.
`/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.
#### 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.
@ -543,8 +598,6 @@ 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.
{what is the difference between `%invite-to-groupchat` and `%invited` `%groupchat-diff`?}
```hoon
++ poke
|= [=stud:neo vax=vase]
@ -594,56 +647,3 @@ Messenger does nothing much on `+init`. Here's `%sig` again, this time head-tagg
==
==
```
### /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
:+ ~ %y
%- ~(gas by *lads:neo)
:~ :- [|/%da |]
[pro/%message (sy %sig ~)]
==
++ deps *deps:neo
++ form
...
```
#### +init
Like `/imp/messenger`, `/imp/message-pub` takes no state.
```hoon
++ init
|= old=(unit pail:neo)
^- (quip card:neo pail:neo)
[~ sig/!>(~)]
```
#### +poke
All that happens in the `+poke` arm is this shrub creating `%message`s below it in the namespace. {But in what context are we receiving well-formed `%message`s and when are we receiving `%txt`s which we have to use to construct valid `%message`s?}
```hoon
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
?> =(our ship.src):bowl
:_ state
?+ stud !!
%message
=/ msg !<(message vax)
:~ :- (welp here.bowl ~[da/now.msg])
[%make %message `message/vax ~]
==
%txt
=/ contents=@t !<(txt vax)
:~ :- (welp here.bowl ~[da/now.bowl])
[%make %message `message/!>([ship.src.bowl now.bowl contents]) ~]
==
==
```