@@ -1,2626 +0,0 @@
5−aliases = ["/docs/learn/arvo/clay/"]
9−`%clay` is version-controlled, referentially-transparent, and global.
10−While this filesystem is stored in `%clay`, it is mirrored to Unix for
11−convenience. Unix tells `%clay`s whenever a file changes in the Unix
12−copy of the filesystem so that the change may be applied. `%clay` tells
13−unix whenever an app or vane changes the filesystem so that the change
14−can be effected in Unix. Apps and vanes may use `%clay` to write to the
15−filesystem, query it, and subscribe to changes in it. Ford and gall use
16−`%clay` to serve up apps and web pages.
18−`%clay` includes three components. First is the filesystem/version
19−control algorithms, which are mostly defined in `++ze` and `++zu` in
20−zuse. Second is the write, query, and subscription logic. Finally, there
21−is the logic for communicating requests to, and receiving requests from,
26−Clay is the primary filesystem for the arvo operating system,
27−which is the core of an urbit. The architecture of clay is
28−intrinsically connected with arvo, but we assume no knowledge of
29−either arvo or urbit. We will point out only those features of
30−arvo that are necessary for an understanding of clay, and we will
31−do so only when they arise.
33−The first relevant feature of arvo is that it is a deterministic
34−system where input and output are defined as a series of events
35−and effects. The state of arvo is simply a function of its event
36−log. None of the effects from an event are emitted until the
37−event is entered in the log and persisted, either to disk or
38−another trusted source of persistence, such as a Kafka cluster.
39−Consequently, arvo is a single-level store: everything in its
42−In a more traditional OS, everything in RAM can be erased at any
43−time by power failure, and is always erased on reboot. Thus, a
44−primary purpose of a filesystem is to ensure files persist across
45−power failures and reboots. In arvo, both power failures and
46−reboots are special cases of suspending computation, which is
47−done safely since our event log is already persistent. Therefore,
48−clay is not needed in arvo for persistence. Why, then, do we have a
49−filesystem? There are two answers to this question.
51−First, clay provides a filesystem tree, which is a convenient
52−user interface for some applications. Unix has the useful concept
53−of virtual filesystems, which are used for everything from direct
54−access to devices, to random number generators, to the /proc
55−tree. It is easy and intuitive to read from and write to a
58−Second, clay has a distributed revision-control system baked into
59−it. Traditional filesystems are not revision controlled, so
60−userspace software -- such as git -- is written on top of them to
61−do so. clay natively provides the same functionality as modern
64−clay has two other unique properties that we'll cover later on:
65−it supports typed data and is referentially transparent.
69−Every urbit has one or more "desks", which are independently
70−revision-controlled branches. Each desk contains its own mark
71−definitions, apps, doc, and so forth.
73−Traditionally, an urbit has at least a base and a home desk. The
74−base desk has all the system software from the distribution. the
75−home desk is a fork of base with all the stuff specific to the
78−A desk is a series of numbered commits, the most recent of which
79−represents the current state of the desk. A commit is composed of
80−(1) an absolute time when it was created, (2) a list of zero or
81−more parents, and (3) a map from paths to data.
83−Most commits have exactly one parent, but the initial commit on a
84−desk may have zero parents, and merge commits have more than one
87−The non-meta data is stored in the map of paths to data. It's
88−worth noting that no constraints are put on this map, so, for
89−example, both /a/b and /a/b/c could have data. This is impossible
90−in a traditional Unix filesystem since it means that /a/b is both
91−a file and a directory. Conventionally, the final element in the
92−path is its mark -- much like a filename extension in Unix. Thus,
93−/doc/readme.md in Unix is stored as /doc/readme/md in urbit.
95−The data is not stored directly in the map; rather, a hash of the
96−data is stored, and we maintain a master blob store. Thus, if the
97−same data is referred to in multiple commits (as, for example,
98−when a file doesn't change between commits), only the hash is
101−In the master blob store, we either store the data directly, or
102−else we store a diff against another blob. The hash is dependent
103−only on the data within and not on whether or not it's stored
104−directly, so we may on occasion rearrange the contents of the
105−blob store for performance reasons.
107−Recall that a desk is a series of numbered commits. Not every
108−commit in a desk must be numbered. For example, if the base desk
109−has had 50 commits since home was forked from it, then a merge
110−from base to home will only add a single revision number to home,
111−although the full commit history will be accessible by traversing
112−the parentage of the individual commits.
114−We do guarantee that the first commit is numbered 1, commits are
115−numbered consecutively after that (i.e. there are no "holes"),
116−the topmost commit is always numbered, and every numbered commit
117−is an ancestor of every later numbered commit.
119−There are three ways to refer to particular commits in the
120−revision history. Firstly, one can use the revision number.
121−Secondly, one can use any absolute time between the one numbered
122−commit and the next (inclusive of the first, exclusive of the
123−second). Thirdly, every desk has a map of labels to revision
124−numbers. These labels may be used to refer to specific commits.
126−Additionally, clay is a global filesystem, so data on other urbit
127−is easily accessible the same way as data on our local urbit. In
128−general, the path to a particular revision of a desk is
129−/~urbit-name/desk-name/revision. Thus, to get /try/readme/md
130−from revision 5 of the home desk on ~sampel-sipnym, we refer to
131−/~sampel-sipnym/home/5/try/readme/md. Clay's namespace is thus
132−global and referentially transparent.
136−### A Typed Filesystem
138−Since clay is a general filesystem for storing data of arbitrary
139−types, in order to revision control correctly it needs to be
140−aware of types all the way through. Traditional revision control
141−does an excellent job of handling source code, so for source code
142−we act very similar to traditional revision control. The
143−challenge is to handle other data similarly well.
145−For example, modern VCSs generally support "binary files", which
146−are files for which the standard textual diffing, patching, and
147−merging algorithms are not helpful. A "diff" of two binary files
148−is just a pair of the files, "patching" this diff is just
149−replacing the old file with the new one, and "merging"
150−non-identical diffs is always a conflict, which can't even be
151−helpfully annotated. Without knowing anything about the structure
152−of a blob of data, this is the best we can do.
154−Often, though, "binary" files have some internal structure, and
155−it is possible to create diff, patch, and merge algorithms that
156−take advantage of this structure. An image may be the result of a
157−base image with some set of operations applied. With algorithms
158−aware of this set of operations, not only can revision control
159−software save space by not having to save every revision of the
160−image individually, these transformations can be made on parallel
161−branches and merged at will.
163−Suppose Alice is tasked with touching up a picture, improving the
164−color balance, adjusting the contrast, and so forth, while Bob
165−has the job of cropping the picture to fit where it's needed and
166−adding textual overlay. Without type-aware revision control,
167−these changes must be made serially, requiring Alice and Bob to
168−explicitly coordinate their efforts. With type-aware revision
169−control, these operations may be performed in parallel, and then
170−the two changesets can be merged programmatically.
172−Of course, even some kinds of text files may be better served by
173−diff, patch, and merge algorithms aware of the structure of the
174−files. Consider a file containing a pretty-printed JSON object.
175−Small changes in the JSON object may result in rather significant
176−changes in how the object is pretty-printed (for example, by
177−addding an indentation level, splitting a single line into
180−A text file wrapped at 80 columns also reacts suboptimally with
181−unadorned Hunt-McIlroy diffs. A single word inserted in a
182−paragraph may push the final word or two of the line onto the
183−next line, and the entire rest of the paragraph may be flagged as
184−a change. Two diffs consisting of a single added word to
185−different sentences may be flagged as a conflict. In general,
186−prose should be diffed by sentence, not by line.
188−As far as the author is aware, clay is the first generalized,
189−type-aware revision control system. We'll go into the workings
190−of this system in some detail.
194−Central to a typed filesystem is the idea of types. In clay, we
195−call these "marks". A mark is a file that defines a type,
196−conversion routines to and from the mark, and diff, patch, and
199−For example, a `%txt` mark may be a list of lines of text, and it
200−may include conversions to `%mime` to allow it to be serialized
201−and sent to a browswer or to the unix filesystem. It will also
202−include Hunt-McIlroy diff, patch, and merge algorithms.
204−A `%json` mark would be defined as a json object in the code, and
205−it would have a parser to convert from `%txt` and a printer to
206−convert back to `%txt`. The diff, patch, and merge algorithms are
207−fairly straightforward for json, though they're very different
210−More formally, a mark is a core with three arms, `++grab`,
211−`++grow`, and `++grad`. In `++grab` is a series of functions to
212−convert from other marks to the given mark. In `++grow` is a
213−series of functions to convert from the given mark to other
214−marks. In `++grad` is `++diff`, `++pact`, `++join`, and `++mash`.
216−The types are as follows, in an informal pseudocode:
220− ++ mime: <mime> -> <mark-type>
221− ++ txt: <txt> -> <mark-type>
224− ++ mime: <mark-type> -> <mime>
225− ++ txt: <mark-type> -> <txt>
228− ++ diff: (<mark-type>, <mark-type>) -> <diff-type>
229− ++ pact: (<mark-type>, <diff-type>) -> <mark-type>
230− ++ join: (<diff-type>, <diff-type>) -> <diff-type> or NULL
231− ++ mash: (<diff-type>, <diff-type>) -> <diff-type>
234−These types are basically what you would expect. Not every mark
235−has each of these functions defined -- all of them are optional
238−In general, for a particular mark, the `++grab` and `++grow` entries
239−(if they exist) should be inverses of each other.
241−In `++grad`, `++diff` takes two instances of a mark and produces
242−a diff of them. `++pact` takes an instance of a mark and patches
243−it with the given diff. `++join` takes two diffs and attempts to
244−merge them into a single diff. If there are conflicts, it
245−produces null. `++mash` takes two diffs and forces a merge,
246−annotating any conflicts.
248−In general, if `++diff` called with A and B produces diff D, then
249−`++pact` called with A and D should produce B. Also, if `++join`
250−of two diffs does not produce null, then `++mash` of the same
251−diffs should produce the same result.
253−Alternately, instead of `++diff`, `++pact`, `++join`, and
254−`++mash`, a mark can provide the same functionality by defining
255−`++sted` to be the name of another mark to which we wish to
256−delegate the revision control responsibilities. Then, before
257−running any of those functions, clay will convert to the other
258−mark, and convert back afterward. For example, the `%hoon` mark
259−is revision-controlled in the same way as `%txt`, so its `++grad`
260−is simply `++sted %txt`. Of course, `++txt` must be defined in
261−`++grow` and `++grab` as well.
263−Every file in clay has a mark, and that mark must have a
264−fully-functioning `++grad`. Marks are used for more than just
265−clay, and other marks don't need a `++grad`, but if a piece of
266−data is to be saved to clay, we must know how to revision-control
269−Additionally, if a file is to be synced out to unix, then it must
270−have conversion routines to and from the `%mime` mark.
274−### Reading and Subscribing
276−When reading from Clay, there are three types of requests. A
277−`%sing` request asks for data at single revsion. A `%next`
278−request asks to be notified the next time there's a change to
279−given file. A `%many` request asks to be notified on every
280−change in a desk for a range of changes.
282−For `%sing` and `%next`, there are generally three things to be
283−queried. A `%u` request simply checks for the existence of a
284−file at a path. A `%x` request gets the data in the file at a
285−path. A `%y` request gets a hash of the data in the file at the
286−path combined with all its children and their data. Thus, `%y`
287−of a node changes if it or any of its children change.
289−A `%sing` request is fulfilled immediately if possible. If the
290−requested revision is in the future, or is on another ship for
291−which we don't have the result cached, we don't respond
292−immediately. If the requested revision is in the future, we wait
293−until the revision happens before we respond to the request. If
294−the request is for data on another ship, we pass on the request
295−to the other ship. In general, Clay subscriptions, like most
296−things in Urbit, aren't guaranteed to return immediately.
297−They'll return when they can, and they'll do so in a
298−referentially transparent manner.
300−A `%next` request checks query at the given revision, and it
301−produces the result of the query the next time it changes, along
302−with the revsion number when it changes. Thus, a `%next` of a
303−`%u` is triggered when a file is added or deleted, a `%next` of a
304−`%x` is triggered when a file is added, deleted, or changed, and
305−a `%next` of a `%y` is triggered when a file or any of its
306−children is added, deleted, or changed.
308−A `%many` request is triggered every time the given desk has a
309−new revision. Unlike a `%next`, a `%many` has both a start and
310−an end revsion, after which it stops returning. For `%next`, a
311−single change is reported, and if the caller wishes to hear of
312−the next change, it must resubscribe. For `%many`, every revsion
313−from the start to the end triggers a response. Since a `%many`
314−request doesn't ask for any particular data, there aren't `%u`,
315−`%x`, and `%y` versions for it.
319−One of the primary functions of clay is as a convenient user
320−interface. While tools exist to use clay from within urbit, it's
321−often useful to be able to treat clay like any other filesystem
322−from the Unix perspective -- to "mount" it, as it were.
324−From urbit, you can run `|mount /path/to/directory %mount-point`,
325−and this will mount the given clay directory to the mount-point
326−directory in Unix. Every file is converted to `%mime` before it's
327−written to Unix, and converted back when read from Unix. The
328−entire directory is watched (a la Dropbox), and every change is
329−committed once you run `|commit %mount-point`.
333−Merging is a fundamental operation for a distributed revision
334−control system. At their root, clay's merges are similar to
335−git's, but with some additions to accomodate typed data. There
336−are seven different merge strategies.
338−Throughout our discussion, we'll say that the merge is from
339−Alice's desk to Bob's. Recall that a commit is a date (for all
340−new commits this will be the current date), a list of parents,
343−A `%init` merge should be used iff it's the first commit to a
344−desk. The head of Alice's desk is used as the number 1 commit to
345−Bob's desk. Obviously, the ancestry remains intact through
346−traversing the parentage of the commit even though previous
347−commits are not numbered for Bob's desk.
349−A `%this` merge means to keep what's in Bob's desk, but join the
350−ancestry. Thus, the new commit has the head of each desk as
351−parents, but the data is exactly what's in Bob's desk. For those
352−following along in git, this is the 'ours' merge strategy, not
353−the '--ours' option to the 'recursive' merge strategy. In other
354−words, even if Alice makes a change that does not conflict with
355−Bob, we throw it away. It's Bob's way or the highway.
357−A `%that` merge means to take what's in Alice's desk, but join
358−the ancestry. This is the reverse of `%this`.
360−A `%fine` merge is a "fast-forward" merge. This succeeds iff one
361−head is in the ancestry of the other. In this case, we use the
362−descendant as our new head.
364−For `%meet`, `%mate`, and `%meld` merges, we first find the most
365−recent common ancestor to use as our merge base. If we have no
366−common ancestors, then we fail. If we have more than one most
367−recent common ancestor, then we have a criss-cross situation,
368−which should be handled delicately. At present, we delicately
369−throw up our hands and give up, but something akin to git's
370−'recursive' strategy should be implemented in the future.
372−There's a functional inclusion ordering on `%fine`, `%meet`,
373−`%mate`, and `%meld` such that if an earlier strategy would have
374−succeeded, then every later strategy will produce the same
375−result. Put another way, every earlier strategy is the same as
376−every later strategy except with a restricted domain.
378−A `%meet` merge only succeeds if the changes from the merge base
379−to Alice's head (hereafter, "Alice's changes") are in different
380−files than Bob's changes. In this case, the parents are both
381−Alice's and Bob's heads, and the data is the merge base plus
382−Alice's changed files plus Bob's changed files.
384−A `%mate` merge attempts to merge changes to the same file when
385−both Alice and bob change it. If the merge is clean, we use it;
386−otherwise, we fail. A merge between different types of changes --
387−for example, deleting a file vs changing it -- is always a
388−conflict. If we succeed, the parents are both Alice's and Bob's
389−heads, and the data is the merge base plus Alice's changed files
390−plus Bob's changed files plus the merged files.
392−A `%meld` merge will succeed even if there are conflicts. If
393−there are conflicts in a file, then we use the merge base's
394−version of that file, and we produce a set of files with
395−conflicts. The parents are both Alice's and Bob's heads, and the
396−data is the merge base plus Alice's changed files plus Bob's
397−changed files plus the successfully merged files plus the merge
398−base's version of the conflicting files.
400−That's the extent of the merge options in clay proper. In
401−userspace there's a final option `%auto`, which is the most
402−common. `%auto` checks to see if Bob's desk exists, and if it
403−doesn't we use a `%init` merge. Otherwise, we progressively try
404−`%fine`, `%meet`, and `%mate` until one succeeds.
406−If none succeed, we merge Bob's desk into a scratch desk. Then,
407−we merge Alice's desk into the scratch desk with the `%meld`
408−option to force the merge. For each file in the produced set of
409−conflicting files, we call the `++mash` function for the
410−appropriate mark, which annotates the conflicts if we know how.
412−Finally, we display a message to the user informing them of the
413−scratch desk's existence, which files have annotated conflicts,
414−and which files have unannotated conflicts. When the user has
415−resolved the conflicts, they can merge the scratch desk back into
416−Bob's desk. This will be a `%fine` merge since Bob's head is in
417−the ancestry of the scratch desk.
421−Tracking and staying in sync with another desk is another
422−fundamental operation. We call this "autosync". This doesn't mean
423−simply mirroring a desk, since that wouldn't allow local changes.
424−We simply want to apply changes as they are made upstream, as
425−long as there are no conflicts with local changes.
427−This is implemented by watching the other desk, and, when it has
428−changes, merging these changes into our desk with the usual merge
431−Note that it's quite reasonable for two desks to be autosynced to
432−each other. This results in any change on one desk being mirrored
433−to the other and vice versa.
435−Additionally, it's fine to set up an autosync even if one desk,
436−the other desk, or both desks do not exist. The sync will be
437−activated when the upstream desk comes into existence and will
438−create the downstream desk if needed.
442−The first part of this section will be reference documentation for the data
443−types used by our filesystem. In fact, as a general guide, we recommend
444−reading and attempting to understand the data structures used in any
445−Hoon code before you try to read the code itself. Although complete
446−understanding of the data structures is impossible without seeing them
447−used in the code, an 80% understanding greatly clarifies the code. As
448−another general guide, when reading Hoon, it rarely pays off to
449−understand every line of code when it appears. Try to get the gist of
450−it, and then move on. The next time you come back to it, it'll likely
451−make a lot more sense.
453−After a description of the data models, we'll give an overview of the
454−interface that vanes and applications can use to interact with the
457−Finally, we'll dive into the code and the algorithms themselves. You
462−As you're reading through this section, remember you can always come
463−back to this when you run into these types later on. You're not going to
464−remember everything the first time through, but it is worth reading, or
465−at least skimming, this so that you get a rough idea of how our state is
468−The types that are certainly worth reading are `++raft`, `++room`,
469−`++dome:clay`, `++ankh:clay`, `++rung:clay`, `++rang:clay`, `++blob:clay`, `++yaki:clay`, and `++nori:clay`
470−(possibly in that order). All in all, though, this section isn't too
471−long, so many readers may wish to quickly read through all of it. If you
472−get bored, though, just skip to the next section. You can always come
473−back when you need to.
475−### `++raft`, formal state
478− ++ raft :: filesystem
479− $: fat=(map ship room) :: domestic
480− hoy=(map ship rung) :: foreign
485−This is the state of our vane. Anything that must be remembered between
486−calls to clay is stored in this state.
488−`fat` is the set of domestic servers. This stores all the information
489−that is specfic to a particular ship on this pier. The keys to this map
490−are the ships on the current pier. all the information that is specific
491−to a particular foreign ship. The keys to this map are all the ships
492−whose filesystems we have attempted to access through clay.
494−`ran` is the store of all commits and deltas, keyed by hash. The is
495−where all the "real" data we know is stored; the rest is "just
498−#### `++room`, filesystem per domestic ship
501− ++ room :: fs per ship
502− $: hun=duct :: terminal duct
503− hez=(unit duct) :: sync duch
504− dos=(map desk dojo) :: native desk
508−This is the representation of the filesystem of a ship on our pier.
510−`hun` is the duct we use to send messages to dill to display
511−notifications of filesystem changes. Only `%note` gifts should be
512−produced along this duct. This is set by the `%init` kiss.
514−`hez`, if present, is the duct we use to send sync messages to unix so
515−that they end up in the pier unix directory. Only `%ergo` gifts should
516−be producd along this duct. This is set by `%into` and `%invo` kisses.
518−`dos` is a well-known operating system released in 1981. It is also the
519−set of desks on this ship, mapped to their data.
521−#### `++desk`, filesystem branch
524− ++ desk ,@tas :: ship desk case spur
527−This is the name of a branch of the filesystem. The default desks are
528−"arvo", "main", and "try". More may be created by simply referencing
529−them. Desks have independent histories and states, and they may be
530−merged into each other.
532−### `++dojo`, domestic desk state
535− ++ dojo ,[p=cult q=dome] :: domestic desk state
538−This is the all the data that is specific to a particular desk on a
539−domestic ship. `p` is the set of subscribers to this desk and `q` is the
542−#### `++cult`, subscriptions
545− ++ cult (map duct rave) :: subscriptions
548−This is the set of subscriptions to a particular desk. The keys are the
549−ducts from where the subscriptions requests came. The results will be
550−produced along these ducts. The values are a description of the
551−requested information.
553−#### `++rave:clay`, general subscription request
556− ++ rave :: general request
557− $% [& p=mood] :: single request
558− [| p=moat] :: change range
562−This represents a subscription request for a desk. The request can be
563−for either a single item in the desk or else for a range of changes on
566−### `++rove`, stored general subscription request
569− ++ rove (each mood moot) :: stored request
572−When we store a request, we store subscriptions with a little extra
573−information so that we can determine whether new versions actually
574−affect the path we're subscribed to.
576−#### `++mood:clay`, single subscription request
579− ++ mood ,[p=care q=case r=path] :: request in desk
582−This represents a request for the state of the desk at a particular
583−commit, specfied by `q`. `p` specifies what kind of information is
584−desired, and `r` specifies the path we are requesting.
586−#### `++moat:clay`, range subscription request
589− ++ moat ,[p=case q=case r=path] :: change range
592−This represents a request for all changes between `p` and `q` on path
593−`r`. You will be notified when a change is made to the node referenced
594−by the path or to any of its children.
596−### `++moot`, stored range subscription request
599− ++ moot ,[p=case q=case r=path s=(map path lobe)] ::
602−This is just a `++moat:clay` plus a map of paths to lobes. This map
603−represents the data at the node referenced by the path at case `p`, if
604−we've gotten to that case (else null). We only send a notification along
605−the subscription if the data at a new revision is different than it was.
607−### `++care:clay`, clay submode
610− ++ care ?(%u %v %w %x %y %z) :: clay submode
613−This specifies what type of information is requested in a subscription
616−`%u` requests the `++rang:clay` at the current moment. Because this
617−information is not stored for any moment other than the present, we
618−crash if the `++case:clay` is not a `%da` for now.
620−`%v` requests the `++dome:clay` at the specified commit.
622−`%w` requests the revsion number of the desk.
624−`%x` requests the file at a specified path at the specified commit. If
625−there is no node at that path or if the node has no contents (that is,
626−if `q:ankh` is null), then this produces null.
628−`%y` requests a `++arch` of the specfied commit at the specified path.
630−`%z` requests the `++ankh` of the specified commit at the specfied path.
632−### `++arch`, shallow filesystem node
635− ++ arch ,[p=@uvI q=(unit ,@uvI) r=(map ,@ta ,~)] :: fundamental node
638−This is analogous to `++ankh:clay` except that the we have neither our
639−contents nor the ankhs of our children. The other fields are exactly the
640−same, so `p` is a hash of the associated ankh, `u.q`, if it exists, is a
641−hash of the contents of this node, and the keys of `r` are the names of
642−our children. `r` is a map to null rather than a set so that the
643−ordering of the map will be equivalent to that of `r:ankh`, allowing
644−efficient conversion.
646−#### `++case:clay`, specifying a commit
649− ++ case :: ship desk case spur
650− $% [%da p=@da] :: date
651− [%tas p=@tas] :: label
652− [%ud p=@ud] :: number
656−A commit can be referred to in three ways: `%da` refers to the commit
657−that was at the head on date `p`, `%tas` refers to the commit labeled
658−`p`, and `%ud` refers to the commit numbered `p`. Note that since these
659−all can be reduced down to a `%ud`, only numbered commits may be
660−referenced with a `++case:clay`.
662−#### `++dome:clay`, desk data
665− ++ dome :: project state
666− $: ang=agon :: pedigree
669− hit=(map ,@ud tako) :: changes by id
670− lab=(map ,@tas ,@ud) :: labels
674−This is the data that is actually stored in a desk.
676−`ang` is unused and should be removed.
678−`ank` is the current state of the desk. Thus, it is the state of the
679−filesystem at revison `let`. The head of a desk is always a numbered
682−`let` is the number of the most recently numbered commit. This is also
683−the total number of numbered commits.
685−`hit` is a map of numerical ids to hashes of commits. These hashes are
686−mapped into their associated commits in `hut:rang:clay`. In general, the keys
687−of this map are exactly the numbers from 1 to `let`, with no gaps. Of
688−course, when there are no numbered commits, `let` is 0, so `hit` is
689−null. Additionally, each of the commits is an ancestor of every commit
690−numbered greater than this one. Thus, each is a descendant of every
691−commit numbered less than this one. Since it is true that the date in
692−each commit (`t:yaki`) is no earlier than that of each of its parents,
693−the numbered commits are totally ordered in the same way by both
694−pedigree and date. Of course, not every commit is numbered. If that
695−sounds too complicated to you, don't worry about it. It basically
696−behaves exactly as you would expect.
698−`lab` is a map of textual labels to numbered commits. Note that labels
699−can only be applied to numbered commits. Labels must be unique across a
702−#### `++ankh`, filesystem node
705− ++ ankh :: fs node (new)
706− $: p=cash :: recursive hash
707− q=(unit ,[p=cash q=*]) :: file
708− r=(map ,@ta ankh) :: folders
712−This is a single node in the filesystem. This may be file or a directory
713−or both. In earth filesystems, a node is a file xor a directory. On
714−mars, we're inclusive, so a node is a file ior a directory.
716−`p` is a recursive hash that depends on the contents of the this file or
717−directory and on any children.
719−`q` is the contents of this file, if any. `p.q` is a hash of the
720−contents while `q.q` is the data itself.
722−`r` is the set of children of this node. In the case of a pure file,
723−this is empty. The keys are the names of the children and the values
724−are, recursively, the nodes themselves.
726−#### `++cash`, ankh hash
729− ++ cash ,@uvH :: ankh hash
732−This is a 128-bit hash of an ankh. These are mostly stored within ankhs
733−themselves, and they are used to check for changes in possibly-deep
736−#### `++rung`, filesystem per neighbor ship
739− ++ rung $: rus=(map desk rede) :: neighbor desks
743−This is the filesystem of a neighbor ship. The keys to this map are all
744−the desks we know about on their ship.
746−#### `++rede`, desk state
749− ++ rede :: universal project
750− $: lim=@da :: complete to
751− qyx=cult :: subscribers
752− ref=(unit rind) :: outgoing requests
753− dom=dome :: revision state
757−This is our knowledge of the state of a desk, either foreign or
760−`lim` is the date of the last full update. We only respond to requests
761−for stuff before this time.
763−`qyx` is the list of subscribers to this desk. For domestic desks, this
764−is simply `p:dojo`, all subscribers to the desk, while in foreign desks
765−this is all the subscribers from our ship to the foreign desk.
767−`ref` is the request manager for the desk. For domestic desks, this is
768−null since we handle requests ourselves.
770−`dom` is the actual data in the desk.
772−#### `++rind`, request manager
775− ++ rind :: request manager
776− $: nix=@ud :: request index
777− bom=(map ,@ud ,[p=duct q=rave]) :: outstanding
778− fod=(map duct ,@ud) :: current requests
779− haw=(map mood (unit)) :: simple cache
783−This is the request manager for a foreign desk.
785−`nix` is one more than the index of the most recent request. Thus, it is
786−the next available request number.
788−`bom` is the set of outstanding requests. The keys of this map are some
789−subset of the numbers between 0 and one less than `nix`. The members of
790−the map are exactly those requests that have not yet been fully
793−`fod` is the same set as `bom`, but from a different perspective. In
794−particular, the values of `fod` are the same as the values of `bom`, and
795−the `p` out of the values of `bom` are the same as the keys of `fod`.
796−Thus, we can map ducts to their associated request number and `++rave:clay`,
797−and we can map numbers to their associated duct and `++rave:clay`.
799−`haw` is a map from simple requests to their values. This acts as a
800−cache for requests that have already been made. Thus, the second request
801−for a particular `++mood:clay` is nearly instantaneous.
803−#### `++rang:clay`, data store
806− ++ rang $: hut=(map tako yaki) ::
807− lat=(map lobe blob) ::
811−This is a set of data keyed by hash. Thus, this is where the "real" data
812−is stored, but it is only meaningful if we know the hash of what we're
815−`hut` is a map from hashes to commits. We often get the hashes from
816−`hit:dome:clay`, which keys them by logical id. Not every commit has an id.
818−`lat` is a map from hashes to the actual data. We often get the hashes
819−from a `++yaki`, a commit, which references this map to get the data.
820−There is no `++blob:clay` in any `++yaki:clay`. They are only accessible through
823−#### `++tako:clay`, commit reference
826− ++ tako ,@ :: yaki ref
829−This is a hash of a `++yaki:clay`, a commit. These are most notably used as
830−the keys in `hut:rang:clay`, where they are associated with the actual
831−`++yaki:clay`, and as the values in `hit:dome:clay`, where sequential ids are
832−associated with these.
834−#### `++yaki:clay`, commit
837− ++ yaki ,[p=(list tako) q=(map path lobe) r=tako t=@da] :: commit
840−This is a single commit.
842−`p` is a list of the hashes of the parents of this commit. In most
843−cases, this will be a single commit, but in a merge there may be more
844−parents. In theory, there may be an arbitrary number of parents, but in
845−practice merges have exactly two parents. This may change in the future.
846−For commit 1, there is no parent.
848−`q` is a map of the paths on a desk to the data at that location. If you
849−understand what a `++lobe:clay` and a `++blob:clay` is, then the type signature
850−here tells the whole story.
852−`r` is the hash associated with this commit.
854−`t` is the date at which this commit was made.
856−#### `++lobe:clay`, data reference
859− ++ lobe ,@ :: blob ref
862−This is a hash of a `++blob:clay`. These are most notably used in `lat:rang:clay`,
863−where they are associated with the actual `++blob:clay`, and as the values in
864−`q:yaki:clay`, where paths are associated with their data in a commit.
866−#### `++blob:clay`, data
869− ++ blob $% [%delta p=lobe q=lobe r=udon] :: delta on q
870− [%direct p=lobe q=* r=umph] ::
871− [%indirect p=lobe q=* r=udon s=lobe] ::
875−This is a node of data. In every case, `p` is the hash of the blob.
877−`%delta` is the case where we define the data by a delta on other data.
878−In practice, the other data is always the previous commit, but nothing
879−depends on this. `q` is the hash of the parent blob, and `r` is the
882−`%direct` is the case where we simply have the data directly. `q` is the
883−data itself, and `r` is any preprocessing instructions. These almost
884−always come from the creation of a file.
886−`%indirect` is both of the preceding cases at once. `q` is the direct
887−data, `r` is the delta, and `s` is the parent blob. It should always be
888−the case that applying `r` to `s` gives the same data as `q` directly
889−(with the prepreprocessor instructions in `p.r`). This exists purely for
890−performance reasons. This is unused, at the moment, but in general these
891−should be created when there are a long line of changes so that we do
892−not have to traverse the delta chain back to the creation of the file.
894−#### `++udon`, abstract delta
897− ++ udon :: abstract delta
898− $: p=umph :: preprocessor
900− $% [%a p=* q=*] :: trivial replace
901− [%b p=udal] :: atomic indel
902− [%c p=(urge)] :: list indel
903− [%d p=upas q=upas] :: tree edit
908−This is an abstract change to a file. This is a superset of what would
909−normally be called diffs. Diffs usually refer to changes in lines of
910−text while we have the ability to do more interesting deltas on
911−arbitrary data structures.
913−`p` is any preprocessor instructions.
915−`%a` refers to the trival delta of a complete replace of old data with
918−`%b` refers to changes in an opaque atom on the block level. This has
919−very limited usefulness, and is not used at the moment.
921−`%c` refers to changes in a list of data. This is often lines of text,
922−which is your classic diff. We, however, will work on any list of data.
924−`%d` refers to changes in a tree of data. This is general enough to
925−describe changes to any hoon noun, but often more special-purpose delta
926−should be created for different content types. This is not used at the
927−moment, and may in fact be unimplemented.
929−#### `++urge`, list change
932− ++ urge |*(a=_,* (list (unce a))) :: list change
935−This is a parametrized type for list changes. For example, `(urge ,@t)`
936−is a list change for lines of text.
938−#### `++unce`, change part of a list.
941− ++ unce |* a=_,* :: change part
942− $% [%& p=@ud] :: skip[copy]
943− [%| p=(list a) q=(list a)] :: p -> q[chunk]
947−This is a single change in a list of elements of type `a`. For example,
948−`(unce ,@t)` is a single change in a lines of text.
950−`%&` means the next `p` lines are unchanged.
952−`%|` means the lines `p` have changed to `q`.
954−#### `++umph`, preprocessing information
957− ++ umph :: change filter
958− $| $? %a :: no filter
962− $% [%d p=@ud] :: blocklist
966−This space intentionally left undocumented. This stuff will change once
967−we get a well-typed clay.
969−#### `++upas`, tree change
972− ++ upas :: tree change (%d)
973− $& [p=upas q=upas] :: cell
974− $% [%0 p=axis] :: copy old
975− [%1 p=*] :: insert new
976− [%2 p=axis q=udon] :: mutate!
980−This space intentionally left undocumented. This stuff is not known to
981−work, and will likely change when we get a well-typed clay. Also, this
982−is not a complicated type; it is not difficult to work out the meaning.
984−#### `++nori:clay`, repository action
987− ++ nori :: repository action
988− $% [& q=soba] :: delta
993−This describes a change that we are asking clay to make to the desk.
994−There are two kinds of changes that may be made: we can modify files or
995−we can apply a label to a commit.
997−In the `|` case, we will simply label the current commit with the given
998−label. In the `&` case, we will apply the given changes.
1000−#### `++soba:clay`, delta
1003− ++ soba ,[p=cart q=(list ,[p=path q=miso])] :: delta
1006−This describes a set of changes to make to a desk. The `cart` is simply
1007−a pair of the old hash and the new hash of the desk. The list is a list
1008−of changes keyed by the file they're changing. Thus, the paths are paths
1009−to files to be changed while `miso` is a description of the change
1012−#### `++miso:clay`, ankh delta
1015− ++ miso :: ankh delta
1016− $% [%del p=*] :: delete
1017− [%ins p=*] :: insert
1018− [%mut p=udon] :: mutate
1022−There are three kinds of changes that may be made to a node in a desk.
1023−We can insert a file, in which case `p` is the contents of the new file.
1024−We can delete a file, in which case `p` is the contents of the old file.
1025−Finally, we can mutate that file, in which case the `udon` describes the
1026−changes we are applying to the file.
1028−#### `++mizu:clay`, merged state
1031− ++ mizu ,[p=@u q=(map ,@ud tako) r=rang] :: new state
1034−This is the input to the `%merg` kiss, which allows us to perform a
1035−merge. The `p` is the number of the new head commit. The `q` is a map
1036−from numbers to commit hashes. This is all the new numbered commits that
1037−are to be inserted. The keys to this should always be the numbers from
1038−`let.dom` plus one to `p`, inclusive. The `r` is the maps of all the new
1039−commits and data. Since these are merged into the current state, no old
1040−commits or data need be here.
1042−#### `++riff:clay`, request/desist
1045− ++ riff ,[p=desk q=(unit rave)] :: request/desist
1048−This represents a request for data about a particular desk. If `q`
1049−contains a `rave`, then this opens a subscription to the desk for that
1050−data. If `q` is null, then this tells clay to cancel the subscription
1053−#### `++riot:clay`, response
1056− ++ riot (unit rant) :: response/complete
1059−A riot is a response to a subscription. If null, the subscription has
1060−been completed, and no more responses will be sent. Otherwise, the
1061−`rant` is the produced data.
1063−#### `++rant:clay`, response data
1066− ++ rant :: namespace binding
1067− $: p=[p=care q=case r=@tas] :: clade release book
1073−This is the data at a particular node in the filesystem. `p.p` specifies
1074−the type of data that was requested (and is produced). `q.p` gives the
1075−specific version reported (since a range of versions may be requested in
1076−a subscription). `r.p` is the desk. `q` is the path to the filesystem
1077−node. `r` is the data itself (in the format specified by `p.p`).
1079−### `++nako`, subscription response data
1082− ++ nako $: gar=(map ,@ud tako) :: new ids
1084− lar=(set yaki) :: new commits
1085− bar=(set blob) :: new content
1089−This is the data that is produced by a request for a range of revisions
1090−of a desk. This allows us to easily keep track of a remote repository --
1091−all the new information we need is contained in the `nako`.
1093−`gar` is a map of the revisions in the range to the hash of the commit
1094−at that revision. These hashes can be used with `hut:rang:clay` to find the
1097−`let` is either the last revision number in the range or the most recent
1098−revision number, whichever is smaller.
1100−`lar` is the set of new commits, and `bar` is the set of new content.
1104−As with all vanes, there are exactly two ways to interact with clay.
1105−`%clay` exports a namespace accessible through `.^`, which is described
1106−above under `++care:clay`. The primary way of interacting with clay, though,
1107−is by sending kisses and receiving gifts.
1110− ++ gift :: out result <-$
1111− $% [%ergo p=@p q=@tas r=@ud] :: version update
1112− [%note p=@tD q=tank] :: debug message
1113− [%writ p=riot] :: response
1115− ++ kiss :: in request ->$
1116− $% [%info p=@p q=@tas r=nori] :: internal edit
1117− [%ingo p=@p q=@tas r=nori] :: internal noun edit
1118− [%init p=@p] :: report install
1119− [%into p=@p q=@tas r=nori] :: external edit
1120− [%invo p=@p q=@tas r=nori] :: external noun edit
1121− [%merg p=@p q=@tas r=mizu] :: internal change
1122− [%wart p=sock q=@tas r=path s=*] :: network request
1123− [%warp p=sock q=riff] :: file request
1127−There are only a small number of possible kisses, so it behooves us to
1128−describe each in detail.
1131− $% [%info p=@p q=@tas r=nori] :: internal edit
1133− [%into p=@p q=@tas r=nori] :: external edit
1136−These two kisses are nearly identical. At a high level, they apply
1137−changes to the filesystem. Whenever we add, remove, or edit a file, one
1138−of these cards is sent. The `p` is the ship whose filesystem we're
1139−trying to change, the `q` is the desk we're changing, and the `r` is the
1140−request change. For the format of the requested change, see the
1141−documentation for `++nori:clay` above.
1143−When a file is changed in the unix filesystem, vere will send a `%into`
1144−kiss. This tells clay that the duct over which the kiss was sent is the
1145−duct that unix is listening on for changes. From within Arvo, though, we
1146−should never send a `%into` kiss. The `%info` kiss is exactly identical
1147−except it does not reset the duct.
1150− [%ingo p=@p q=@tas r=nori] :: internal noun edit
1152− [%invo p=@p q=@tas r=nori] :: external noun edit
1155−These kisses are currently identical to `%info` and `%into`, though this
1156−will not always be the case. The intent is for these kisses to allow
1157−typed changes to clay so that we may store typed data. This is currently
1161− [%init p=@p] :: report install
1164−Init is called when a ship is started on our pier. This simply creates a
1165−default `room` to go into our `raft`. Essentially, this initializes the
1166−filesystem for a ship.
1169− [%merg p=@p q=@tas r=mizu] :: internal change
1172−This is called to perform a merge. This is most visibly called by
1173−:update to update the filesystem of the current ship to that of its
1174−sein. The `p` and `q` are as in `%info`, and the `r` is the description
1175−of the merge. See `++mizu:clay` above.
1178−`XX [%wake ~] :: timer activate XX`
1180−XX This card is sent by unix at the time specified by `++doze`. This
1181−time is XX usually the closest time specified in a subscription request.
1182−When `%wake` is XX called, we update our subscribers if there have been
1186− [%wart p=sock q=@tas r=path s=*] :: network request
1189−This is a request that has come across the network for a particular
1190−file. When another ship asks for a file from us, that request comes to
1191−us in the form of a `%wart` kiss. This is handled by trivially turning
1195− [%warp p=sock q=riff] :: file request
1198−This is a request for information about a particular desk. This is, in
1199−its most general form, a subscription, though in many cases it is the
1200−trivial case of a subscription -- a read. See `++riff:clay` for the format of
1203−## Lifecycle of a Local Read
1205−There are two real types of interaction with a filesystem: you can read,
1206−and you can write. We'll describe each process, detailing both the flow
1207−of control followed by the kernel and the algorithms involved. The
1208−simpler case is that of the read, so we'll begin with that.
1210−When a vane or an application wishes to read a file from the filesystem,
1211−it sends a `%warp` kiss, as described above. Of course, you may request
1212−a file on another ship and, being a global filesystem, clay will happily
1213−produce it for you. That code pathway will be described in another
1214−section; here, we will restrict ourselves to examining the case of a
1215−read from a ship on our own pier.
1217−The kiss can request either a single version of a file node or a range
1218−of versions of a desk. Here, we'll deal only with a request for a single
1221−As in all vanes, a kiss enters clay via a call to `++call`. Scanning
1222−through the arm, we quickly see where `%warp` is handled.
1225− ?: =(p.p.q.hic q.p.q.hic)
1226− =+ une=(un p.p.q.hic now ruf)
1227− =+ wex=(di:une p.q.q.hic)
1231− (eave:wex hen u.q.q.q.hic)
1234− [-.woo abet:(pish:une p.q.q.hic +.woo ran.wao)]
1237−We're following the familar patern of producing a list of moves and an
1238−updated state. In this case, the state is `++raft`.
1240−We first check to see if the sending and receiving ships are the same.
1241−If they're not, then this is a request for data on another ship. We
1242−describe that process later. Here, we discuss only the case of a local
1245−At a high level, the call to `++un` sets up the core for the domestic
1246−ship that contains the files we're looking for. The call to `++di` sets
1247−up the core for the particular desk we're referring to.
1249−After this, we perform the actual request. If there is no rave in the
1250−riff, then that means we are cancelling a request, so we call
1251−`++ease:de`. Otherwise, we start a subscription with `++eave:de`. We
1252−call `++abet:de` to resolve our various types of output into actual
1253−moves. We produce the moves we found above and the `++un` core resolved
1254−with `++pish:un` (putting the modified desk in the room) and `++abet:un`
1255−(putting the modified room in the raft).
1257−Much of this is fairly straightforward, so we'll only describe `++ease`,
1258−`++eave`, and `++abet:de`. Feel free to look up the code to the other
1259−steps -- it should be easy to follow.
1261−Although it's called last, it's usually worth examining `++abet` first,
1262−since it defines in what ways we can cause side effects. Let's do that,
1263−and also a few of the lines at the beginning of `++de`.
1266− =| yel=(list ,[p=duct q=gift])
1267− =| byn=(list ,[p=duct q=riot])
1268− =| vag=(list ,[p=duct q=gift])
1269− =| say=(list ,[p=duct q=path r=ship s=[p=@ud q=riff]])
1270− =| tag=(list ,[p=duct q=path c=note])
1273− ^- [(list move) rede]
1277− |=([a=duct b=gift] [hun %give b])
1280− |=([a=duct b=riot] [a %give [%writ b]])
1283− |=([a=duct b=gift] [a %give b])
1286− |= [a=duct b=path c=ship d=[p=@ud q=riff]]
1288− [%pass b %a %want [who c] [%q %re p.q.d (scot %ud p.d) ~] q.d]
1291− |=([a=duct b=path c=note] [a %pass b c])
1295−This is very simple code. We see there are exactly five different kinds
1296−of side effects we can generate.
1298−In `yel` we put gifts that we wish to be sent along the `hun:room` duct
1299−to dill. See the documentation for `++room` above. This is how we
1300−display messages to the terminal.
1302−In `byn` we put riots that we wish returned to subscribers. Recall that
1303−a riot is a response to a subscription. These are returned to our
1304−subscribers in the form of a `%writ` gift.
1306−In `vag` we put gifts along with the ducts on which to send them. This
1307−allows us to produce arbitrary gifts, but in practice this is only used
1308−to produce `%ergo` gifts.
1310−In `say` we put messages we wish to pass to ames. These messages are
1311−used to request information from clay on other piers. We must provide
1312−not only the duct and the request (the riff), but also the return path,
1313−the other ship to talk to, and the sequence number of the request.
1315−In `tag` we put arbitrary notes we wish to pass to other vanes. For now,
1316−the only notes we pass here are `%wait` and `%rest` to the timer vane.
1318−Now that we know what kinds of side effects we may have, we can jump
1319−into the handling of requests.
1322− ++ ease :: release request
1326− =+ rov=(~(got by qyx) hen)
1327− =. qyx (~(del by qyx) hen)
1328− (mabe rov (cury best hen))
1329− =. qyx (~(del by qyx) hen)
1331− =+ nux=(~(get by fod.u.ref) hen)
1334− say [[hen [(scot %ud u.nux) ~] for [u.nux syd ~]] say]
1335− fod.u.ref (~(del by fod.u.ref) hen)
1336− bom.u.ref (~(del by bom.u.ref) u.nux)
1340−This is called when we're cancelling a subscription. For domestic desks,
1341−`ref` is null, so we're going to cancel any timer we might have created.
1342−We first delete the duct from our map of requests, and then we call
1343−`++mabe` with `++best` to send a `%rest` kiss to the timer vane if we
1344−have started a timer. We'll describe `++best` and `++mabe` momentarily.
1346−Although we said we're not going to talk about foreign requests yet,
1347−it's easy to see that for foreign desks, we cancel any outstanding
1348−requests for this duct and send a message over ames to the other ship
1349−telling them to cancel the subscription.
1353− |= [hen=duct tym=@da]
1354− %_(+> tag :_(tag [hen /tyme %t %rest tym]))
1357−This simply pushes a `%rest` note onto `tag`, from where it will be
1358−passed back to arvo to be handled. This cancels the timer at the given
1359−duct (with the given time).
1362− ++ mabe :: maybe fire function
1363− |* [rov=rove fun=$+(@da _+>.^$)]
1370− ?. ?=(%da -.q.p.rov) ~
1375− ?. ?=(%da -.p.mot) ~
1376− ?.((lth now p.p.mot) ~ [~ p.p.mot])
1377− ?. ?=(%da -.q.mot) ~
1378− ?.((lth now p.q.mot) [~ now] [~ p.q.mot])
1382−This decides whether the given request can only be satsified in the
1383−future. In that case, we call the given function with the time in the
1384−future when we expect to have an update to give to this request. This is
1385−called with `++best` to cancel timers and with `++bait` to start them.
1387−For single requests, we have a time if the request is for a particular
1388−time (which is assumed to be in the future). For ranges of requests, we
1389−check both the start and end cases to see if they are time cases. If so,
1390−we choose the earlier time.
1392−If any of those give us a time, then we call the given funciton with the
1395−The more interesting case is, of course, when we're not cancelling a
1396−subscription but starting one.
1399− ++ eave :: subscribe
1400− |= [hen=duct rav=rave]
1404− ?: &(=(p.p.rav %u) !=(p.q.p.rav now))
1405− ~& [%clay-fail p.q.p.rav %now now]
1407− =+ ver=(aver p.rav)
1412− (blab hen p.rav u.u.ver)
1415−There are two types of subscriptions -- either we're requesting a single
1416−file or we're requesting a range of versions of a desk. We'll dicuss the
1419−First, we check that we're not requesting the `rang` from any time other
1420−than the present. Since we don't store that information for any other
1421−time, we can't produce it in a referentially transparent manner for any
1422−time other than the present.
1424−Then, we try to read the requested `mood:clay` `p.rav`. If we can't access
1425−the request data right now, we call `++duce` to put the request in our
1426−queue to be satisfied when the information becomes available.
1428−This case occurs when we make a request for a case whose (1) date is
1429−after the current date, (2) number is after the current number, or (3)
1430−label is not yet used.
1433− ++ duce :: produce request
1434− |= [hen=duct rov=rove]
1436− =. qyx (~(put by qyx) hen rov)
1438− (mabe rov (cury bait hen))
1439− |- ^+ +>+.$ :: XX why?
1442− ?. ?=([%& %v *] rav) rav
1443− [%| [%ud let.dom] `case`q.p.rav r.p.rav]
1446− say [[hen [(scot %ud inx) ~] for [inx syd ~ vaw]] say]
1447− nix.u.ref +(nix.u.ref)
1448− bom.u.ref (~(put by bom.u.ref) inx [hen vaw])
1449− fod.u.ref (~(put by fod.u.ref) hen inx)
1453−The code for `++duce` is nearly the exact inverse of `++ease`, which in
1454−the case of a domestic desk is very simple -- we simply put the duct and
1455−rave into `qyx` and possibly start a timer with `++mabe` and `++bait`.
1456−Recall that `ref` is null for domestic desks and that `++mabe` fires the
1457−given function with the time we need to be woken up at, if we need to be
1458−woken up at a particular time.
1462− |= [hen=duct tym=@da]
1463− %_(+> tag :_(tag [hen /tyme %t %wait tym]))
1466−This sets an alarm by sending a `%wait` card with the given time to the
1469−Back in `++eave`, if `++aver` returned `[~ ~]`, then we cancel the
1470−subscription. This occurs when we make (1) a `%x` request for a file
1471−that does not exist, (2) a `%w` request with a case that is not a
1472−number, or (3) a `%w` request with a nonempty path. The `++blub` is
1473−exactly what you would expect it to be.
1476− ++ blub :: ship stop
1478− %_(+> byn [[hen ~] byn])
1481−We notify the duct that we're cancelling their subscription since it
1484−Otherwise, we have received the desired information, so we send it on to
1485−the subscriber with `++blab`.
1488− ++ blab :: ship result
1489− |= [hen=duct mun=mood dat=*]
1491− +>(byn [[hen ~ [p.mun q.mun syd] r.mun dat] byn])
1494−The most interesting arm called in `++eave` is, of course, `++aver`,
1495−where we actually try to read the data.
1500− ^- (unit (unit ,*))
1501− ?: &(=(p.mun %u) !=(p.q.mun now)) :: prevent bad things
1502− ~& [%clay-fail p.q.mun %now now]
1504− =+ ezy=?~(ref ~ (~(get by haw.u.ref) mun))
1506− =+ nao=(~(case-to-aeon ze lim dom ran) q.mun)
1507− ?~(nao ~ [~ (~(read-at-aeon ze lim dom ran) u.nao mun)])
1510−We check immediately that we're not requesting the `rang` for any time
1511−other than the present.
1513−If this is a foreign desk, then we check our cache for the specific
1514−request. If either this is a domestic desk or we don't have the request
1515−in our cache, then we have to actually go read the data from our dome.
1517−We need to do two things. First, we try to find the number of the commit
1518−specified by the given case, and then we try to get the data there.
1520−Here, we jump into `arvo/zuse.hoon`, which is where much of the
1521−algorithmic code is stored, as opposed to the clay interface, which is
1522−stored in `arvo/clay.hoon`. We examine `++case-to-aeon:ze`.
1525− ++ case-to-aeon :: case-to-aeon:ze
1526− |= lok=case :: act count through
1530− ?: (gth p.lok lim) ~
1532− ?: =(0 let) [~ 0] :: avoid underflow
1541− %tas (~(get by lab) p.lok)
1542− %ud ?:((gth p.lok let) ~ [~ p.lok])
1546−We handle each type of `case:clay` differently. The latter two types are
1549−If we're requesting a revision by label, then we simply look up the
1550−requested label in `lab` from the given dome. If it exists, that is our
1551−aeon; else we produce null, indicating the requested revision does not
1554−If we're requesting a revision by number, we check if we've yet reached
1555−that number. If so, we produce the number; else we produce null.
1557−If we're requesting a revision by date, we check first if the date is in
1558−the future, returning null if so. Else we start from the most recent
1559−revision and scan backwards until we find the first revision committed
1560−before that date, and we produce that. If we requested a date before any
1561−revisions were committed, we produce `0`.
1563−The definitions of `++aeon-to-tako` and `++tako-to-yaki` are trivial.
1566− ++ aeon-to-tako ~(got by hit)
1568− ++ tako-to-yaki ~(got by hut) :: grab yaki
1571−We simply look up the aeon or tako in their respective maps (`hit` and
1574−Assuming we got a valid version number, `++aver` calls
1575−`++read-at-aeon:ze`, which reads the requested data at the given
1579− ++ read-at-aeon :: read-at-aeon:ze
1580− |= [oan=aeon mun=mood] :: seek and read
1582− ?: &(?=(%w p.mun) !?=(%ud -.q.mun)) :: NB only for speed
1583− ?^(r.mun ~ [~ oan])
1584− (read:(rewind oan) mun)
1587−If we're requesting the revision number with a case other than by
1588−number, then we go ahead and just produce the number we were given.
1589−Otherwise, we call `++rewind` to rewind our state to the given revision,
1590−and then we call `++read` to get the requested information.
1593− ++ rewind :: rewind:ze
1594− |= oan=aeon :: rewind to aeon
1597− ?: (gth oan let) !! :: don't have version
1598− +>(ank (checkout-ankh q:(tako-to-yaki (aeon-to-tako oan))), let oan)
1601−If we're already at the requested version, we do nothing. If we're
1602−requesting a version later than our head, we are unable to comply.
1604−Otherwise, we get the hash of the commit at the number, and from that we
1605−get the commit itself (the yaki), which has the map of path to lobe that
1606−represents a version of the filesystem. We call `++checkout-ankh` to
1607−checkout the commit, and we replace `ank` in our context with the
1611− ++ checkout-ankh :: checkout-ankh:ze
1612− |= hat=(map path lobe) :: checkout commit
1615− %+ roll (~(tap by hat) ~)
1616− |= [[pat=path bar=lobe] ank=ankh]
1620− =+ zar=(lobe-to-noun bar)
1621− ank(q [~ (sham zar) zar])
1622− =+ nak=(~(get by r.ank) i.pat)
1624− r %+ ~(put by r.ank) i.pat
1625− $(pat t.pat, ank (fall nak _ankh))
1629−Twice we call `++cosh`, which hashes a commit, updating `p` in an
1630−`ankh`. Let's jump into that algorithm before we describe
1634− ++ cosh :: locally rehash
1635− |= ank=ankh :: NB v/unix.c
1636− ank(p rehash:(zu ank))
1639−We simply replace `p` in the hash with the `cash` we get from a call to
1643− ++ zu !: :: filesystem
1644− |= ank=ankh :: filesystem state
1645− =| myz=(list ,[p=path q=miso]) :: changes in reverse
1646− =| ram=path :: reverse path into
1648− ++ rehash :: local rehash
1650− %+ mix ?~(q.ank 0 p.u.q.ank)
1655− (shaf %dash (mix axe (shaf %dush (mix p.n.r.ank p.q.n.r.ank))))
1656− $(r.ank l.r.ank, axe (peg axe 2))
1657− $(r.ank r.r.ank, axe (peg axe 3))
1661−`++zu` is a core we set up with a particular filesystem node to traverse
1662−a checkout of the filesystem and access the actual data inside it. One
1663−of the things we can do with it is to create a recursive hash of the
1666−In `++rehash`, if this node is a file, then we xor the remainder of the
1667−hash with the hash of the contents of the file. The remainder of the
1668−hash is `0` if we have no children, else we descend into our children.
1669−Basically, we do a half SHA-256 of the xor of the axis of this child and
1670−the half SHA-256 of the xor of the name of the child and the hash of the
1671−child. This is done for each child and all the results are xored
1674−Now we return to our discussion of `++checkout-ankh`.
1676−We fold over every path in this version of the filesystem and create a
1677−great ankh out of them. First, we call `++lobe-to-noun` to get the raw
1678−data referred to be each lobe.
1681− ++ lobe-to-noun :: grab blob
1687−This converts a lobe into the raw data it refers to by first getting the
1688−blob with `++lobe-to-blob` and converting that into data with
1692− ++ lobe-to-blob ~(got by lat) :: grab blob
1695−This just grabs the blob that the lobe refers to.
1698− ++ blob-to-noun :: grab blob
1701− %delta (lump r.p (lobe-to-noun q.p))
1707−If we have either a direct or an indirect blob, then the data is stored
1708−right in the blob. Otherwise, we have to reconstruct it from the diffs.
1709−We do this by calling `++lump` on the diff in the blob with the data
1710−obtained by recursively calling the parent of this blob.
1713− ++ lump :: apply patch
1714− |= [don=udon src=*]
1716− ?+ p.don ~|(%unsupported !!)
1718− ?+ -.q.don ~|(%unsupported !!)
1720− %c (lurk ((hard (list)) src) p.q.don)
1721− %d (lure src p.q.don)
1725− =+ dst=(lore ((hard ,@) src))
1727− ?+ -.q.don ~|(%unsupported !!)
1728− %a ((hard (list ,@t)) q.q.don)
1729− %c (lurk dst p.q.don)
1734−This is defined in `arvo/hoon.hoon` for historical reasons which are
1735−likely no longer applicable. Since the `++umph` structure will likely
1736−change we convert clay to be a typed filesystem, we'll only give a
1737−high-level description of this process. If we have a `%a` udon, then
1738−we're performing a trivial replace, so we produce simply `q.q.don`. If
1739−we have a `%c` udon, then we're performing a list merge (as in, for
1740−example, lines of text). The merge is performed by `++lurk`.
1743− ++ lurk :: apply list patch
1744− |* [hel=(list) rug=(urge)]
1753− hel (slag p.i.rug hel)
1754− war (weld (flop (scag p.i.rug hel)) war)
1760− hel =+ gur=(flop p.i.rug)
1763− ?>(&(?=(^ hel) =(i.gur i.hel)) $(hel t.hel, gur t.gur))
1764− war (weld q.i.rug war)
1769−We accumulate our final result in `war`. If there's nothing more in our
1770−list of merge instructions (unces), we just reverse `war` and produce
1771−it. Otherwise, we process another unce. If the unce is of type `&`, then
1772−we have `p.i.rug` lines of no changes, so we just copy them over from
1773−`hel` to `war`. If the unice is of type `|`, then we verify that the
1774−source lines (in `hel`) are what we expect them to be (`p.i.rug`),
1775−crashing on failure. If they're good, then we append the new lines in
1776−`q.i.rug` onto `war`.
1778−And that's really it. List merges are pretty easy. Anyway, if you
1779−recall, we were discussing `++checkout-ankh`.
1782− ++ checkout-ankh :: checkout-ankh:ze
1783− |= hat=(map path lobe) :: checkout commit
1786− %+ roll (~(tap by hat) ~)
1787− |= [[pat=path bar=lobe] ank=ankh]
1791− =+ zar=(lobe-to-noun bar)
1792− ank(q [~ (sham zar) zar])
1793− =+ nak=(~(get by r.ank) i.pat)
1795− r %+ ~(put by r.ank) i.pat
1796− $(pat t.pat, ank (fall nak _ankh))
1800−If the path is null, then we calculate `zar`, the raw data at the path
1801−`pat` in this version. We produce the given ankh with the correct data.
1803−Otherwise, we try to get the child we're looking at from our parent
1804−ankh. If it's already been created, this succeeds; otherwise, we simply
1805−create a default blank ankh. We place ourselves in our parent after
1806−recursively computing our children.
1808−This algorithm really isn't that complicated, but it may not be
1809−immediately obvious why it works. An example should clear everything up.
1811−Suppose `hat` is a map of the following information.
1814− /greeting --> "customary upon meeting"
1815− /greeting/english --> "hello"
1816− /greeting/spanish --> "hola"
1817− /greeting/russian/short --> "привет"
1818− /greeting/russian/long --> "Здравствуйте"
1819− /farewell/russian --> "до свидания"
1822−Furthermore, let's say that we process them in this order:
1826− /greeting/russian/short
1827− /greeting/russian/long
1833−Then, the first path we process is `/greeting/english` . Since our path
1834−is not null, we try to get `nak`, but because our ankh is blank at this
1835−point it doesn't find anything. Thus, update our blank top-level ankh
1836−with a child `%greeting`. and recurse with the blank `nak` to create the
1837−ankh of the new child.
1839−In the recursion, we our path is `/english` and our ankh is again blank.
1840−We try to get the `english` child of our ankh, but this of course fails.
1841−Thus, we update our blank `/greeting` ankh with a child `english`
1842−produced by recursing.
1844−Now our path is null, so we call `++lobe-to-noun` to get the actual
1845−data, and we place it in the brand-new ankh.
1847−Next, we process `/greeting/russian/short`. Since our path is not null,
1848−we try to get the child named `%greeting`, which does exist since we
1849−created it earlier. We put modify this child by recursing on it. Our
1850−path is now `/russian/short`, so we look for a `%russian` child in our
1851−`/greeting` ankh. This doesn't exist, so we add it by recursing. Our
1852−path is now `/short`, so we look for a `%short` child in our
1853−`/greeting/russian` ankh. This doesn't exist, so we add it by recursing.
1854−Now our path is null, so we set the contents of this node to `"привет"`,
1855−and we're done processing this path.
1857−Next, we process `/greeting/russian/long`. This proceeds similarly to
1858−the previous except that now the ankh for `/greeting/russian` already
1859−exists, so we simply reuse it rather than creating a new one. Of course,
1860−we still must create a new `/greeting/russian/long` ankh.
1862−Next, we process `/greeting`. This ankh already exists, so after we've
1863−recursed once, our path is null, and our ankh is not blank -- it already
1864−has two children (and two grandchildren). We don't touch those, though,
1865−since a node may be both a file and a directory. We just add the
1866−contents of the file -- "customary upon meeting" -- to the existing
1869−Next, we process `/greeting/spanish`. Of course, the `/greeting` ankh
1870−already exists, but it doesn't have a `%spanish` child, so we create
1871−that, taking care not to disturb the contents of the `/greeting` file.
1872−We put "hola" into the ankh and call it good.
1874−Finally, we process `/farewell/russian`. Here, the `/farewell` ankh
1875−doesn't exist, so we create it. Clearly the newly-created ankh doesn't
1876−have any children, so we have to add a `%russian` child, and in this
1877−child we put our last content -- "до свидания".
1879−We hope it's fairly obvious that the order we process the paths doesn't
1880−affect the final ankh tree. The tree will be constructed in a very
1881−different order depending on what order the paths come in, but the
1882−resulting tree is independent of order.
1884−At any rate, we were talking about something important, weren't we? If
1885−you recall, that concludes our discussion of `++rewind`, which was
1886−called from `++read-at-aeon`. In summary, `++rewind` returns a context
1887−in which our current state is (very nearly) as it was when the specified
1888−version of the desk was the head. This allows `++read-at-aeon` to call
1889−`++read` to read the requested information.
1893− |= mun=mood :: read at point
1896− [~ `dome`+<+<.read]
1897− ?: &(?=(%w p.mun) !?=(%ud -.q.mun))
1898− ?^(r.mun ~ [~ let])
1904− ?^(r.mun ~ [~ [t.yak (forge-nori yak)]])
1905− ::?> ?=(^ hit) ?^(r.mun ~ [~ i.hit]) :: what do?? need [@da nori]
1906− (query(ank ank:(descend-path:(zu ank) r.mun)) p.mun)
1909−If we're requesting the dome, then we just return that immediately.
1911−If we're requesting the revision number of the desk and we're not
1912−requesting it by number, then we just return the current number of this
1913−desk. Note of course that this was really already handled in
1916−If we're requesting a `%w` with a specific revision number, then we do
1917−something or other with the commit there. It's kind of weird, and it
1918−doesn't seem to work, so we'll ignore this case.
1920−Otherwise, we descend into the ankh tree to the given path with
1921−`++descend-path:zu`, and then we handle specific request in `++query`.
1924− ++ descend-path :: descend recursively
1927− ?~(way +> $(way t.way, +> (descend i.way)))
1930−This is simple recursion down into the ankh tree. `++descend` descends
1931−one level, so this will eventually get us down to the path we want.
1934− ++ descend :: descend
1937− =+ you=(~(get by r.ank) lol)
1938− +>.$(ram [lol ram], ank ?~(you [*cash ~ ~] u.you))
1941−`ram` is the path that we're at, so to descend one level we push the
1942−name of this level onto that path. We update the ankh with the correct
1943−one at that path if it exists; else we create a blank one.
1945−Once we've decscended to the correct level, we need to actually deal
1949− ++ query :: query:ze
1950− |= ren=?(%u %v %x %y %z) :: endpoint query
1953− %u [~ `rang`+<+>.query]
1954− %v [~ `dome`+<+<.query]
1955− %x ?~(q.ank ~ [~ q.u.q.ank])
1961−Now that everything's set up, it's really easy. If they're requesting
1962−the rang, dome, or ankh, we give it to them. If the contents of a file,
1963−we give it to them if it is in fact a file. If the `arch`, then we
1964−calculate it with `++as-arch`.
1967− ++ as-arch :: as-arch:ze
1968− ^- arch :: arch report
1970− ?~(q.ank ~ [~ p.u.q.ank])
1971− |- ^- (map ,@ta ,~)
1973− [[p.n.r.ank ~] $(r.ank l.r.ank) $(r.ank r.r.ank)]
1976−This very simply strips out all the "real" data and returns just our own
1977−hash, the hash of the file contents (if we're a file), and a map of the
1978−names of our immediate children.
1980−## Lifecycle of a Local Subscription
1982−A subscription to a range of revisions of a desk initially follows the
1983−same path that a single read does. In `++aver`, we checked the head of
1984−the given rave. If the head was `&`, then it was a single request, so we
1985−handled it above. If `|`, then we handle it with the following code.
1988− =+ nab=(~(case-to-aeon ze lim dom ran) p.p.rav)
1990− ?> =(~ (~(case-to-aeon ze lim dom ran) q.p.rav))
1991− (duce hen (rive rav))
1992− =+ huy=(~(case-to-aeon ze lim dom ran) q.p.rav)
1993− ?: &(?=(^ huy) |((lth u.huy u.nab) &(=(0 u.huy) =(0 u.nab))))
1995− =+ top=?~(huy let.dom u.huy)
1996− =+ sar=(~(lobes-at-path ze lim dom ran) u.nab r.p.rav)
1997− =+ ear=(~(lobes-at-path ze lim dom ran) top r.p.rav)
2000− =+ fud=(~(make-nako ze lim dom ran) u.nab top)
2001− (bleb hen u.nab fud)
2006− (duce hen `rove`[%| ptr q.p.rav r.p.rav ear])
2010−Recall that `++case-to-aeon:ze` produces the revision number that a case
2011−corresponds to, if it corresponds to any. If it doesn't yet correspond
2012−to a revision, then it produces null.
2014−Thus, we first check to see if we've even gotten to the beginning of the
2015−range of revisions requested. If not, then we assert that we haven't yet
2016−gotten to the end of the range either, because that would be really
2017−strange. If not, then we immediately call `++duce`, which, if you
2018−recall, for a local request, simply puts this duct and rove into our
2019−cult `qyx`, so that we know who to respond to when the revision does
2022−If we've already gotten to the first revision, then we can produce some
2023−content immediately. If we've also gotten to the final revision, and
2024−that revision is earlier than the start revision, then it's a bad
2025−request and we call `++blub`, which tells the subscriber that his
2026−subscription will not be satisfied.
2028−Otherwise, we find the data at the given path at the beginning of the
2029−subscription and at the last available revision in the subscription. If
2030−they're the same, then we don't send a notification. Otherwise, we call
2031−`++gack`, which creates the `++nako` we need to produce. We call
2032−`++bleb` to actually produce the information.
2034−If we already have the last requested revision, then we also tell the
2035−subscriber with `++blub` that the subscription will receive no further
2038−If there will be more revisions in the subscription, then we call
2039−`++duce`, adding the duct to our subscribers. We modify the rove to
2040−start at the next revision since we've already handled all the revisions
2043−We glossed over the calls to `++lobes-at-path`, `++make-nako`, and
2044−`++bleb`, so we'll get back to those right now. `++bleb` is simple, so
2045−we'll start with that.
2048− ++ bleb :: ship sequence
2049− |= [hen=duct ins=@ud hip=nako]
2051− (blab hen [%w [%ud ins] ~] hip)
2054−We're given a duct, the beginning revision number, and the nako that
2055−contains the updates since that revision. We use `++blab` to produce
2056−this result to the subscriber. The case is `%w` with a revision number
2057−of the beginning of the subscription, and the data is the nako itself.
2059−We call `++lobes-at-path:ze` to get the data at the particular path.
2062− ++ lobes-at-path :: lobes-at-path:ze
2063− |= [oan=aeon pax=path] :: data at path
2078− $(p +.p, pax +.pax)
2082−At revision zero, the theoretical common revision between all
2083−repositories, there is no data, so we produce null.
2085−We get the list of paths (paired with their lobe) in the revision
2086−referred to by the given number and we keep only those paths which begin
2087−with `pax`. Converting to a map, we now have a map from the subpaths at
2088−the given path to the hash of their data. This is simple and efficient
2089−to calculate and compare to later revisions. This allows us to easily
2090−tell if a node or its children have changed.
2092−Finally, we will describe `++make-nako:ze`.
2095− ++ make-nako :: gack a through b
2097− ^- [(map aeon tako) aeon (set yaki) (set blob)]
2099− =- [(takos-to-yakis -<) (lobes-to-blobs ->)]
2100− %+ reachable-between-takos
2101− (~(get by hit) a) :: if a not found, a=0
2104− %- mo %+ skim (~(tap by hit) ~)
2106− &((gth p a) (lte p b))
2109−We need to produce four things -- the numbers of the new commits, the
2110−number of the latest commit, the new commits themselves, and the new
2113−The first is fairly easy to produce. We simply go over our map of
2114−numbered commits and produce all those numbered greater than `a` and not
2117−The second is even easier to produce -- `b` is clearly our most recent
2120−The third and fourth are slightly more interesting, though not too
2121−terribly difficult. First, we call `++reachable-between-takos`.
2124− ++ reachable-between-takos
2125− |= [a=(unit tako) b=tako] :: pack a through b
2126− ^- [(set tako) (set lobe)]
2129− (reachable-takos r:(tako-to-yaki u.a))
2130− =+ yak=`yaki`(tako-to-yaki b)
2131− %+ new-lobes-takos (new-lobes ~ sar) :: get lobes
2132− |- ^- (set tako) :: walk onto sar
2133− ?: (~(has in sar) r.yak)
2135− =+ ber=`(set tako)`(~(put in `(set tako)`~) `tako`r.yak)
2139− |= [yek=tako bar=(set tako)]
2141− ?: (~(has in bar) yek) :: save some time
2144− ^$(yak (tako-to-yaki yek))
2147−We take a possible starting commit and a definite ending commit, and we
2148−produce the set of commits and the set of data between them.
2150−We let `sar` be the set of commits reachable from `a`. If `a` is null,
2151−then obviously no commits are reachable. Otherwise, we call
2152−`++reachable-takos` to calculate this.
2155− ++ reachable-takos :: reachable
2156− |= p=tako :: XX slow
2158− =+ y=(tako-to-yaki p)
2159− =+ t=(~(put in _(set tako)) p)
2162− ?: (~(has in s) q) :: already done
2164− (~(uni in s) ^$(p q)) :: otherwise traverse
2167−We very simply produce the set of the given tako plus its parents,
2170−Back in `++reachable-between-takos`, we let `yak` be the yaki of `b`,
2171−the ending commit. With this, we create a set that is the union of `sar`
2172−and all takos reachable from `b`.
2174−We pass `sar` into `++new-lobes` to get all the lobes referenced by any
2175−tako referenced by `a`. The result is passed into `++new-lobes-takos` to
2176−do the same, but not recomputing those in already calculated last
2177−sentence. This produces the sets of takos and lobes we need.
2180− ++ new-lobes :: object hash set
2181− |= [b=(set lobe) a=(set tako)] :: that aren't in b
2183− %+ roll (~(tap in a) ~)
2184− |= [tak=tako bar=(set lobe)]
2186− =+ yak=(tako-to-yaki tak)
2187− %+ roll (~(tap by q.yak) ~)
2188− |= [[path lob=lobe] far=_bar]
2190− ?~ (~(has in b) lob) :: don't need
2192− =+ gar=(lobe-to-blob lob)
2194− %direct (~(put in far) lob)
2195− %delta (~(put in $(lob q.gar)) lob)
2196− %indirect (~(put in $(lob s.gar)) lob)
2200−Here, we're creating a set of lobes referenced in a commit in `a`. We
2201−start out with `b` as the initial set of lobes, so we don't need to
2202−recompute any of the lobes referenced in there.
2204−The algorithm is pretty simple, so we won't bore you with the details.
2205−We simply traverse every commit in `a`, looking at every blob referenced
2206−there, and, if it's not already in `b`, we add it to `b`. In the case of
2207−a direct blob, we're done. For a delta or an indirect blob, we
2208−recursively add every blob referenced within the blob.
2211− ++ new-lobes-takos :: garg & repack
2212− |= [b=(set lobe) a=(set tako)]
2213− ^- [(set tako) (set lobe)]
2214− [a (new-lobes b a)]
2217−Here, we just update the set of lobes we're given with the commits we're
2218−given and produce both sets.
2220−This concludes our discussion of a local subscription.
2222−## Lifecycle of a Foreign Read or Subscription
2224−Foreign reads and subscriptions are handled in much the same way as
2225−local ones. The interface is the same -- a vane or app sends a `%warp`
2226−kiss with the request. The difference is simply that the `sock` refers
2227−to the foreign ship.
2229−Thus, we start in the same place -- in `++call`, handling `%warp`.
2230−However, since the two side of the `sock` are different, we follow a
2234− =+ wex=(do now p.q.hic p.q.q.hic ruf)
2237− abet:(ease:wex hen)
2238− abet:(eave:wex hen u.q.q.q.hic)
2239− [-.woo (posh q.p.q.hic p.q.q.hic +.woo ruf)]
2242−If we compare this to how the local case was handled, we see that it's
2243−not all that different. We use `++do` rather than `++un` and `++de` to
2244−set up the core for the foreign ship. This gives us a `++de` core, so we
2245−either cancel or begin the request by calling `++ease` or `++eave`,
2246−exactly as in the local case. In either case, we call `++abet:de` to
2247−resolve our various types of output into actual moves, as described in
2248−the local case. Finally, we call `++posh` to update our raft, putting
2249−the modified rung into the raft.
2251−We'll first trace through `++do`.
2255− |= [now=@da [who=ship him=ship] syd=@tas ruf=raft]
2257− =+ rug=(~(get by hoy.ruf) him)
2258− ?^(rug u.rug *rung)
2260− =+ yit=(~(get by rus.rug) syd)
2261− ?^(yit u.yit `rede`[~2000.1.1 ~ [~ *rind] *dome])
2262− ((de now ~ ~) [who him] syd red ran.ruf)
2265−If we already have a rung for this foreign ship, then we use that.
2266−Otherwise, we create a new blank one. If we already have a rede in this
2267−rung, then we use that, otherwise we create a blank one. An important
2268−point to note here is that we let `ref` in the rede be `[~ *rind]`.
2269−Recall, for domestic desks `ref` is null. We use this to distinguish
2270−between foreign and domestic desks in `++de`.
2272−With this information, we create a `++de` core as usual.
2274−Although we've already covered `++ease` and `++eave`, we'll go through
2275−them quickly again, highlighting the case of foreign request.
2278− ++ ease :: release request
2282− =+ rov=(~(got by qyx) hen)
2283− =. qyx (~(del by qyx) hen)
2284− (mabe rov (cury best hen))
2285− =. qyx (~(del by qyx) hen)
2287− =+ nux=(~(get by fod.u.ref) hen)
2290− say [[hen [(scot %ud u.nux) ~] for [u.nux syd ~]] say]
2291− fod.u.ref (~(del by fod.u.ref) hen)
2292− bom.u.ref (~(del by bom.u.ref) u.nux)
2296−Here, we still remove the duct from our cult (we maintain a cult even
2297−for foreign desks), but we also need to tell the foreign desk to cancel
2298−our subscription. We do this by sending a request (by appending to
2299−`say`, which gets resolved in `++abet:de` to a `%want` kiss to ames) to
2300−the foreign ship to cancel the subscription. Since we don't anymore
2301−expect a response on this duct, we remove it from `fod` and `bom`, which
2302−are the maps between ducts, raves, and request sequence numbers.
2303−Basically, we remove every trace of the subscription from our request
2306−In the case of `++eave`, where we're creating a new request, everything
2307−is exactly identical to the case of the local request except `++duce`.
2308−We said that `++duce` simply puts the request into our cult. This is
2309−true for a domestic request, but distinctly untrue for foreign requests.
2312− ++ duce :: produce request
2313− |= [hen=duct rov=rove]
2315− =. qyx (~(put by qyx) hen rov)
2317− |- ^+ +>+.$ :: XX why?
2320− ?. ?=([%& %v *] rav) rav
2321− [%| [%ud let.dom] `case`q.p.rav r.p.rav]
2324− say [[hen [(scot %ud inx) ~] for [inx syd ~ vaw]] say]
2325− nix.u.ref +(nix.u.ref)
2326− bom.u.ref (~(put by bom.u.ref) inx [hen vaw])
2327− fod.u.ref (~(put by fod.u.ref) hen inx)
2331−If we have a request manager (i.e. this is a foreign desk), then we do
2332−the approximate inverse of `++ease`. We create a rave out of the given
2333−request and send it off to the foreign desk by putting it in `say`. Note
2334−that the rave is created to request the information starting at the next
2335−revision number. Since this is a new request, we put it into `fod` and
2336−`bom` to associate the request with its duct and its sequence number.
2337−Since we're using another sequence number, we must increment `nix`,
2338−which represents the next available sequence number.
2340−And that's really it for this side of the request. Requesting foreign
2341−information isn't that hard. Let's see what it looks like on the other
2342−side. When we get a request from another ship for information on our
2343−ship, that comes to us in the form of a `%wart` from ames.
2345−We handle a `%wart` in `++call`, right next to where we handle the
2351− =+ ryf=((hard riff) s.q.hic)
2354− :^ %pass [(scot %p p.p.q.hic) (scot %p q.p.q.hic) r.q.hic]
2356− [%warp [p.p.q.hic p.p.q.hic] ryf]
2360−Every request we receive should be of type `riff`, so we coerce it into
2361−that type. We just convert this into a new `%warp` kiss that we pass to
2362−ourself. This gets handled like normal, as a local request. When the
2363−request produces a value, it does so like normal as a `%writ`, which is
2364−returned to `++take` along the path we just sent it on.
2369− =+ our=(need (slaw %p i.tea))
2370− =+ him=(need (slaw %p i.t.tea))
2373− [%pass ~ %a [%want [our him] [%r %re %c t.t.tea] p.+.q.hin]]
2377−Since we encoded the ship we need to respond to in the path, we can just
2378−pass our `%want` back to ames, so that we tell the requesting ship about
2381−This comes back to the original ship as a `%waft` from ames, which comes
2382−into `++take`, right next to where we handled `%writ`.
2387− =+ syd=(need (slaw %tas i.tea))
2388− =+ inx=(need (slaw %ud i.t.tea))
2391− (knit:(do now p.+.q.hin syd ruf) [inx ((hard riot) q.+.q.hin)])
2394− [-.zot (posh q.p.+.q.hin syd +.zot ruf)]
2395− [mos ..^$(ran.ruf ran.zat)] :: merge in new obj
2398−This gets the desk and sequence number from the path the request was
2399−sent over. This determines exactly which request is being responded to.
2400−We call `++knit:de` to apply the changes to our local desk, and we call
2401−`++wake` to update our subscribers. Then we call `++abet:de` and
2402−`++posh` as normal (like in `++eave`).
2404−We'll examine `++knit` and `++wake`, in that order.
2407− ++ knit :: external change
2408− |= [inx=@ud rot=riot]
2412− =+ ruv=(~(get by bom.u.ref) inx)
2414− => ?. |(?=(~ rot) ?=(& -.q.u.ruv)) .
2416− bom.u.ref (~(del by bom.u.ref) inx)
2417− fod.u.ref (~(del by fod.u.ref) p.u.ruv)
2420− =+ rav=`rave`q.u.ruv
2423− ?.(&(?=(| -.rav) ?=(%da -.q.p.rav)) lim `@da`p.q.p.rav)
2426− ?. ?=(& -.rav) haw.u.ref
2427− (~(put by haw.u.ref) p.rav ~)
2429− ?< ?=(%v p.p.u.rot)
2431− (~(put by haw.u.ref) [p.p.u.rot q.p.u.rot q.u.rot] ~ r.u.rot)
2432− ?. ?=(%w p.p.u.rot) +>+.$
2434− =+ nez=[%w [%ud let.dom] ~]
2435− =+ nex=(~(get by haw.u.ref) nez)
2437− ?~ u.nex +>+.^$ :: should never happen
2438− =. +>+.^$ =+ roo=(edis ((hard nako) u.u.nex))
2439− ?>(?=(^ ref.roo) roo)
2441− haw.u.ref (~(del by haw.u.ref) nez)
2445−This is kind of a long gate, but don't worry, it's not bad at all.
2447−First, we assert that we're not a domestic desk. That wouldn't make any
2450−Since we have the sequence number of the request, we can get the duct
2451−and rave from `bom` in our request manager. If we didn't actually
2452−request this data (or the request was canceled before we got it), then
2455−Else, we remove the request from `bom` and `fod` unless this was a
2456−subscription request and we didn't receive a null riot (which would
2457−indicate the last message on the subscription).
2459−Now, if we received a null riot, then if this was a subscription request
2460−by date, then we update `lim` in our request manager (representing the
2461−latest time at which we have complete information for this desk) to the
2462−date that was requested. If this was a single read request, then we put
2463−the result in our simple cache `haw` to make future requests faster.
2466−If we received actual data, then we put it into our cache `haw`. Unless
2467−it's a `%w` request, we're done.
2469−If it is a `%w` request, then we try to get the `%w` at our current head
2470−from the cache. In general, that should be the thing we just put in a
2471−moment ago, but that is not guaranteed. The most common case where this
2472−is different is when we receive desk updates out of order. At any rate,
2473−since we now have new information, we need to apply it to our local copy
2474−of the desk. We do so in `++edis`, and then we remove the stuff we just
2475−applied from the cache, since it's not really a true "single read", like
2476−what should really be in the cache.
2479− ++ edis :: apply subscription
2483− hit.dom (~(uni by hit.dom) gar.nak)
2485− lat.ran %+ roll (~(tap in bar.nak) ~)
2487− |= [sar=blob yeb=(map lobe blob)]
2488− =+ zax=(blob-to-lobe sar)
2489− %+ ~(put by yeb) zax sar
2490− hut.ran %+ roll (~(tap in lar.nak) ~)
2492− |= [sar=yaki yeb=(map tako yaki)]
2493− %+ ~(put by yeb) r.sar sar
2497−This shows, of course, exactly why nako is defined the way it is. To
2498−become completely up to date with the foreign desk, we need to merge
2499−`hit` with the foreign one so that we have all the revision numbers. We
2500−update `let` so that we know which revision is the head.
2502−We merge the new blobs in `lat`, keying them by their hash, which we get
2503−from a call to `++blob-to-lobe`. Recall that the hash is stored in the
2504−actual blob itself. We do the same thing to the new yakis, putting them
2505−in `hut`, keyed by their hash.
2507−Now, our local dome should be exactly the same as the foreign one.
2509−This concludes our discussion of `++knit`. Now the changes have been
2510−applied to our local copy of the desk, and we just need to update our
2511−subscribers. We do so in `++wake:de`.
2514− ++ wake :: update subscribers
2516− =+ xiq=(~(tap by qyx) ~)
2517− =| xaq=(list ,[p=duct q=rove])
2520− ..wake(qyx (~(gas by *cult) xaq))
2523− =+ cas=?~(ref ~ (~(get by haw.u.ref) `mood`p.q.i.xiq))
2527− ..wake ?~ u.cas (blub p.i.xiq)
2528− (blab p.i.xiq p.q.i.xiq u.u.cas)
2530− =+ nao=(~(case-to-aeon ze lim dom ran) q.p.q.i.xiq)
2531− ?~ nao $(xiq t.xiq, xaq [i.xiq xaq])
2532− $(xiq t.xiq, ..wake (balk p.i.xiq u.nao p.q.i.xiq))
2535− =+ mot=`moot`p.q.i.xiq
2536− =+ nab=(~(case-to-aeon ze lim dom ran) p.mot)
2538− $(xiq t.xiq, xaq [i.xiq xaq])
2539− =+ huy=(~(case-to-aeon ze lim dom ran) q.mot)
2541− =+ ptr=[%ud +(let.dom)]
2544− xaq [[p.i.xiq [%| ptr q.mot r.mot s.mot]] xaq]
2546− (~(lobes-at-path ze lim dom ran) let.dom r.p.q.i.xiq)
2547− ?: =(s.p.q.i.xiq ear) ..wake
2548− =+ fud=(~(make-nako ze lim dom ran) u.nab let.dom)
2549− (bleb p.i.xiq let.dom fud)
2553− ..wake =- (blub:- p.i.xiq)
2555− (~(lobes-at-path ze lim dom ran) u.huy r.p.q.i.xiq)
2556− ?: =(s.p.q.i.xiq ear) (blub p.i.xiq)
2557− =+ fud=(~(make-nako ze lim dom ran) u.nab u.huy)
2558− (bleb p.i.xiq +(u.nab) fud)
2564−This is even longer than `++knit`, but it's pretty similar to `++eave`.
2565−We loop through each of our subscribers `xiq`, processing each in turn.
2566−When we're done, we just put the remaining subscribers back in our
2569−If the subscriber is a single read, then, if this is a foreign desk
2570−(note that `++wake` is called from other arms, and not only on foreign
2571−desks). Obviously, if we find an identical request there, then we can
2572−produce the result immediately. Referential transparency for the win. We
2573−produce the result with a call to `++blab`. If this is a foreign desk
2574−but the result is not in the cache, then we produce `++blub` (canceled
2575−subscription with no data) for reasons entirely opaque to me. Seriously,
2576−it seems like we should wait until we get an actual response to the
2577−request. If someone figures out why this is, let me know. At any rate,
2580−If this is a domestic desk, then we check to see if the case exists yet.
2581−If it doesn't, then we simply move on to the next subscriber, consing
2582−this one onto `xaq` so that we can check again the next time around. If
2583−it does exist, then we call `++balk` to fulfill the request and produce
2586−`++balk` is very simple, so we'll describe it here before we get to the
2590− ++ balk :: read and send
2591− |= [hen=duct oan=@ud mun=mood]
2593− =+ vid=(~(read-at-aeon ze lim dom ran) oan mun)
2594− ?~ vid (blub hen) (blab hen mun u.vid)
2597−We call `++read-at-aeon` on the given request and aeon. If you recall,
2598−this processes a `mood` at a particular aeon and produces the result, if
2599−there is one. If there is data at the requested location, then we
2600−produce it with `++blab`. Else, we call `++blub` to notify the
2601−subscriber that no data can ever come over this subscriptioin since it
2602−is now impossible for there to ever be data for the given request.
2603−Because referential transparency.
2605−At any rate, back to `++wake`. If the given rave is a subscription
2606−request, then we proceed similarly to how we do in `++eave`. We first
2607−try to get the aeon referred to by the starting case. If it doesn't
2608−exist yet, then we can't do anything interesting with this subscription,
2609−so we move on to the next one.
2611−Otherwise, we try to get the aeon referred to by the ending case. If it
2612−doesn't exist yet, then we produce all the information we can. We call
2613−`++lobes-at-path` at the given aeon and path to see if the requested
2614−path has actually changed. If it hasn't, then we don't produce anything;
2615−else, we produce the correct nako by calling `++bleb` on the result of
2616−`++make-nako`, as in `++eave`. At any rate, we move on to the next
2617−subscription, putting back into our cult the current subscription with a
2618−new start case of the next aeon after the present.
2620−If the aeon referred to by the ending case does exist, then we drop this
2621−subscriber from our cult and satisfy its request immediately. This is
2622−the same as before -- we check to see if the data at the path has
2623−actually changed, producing it if it has; else, we call `++blub` since
2624−no more data can be produced over this subscription.
2626−This concludes our discussion of foreign requests.