Reference ========= Data Models ----------- ### `++axle`, formal state ++ axle :: all %ford state $: %1 :: version for update pol=(map ship baby) :: == :: This is the formal state of our vane. Anything that must be remembered between calls to ford must be stored here. The number `%1` is a version number for our state that allows us to upgrade the structure of our state in the future if we wish. `pol` is the a map from every ship on our pier to their individual ford state. There is no shared ford state -- every ship is entirely separate. ### `++baby`, state by ship ++ baby :: state by ship $: tad=[p=@ud q=(map ,@ud task)] :: tasks by number dym=(map duct ,@ud) :: duct to task number jav=(map ,* calx) :: cache == :: This is the state specific to each ship. `tad` and `dym` keep track of the tasks we're currently working on. `dym` is a map from ducts to task numbers, and `q.tad` is a map from task number to the task itself. `p.tad` is the next available task number. Thus, the keys of `q.tad` are a subset of the numbers less than `p.tad`, and ford has attempted exactly `p.tad` tasks so far. `jav` is the cache of previously-solved problems. The keys are a pair of a term (either `%hood`, `%slap`, or `%slam`) and a noun that represents the exact problem solved. In the case of a `%hood`, then, the key is of the form `[%hood beam cage]`. For `%slap`, there is `[%slap vase twig]`. For `%slam`, there is `[%slam vase vase]`. The values are the result of solving the problem. Note that this cache is wiped in `++stay` when ford is reloaded. ### `++task`, problem in progress ++ task :: problem in progress $: nah=duct :: cause kas=silk :: problem kig=[p=@ud q=(map ,@ud beam)] :: blocks == :: This is all the state we keep regarding a particular task. `nah` is the duct which asked us to solve the problem, and `kas` is the most recent statement of the problem. `kig` keeps track of which resources we are blocked on. Our blocks are stored by index in `q.kig`, and the next available index is `p.kig`. ### `++silk`, problem ++ silk :: construction layer $& [p=silk q=silk] :: cons $% [%bake p=mark q=beam r=path] :: local synthesis [%boil p=mark q=beam r=path] :: general synthesis [%call p=silk q=silk] :: slam [%cast p=mark q=silk] :: translate [%diff p=silk q=silk] :: diff [%done p=(set beam) q=cage] :: literal [%dude p=tank q=silk] :: error wrap [%dune p=(set beam) q=(unit cage)] :: unit literal [%mute p=silk q=(list (pair wing silk))] :: mutant [%pact p=silk q=silk] :: patch [%plan p=beam q=spur r=hood] :: structured assembly [%reef ~] :: kernel reef [%ride p=twig q=silk] :: silk thru twig [%vale p=mark q=ship r=*] :: validate [our his] == :: This is the every type of problem we can solve. Every `%exec` kiss that requests us to solve a problem must choose one of these problems to solve. Because this is both an internal structure used in ford and the public interface to ford, we choose to document this structure in our discussion of the public interface to ford below. ### `++calx`, cache line ++ calx :: concrete cache line $% [%hood p=calm q=(pair beam cage) r=hood] :: compile [%slap p=calm q=[p=vase q=twig] r=vase] :: compute [%slam p=calm q=[p=vase q=vase] r=vase] :: compute == :: There are three kinds of cache entries. Every entry includes some metadata in `p` and is the combination of an input and its output. The input to a `%hood` is the location of the resource and a cage representing the data at that location. The output is the hood found by compiling the given cage at the given location. The input to a `%slap` is the vase of the subject and the twig of the formula against which we are slapping the subject. The output is the vase produced by slapping them. The input to a `%slam` is the vase of the subject and the vase of the gate which we are slapping. The output is the vase produced by slamming them. ### `++calm`, cache line metadata ++ calm :: cache metadata $: laz=@da :: last accessed dep=(set beam) :: dependencies == :: Every line in the cache needs to have two pieces of metadata. We must know the last time this line in the cache was accessed, and we must know what are the dependencies of this line. ### `++hood`, assembly components ++ hood :: assembly plan $: zus=@ud :: zuse kelvin sur=(list hoot) :: structures lib=(list hoof) :: libraries fan=(list horn) :: resources src=(list hoop) :: program == :: When assembling a hook file, we split it into several sections. `zus` is the kelvin version of the required zuse. In general, we assume that any newer (lower-numbered) zuse will retain backward compatibility, so any newer zuse is also permissible. This number is set with a `/?` at the beginning of the file. `sur` is the set of structures included. These are included with the `/-` rune. When a structure is included, we look in `/=main=/sur` for the given structure and we load the gate there. When compiling, all the included structures are collected into a single core placed in the subject of the body with a `=>`. `lib` is the set of librarires included. These are included with the `/+` rune. When a library is included, we look in `/=main=/lib` for the given library and we load the library there. As with structures, all the included libraries are collected into a single core placed in the subject of the body with a `=>`. `fan` is the set of resources included. These are loaded in many different ways and may load resources from any location. These are placed in the subject of the body with a `=~`. `src` is the set of twigs or references to twigs in the body of the program. Generally, each of these will represent a core, but this is not required. When compiling, these are strung together in a `=~`. ### `++hoot` ++ hoot (pair bean hoof) :: structure gate/core A structures may be either a direct gate or a core. These are syntactically distinguished by adding a `*` to the beginning of the structure name for a core. The structure itself is a `hoof`. ### `++hoof` ++ hoof (pair term (unit (pair case ship))) :: resource reference A hoof, which is either a structure or a library, has a name, and it may also specify which version of the resource to use and which ship to retrieve it from. ### `++horn` ++ horn :: resource tree $% [%ape p=twig] :: /~ twig by hand [%arg p=twig] :: /$ argument [%day p=horn] :: /| list by @dr [%dub p=term q=horn] :: /= apply face [%fan p=(list horn)] :: /. list [%for p=path q=horn] :: /, descend [%hub p=horn] :: /@ list by @ud [%man p=(map span horn)] :: /* hetero map [%nap p=horn] :: /% homo map [%now p=horn] :: /& list by @da [%saw p=twig q=horn] :: /; operate on [%see p=beam q=horn] :: /: relative to [%sic p=tile q=horn] :: /^ cast [%toy p=mark] :: /mark/ static == :: This is how we represent the static resources hook files can load. The discussion of their use from a user's perspective is documented elsewhere (link), so we will here only give a description of the data structure itself. A `%ape` horn is simply a twig that gets evaluated and placed in the subject. A `%arg` is a gate that gets evaluated with a sample of our location and our heel. A `%day` is a horn that applies to each of a list of `@dr`-named files in the directory. A `%dub` is a term and horn, where the result of a horn is given the face of the term. A `%fan` is a list of horns, all at the current directory level. A `%for` is a path and a horn, where the horn is evaluated relative to the given path, where the given path is relative to the current location. A `%hub` is a horn that applies to each of a list of `@ud`-named files in the directory. A `%man` is a map of spans to horns where the result is a set of each horn applied to the current directory given the associated face. A `%nap` is a homogenous map where each entry in a directory is handled with the same horn and is given a face according to its name. A `%now` is a horn that applies to each of a list of `@da`-named files in the directory. A `%saw` is a twig and a horn, where the twig operates on the result of the horn. A `%see` is a beam and a horn, where the horn is evaluated at a location of the given beam. A `%sic` is a tile and a horn, where the horn is evaluated and cast to the type associated with the tile. A `%toy` is simply a mark to be baked. ### `++hoop`, body ++ hoop :: source in hood $% [%& p=twig] :: direct twig [%| p=beam] :: resource location == :: This is an entry in the body of the hook file. The hoop can either be defined directly in the given file or it can be a reference to another file. The second is specified with a `//` rune. ### `++bolt`, monadic edge ++ bolt :: gonadic edge |* a=$+(* *) :: product clam $: p=cafe :: cache $= q :: $% [%0 p=(set beam) q=a] :: depends/product [%1 p=(set ,[p=beam q=(list tank)])] :: blocks [%2 p=(list tank)] :: error == :: == :: Throughout our computation, we let our result flow through with the set of dependencies of the value. At various times, we may wish to either throw an error or declare that the actual result cannot be found until a particular resource is retrieved. This is a perfect case for a monad, so here we define a data structure for it. At every step, we have a cache, so we store that in `p`. In `q` we store the data. In the case of `%0`, we have the result in `q` and the set of dependencies in `p`. In the case of `%1`, we have a set of dependencies on which we are blocking. When this happens, we make a call to clay to get the dependencies, and we proceed with the computation when we receive them. Technically, we restart the computation, but since every expensive step is cached, there is no significant performance penalty to doing this. Referential transparency has its uses. In the case of `%2`, we have a hit an error. This gets passed all the way through to the calling duct. The list of tanks is some description of what went wrong, often including a stack trace. ### `++burg`, monadic rule ++ burg :: gonadic rule |* [a=$+(* *) b=$+(* *)] :: from and to $+([c=cafe d=a] (bolt b)) :: :: :: To operate on bolts, we use `++cope` as our bind operator, and the functions it works on are of type `burg`. Our functions that operate on bolts should have a sample of the cache and a value. Their output should be a bolt of the output value. Then, `++cope` will only call the function when necessary (in the `%0` case), and it will do so without the wrapping of a bolt. If you understand monads, this is probably fairly obvious. Otherwise, see the discussion on `++cope` (link). Public Interface ---------------- Ford does not export a scry interface, so the only way to interact with ford is by sending kisses and receiving gifts. In fact, ford only sends accepts one kiss and gives one gift. This is, of course, misleading because ford actually does many different things. It does, however, only produce one type of thing -- a result of a computation, which is either an error or the value produced along with the set of dependencies referenced by it. ++ kiss :: in request ->$ $% [%exec p=@p q=(unit silk)] :: make / kill == :: The `%exec` gift requests ford to perform a computation on behalf of a particular ship. `p` is the ship, and `q` is the computation. If `q` is null, then we are requesting that ford cancel the computation that it is currently being run along this duct. Thus, if you wish to cancel a computation, you must send the kiss along the same duct as the original request. Otherwise, we ask ford to perform a certain computation, as defined in `++silk`. Since all computations produce the same type of result, we will discuss that result before we jump into `++silk`. ++ gift :: out result <-$ $% [%made p=(each bead (list tank))] :: computed result == :: We give either a `bead`, which is a result, or a list of tanks, which is an error messge, often including a stack trace. ++ bead ,[p=(set beam) q=cage] :: computed result This is a set of dependencies required to compute this value and a cage of the result with its associated mark. There are twelve possible computations defined in `++silk`. ++ silk :: construction layer $& [p=silk q=silk] :: cons $% [%bake p=mark q=beam r=path] :: local synthesis [%boil p=mark q=beam r=path] :: general synthesis [%call p=silk q=silk] :: slam [%cast p=mark q=silk] :: translate [%done p=(set beam) q=cage] :: literal [%dude p=tank q=silk] :: error wrap [%dune p=(set beam) q=(unit cage)] :: unit literal [%mute p=silk q=(list (pair wing silk))] :: mutant [%plan p=beam q=spur r=hood] :: structured assembly [%reef ~] :: kernel reef [%ride p=twig q=silk] :: silk thru twig [%vale p=mark q=ship r=*] :: validate [our his] == :: First, we allow silks to autocons. A cell of silks is also a silk, and the product vase is a cell of the two silks. This obviously extends to an arbitrary number of silks. `%bake` tries to functionally produce the file at a given beam with the given mark and heel. It fails if there is no way to translate at this level. `%boil` functionally produces the file at a given beam with the given mark and heel. If there is no way to translate at this beam, we pop levels off the stack and attempt to bake there until we find a level we can bake. This should almost always be called instead of `%bake`. `%call` slams the result of one silk against the result of another. `%cast` translates the given silk to the given mark, if possible. This is one of the critical and fundamental operations of ford. `%done` produces exactly its input. This is rarely used on its own, but many silks are recursively defined in terms of other silks, so we often need a silk that simply produces its input. A monadic return, if you will. `%diff` diffs the two given silks (which must be of the same mark), producing a cage of the mark specified in `++mark` in `++grad` for the mark of the two silks. `%dude` computes the given silk with the given tank as part of the stack trace if there is an error. `%dune` produces an error if the cage is empty. Otherwise, it produces the value in the unit. `%mute` takes a silk and a list of changes to make to the silk. At each wing in the list we put the value of the associated silk. `%pact` applies the second silk as a patch to the first silk. The second silk must be of the mark specified in `++mark` in `++grad` for the mark of the first silk. `%plan` performs a structured assembly directly. This is not generally directly useful because several other silks perform supersets of this functionality. We don't usually have naked hoods outside ford. `%reef` produces a core containing the entirety of zuse and hoon, suitable for running arbitrary code against. The mark is `%noun`. `%ride` slaps a twig against a subject silk. The mark of the result is `%noun`. `%vale` validates untyped data from a ship against a given mark. This is an extremely useful function. Commentary ========== Parsing Hook Files ------------------ In the commentary on other vanes, we have traced through the lifecycle of various external requests. This is generally a very reasonable order to examine vanes since it will eventually cover the entire vane, and we are never left wondering why we are doing something. For ford, however, it makes more sense to begin by discussing the parsing and assembliing of hook files. Many of the possible requests require us to assemble hook files, so we may as well examine this immediately. First, we will examine the parsing. We parse a file at a beam to a hood in `++fade:zo:za`. The top-level parsing rule is `++fair`, which takes a beam and produces a rule to parse an entire hood file. A note on the naming scheme: the parsing the combinators that parse into a particular structure are conventionally given the same name as the structure. Although this locally clobbers the type names, this pattern makes obvious the intent of the parsing combinators. We kick off with `++hood:fair`. ++ hood %+ ifix [gay gay] ;~ plug ;~ pose (ifix [;~(plug fas wut gap) gap] dem) (easy zuse) == :: ;~ pose (ifix [;~(plug fas hep gap) gap] (most ;~(plug com gaw) hoot)) (easy ~) == :: ;~ pose (ifix [;~(plug fas lus gap) gap] (most ;~(plug com gaw) hoof)) (easy ~) == :: (star ;~(sfix horn gap)) (most gap hoop) == There are five sections to a hood: system version, structures, libraries, resources, and body. First, we parse the requested version number of the system. This is specified with a unary `/?` rune. If not present, then we default to the current version. Second, we may have zero or more `/-` runes followed by a parsing of a `++hoot`, which represents a shared structure. Third, we may have zero or more `/+` runes followed by a parsing of a `++hoof`, which represents a shared library. Fourth, we may have zero or more other `/` runes (as described in `++horn`), which represent program-specific resources to be loaded. Fifth and finally, we must have one or more body statements (hoops), which are either direct twigs or `//` runes. ++ hoot ;~ pose (stag %| ;~(pfix tar hoof)) (stag %& hoof) == A structure can either be a direct gate, or it can be a simple core. Either one is parsed with `++hoof`, so we distinguish the two cases by requireing core references to be prefixed by a `*`. ++ hoof %+ cook |=(a=^hoof a) ;~ plug sym ;~ pose %+ stag ~ ;~(plug ;~(pfix fas case) ;~(pfix ;~(plug fas sig) fed:ag)) (easy ~) == == A hoof must have a name, which is a term. Optionally, we also include a case and a ship. This is marked by appending a `/` followed by a case to denote the requested version of the resource and a `/` followed by a ship name to denote the requested source of the resource. For example, `resource/1/~zod` requests the first version of `resource` on `~zod`. ++ case %- sear :_ nuck:so |= a=coin ?. ?=([%$ ?(%da %ud %tas) *] a) ~ [~ u=(^case a)] Here, we parse a literal with `++nuck:so`, and we accept the input if it is either an absolute date, an unsigned decimal, or a label. This leaves only horns and hoops to parse. Hoops are much simple to parse, so we'll discuss those first. ++ hoop ;~ pose (stag %| ;~(pfix ;~(plug fas fas gap) have)) (stag %& tall:vez) == There are two types of hoops. Direct twigs are parsed with `++tall:vast`, which is the just the hoon parser for a tall-form twig. References to external twigs are marked with a `//` rune followed by a beam, which is parsed with `++have`. ++ hath (sear plex:voz (stag %clsg poor:voz)) :: hood path ++ have (sear tome ;~(pfix fas hath)) :: hood beam `++have` parses a path with `++hath`, and then it converts the path into a beam with `++tome`. `++hath` parses a `/`-separated list with `++poor:vast`, then converts it to an actual path with `++plex:vast`. This leaves only horns to parse. ++ horn =< apex =| tol=? |% ++ apex %+ knee *^horn |. ~+ ;~ pfix fas ;~ pose (stag %toy ;~(sfix sym fas)) (stag %ape ;~(pfix sig ape:read)) (stag %arg ;~(pfix buc ape:read)) (stag %day ;~(pfix bar day:read)) (stag %dub ;~(pfix tis dub:read)) (stag %fan ;~(pfix dot fan:read)) (stag %for ;~(pfix com for:read)) (stag %hub ;~(pfix pat day:read)) (stag %man ;~(pfix tar man:read)) (stag %nap ;~(pfix cen day:read)) (stag %now ;~(pfix pam day:read)) (stag %saw ;~(pfix sem saw:read)) (stag %see ;~(pfix col see:read)) (stag %sic ;~(pfix ket sic:read)) == == Horn parsing is slightly complex, so we create an internal core to organize our code. Our core has a global variable of `tol`, which is true if tall form is permissible and false if we're already in wide form. We kick off the parsing with `++apex`. `++apex` specifies how each rune is parsed. This allows us to offload the different ways of parsing the arguments to these runes into separate arms. The exception here is that the `%toy` horn is simply of the form `/mark/`. We'll examine each of the horn parsing arms right after we discuss `++rail`, which is used in each one. ++ rail |* [wid=_rule tal=_rule] ?. tol wid ;~(pose wid tal) This takes a wide-form and a tall-form parsing rule. If tall form is permissible, then it allows either rule to match; else, it allows only the wide form rule. ++ read |% ++ ape %+ rail (ifix [sel ser] (stag %cltr (most ace wide:vez))) ;~(pfix gap tall:vez) `++ape:read` parses for both the `/~` and the `/$` runes. It produces a twig. The wide form is a tuple of one or more ace-separated wide-form twigs parsed with `++wide:vast` and surrounded by `[` and `]`. The tall form is a single tall form twig parsed by `++tall:vast` ++ day %+ rail apex(tol |) ;~(pfix gap apex) This parses for the `/|`, `/@`, `/%`, and `/&` runes. It produces a horn. The wide form is, recursively, the entire horn parser with tall form disabled. The tall form is a gap followed by, recursively, the entire horn parser. ++ dub %+ rail ;~(plug sym ;~(pfix tis apex(tol |))) ;~(pfix gap ;~(plug sym ;~(pfix gap apex))) This parses for the `/=` rune. It produces a term followed by a horn. The wide form is a symbol name followed by a `=` and, recursively, the entire horn parser with tall form disabled. The tall form is a gap followed by a symbol name, another gap, and, recursively, the entire horn parser. ++ fan %+ rail fail ;~(sfix (star ;~(pfix gap apex)) ;~(plug gap duz)) This parses for the `/.` rune. It produces a list of horns. There is no wide form. The tall form is a stet-terminated series of gap-separated recursive calls to the entire horn parser. ++ for %+ rail ;~(plug (ifix [sel ser] hath) apex(tol |)) ;~(pfix gap ;~(plug hath ;~(pfix gap apex))) This parses for the `/,` rune. It produces a path and a horn. The wide form is a `[`-`]`-surrounded path followed by, recursively, the entire horn parser with tall form disabled. The tall form is a gap followed by a path, another gap, and, recursively, the entire horn parser. ++ man %+ rail fail %- sear :_ ;~(sfix (star ;~(pfix gap apex)) ;~(plug gap duz)) |= fan=(list ^horn) =| naf=(list (pair term ^horn)) |- ^- (unit (map term ^horn)) ?~ fan (some (~(gas by *(map term ^horn)) naf)) ?. ?=(%dub -.i.fan) ~ $(fan t.fan, naf [[p.i.fan q.i.fan] naf]) This parses for the `/*` rune. It produces a map of spans to horns. There is no wide form. The tall form is a stet-terminated series of gap-separated recursive calls to the entire horn parser. All produced horns are expected to be from `/=` runes. The term and horn in each `/=` horn is inserted into the produced map as a key-value pair. ++ saw %+ rail ;~(plug ;~(sfix wide:vez sem) apex(tol |)) ;~(pfix gap ;~(plug tall:vez ;~(pfix gap apex))) This parses for the `/;` rune. It produces a twig and a horn. The wide form is a wide-form twig followed by a `;` and, recursively, the entire horn parser with tall form disabled. The tall form is a gap followed by a tall-form twig, another gap, and, recursively, the entire horn parser. ++ see %+ rail ;~(plug ;~(sfix have col) apex(tol |)) ;~(pfix gap ;~(plug have ;~(pfix gap apex))) This parses for the `/:` rune. It produces a beam and a horn. The wide form is a beam followed by a `;` and, recursively, the entire horn parser with tall form disabled. The tall form is a gap followed by a beam, another gap, and, recursively, the entire horn parser. ++ sic %+ rail ;~(plug ;~(sfix toil:vez ket) apex(tol |)) ;~(pfix gap ;~(plug howl:vez ;~(pfix gap apex))) -- This parses for the `/^` rune. It produces a tile and a horn. The wide form is a wide-form tile, parsed with `++toil:vast`, followed by a `^` and, recursively, the entire horn parser with tall form disabled. The tall form is a gap followed by a tall-form tile, parsed with `++howl:vast`, another gap, and, recursively, the entire horn parser. Assembling Hook Files --------------------- At this point, we've parsed a hook file into a hood. We will now describe exactly how this hood is assembled into a vase. The problem of assembling is handled entirely within the `++meow:zo:za` core. ++ meow :: assemble |= [how=beam arg=heel] =| $: rop=(map term (pair hoof twig)) :: structure/complex zog=(set term) :: structure guard bil=(map term (pair hoof twig)) :: libraries known lot=(list term) :: library stack zeg=(set term) :: library guard boy=(list twig) :: body stack hol=? :: horns allowed? == |% We take two arguments and keep seven pieces of state. `how` is the location of the hook file we're assembling, and `arg` is the heel, or virtual path extension, of the file. In `rop`, we maintain a map of terms to pairs of hooves and twigs to represent the structures we've encountered that we will put together in a core at the top of the file. In `zog`, we maintain the set of structures we're in the middle of loading. If we try to load a structure already in our dependency ancestry, then we fail because we do not allow circular dependencies. This enforces that our structure dependency graph is a DAG. In `bil`, we maintain a map of terms to pairs of hooves and twigs to represent the libraries we've encountered that we will put together in a series of cores after the structure core. In `lot`, we maintain a stack of library names as they are encountered during a depth-first search. More precisely, we push a library onto the stack after we've processed all its children. Thus, every library depends only on things deeper in the list. The libraries must be loaded in the reverse of this order. Concisely, this is a topological sort of the library dependency partial ordering. In `zeg`, we maintain the set of libraries we're in the middle of loading. If we try to load a library already in our dependency ancestry, then we fail because we do not allow circular dependencies. This enforces that our library dependency graph is a DAG. In `boy`, we maintain a stack of body twigs, which we'll put together in a series of cores at the end of the file. In `hol`, we decide if we're allowed to contain horns. Libraries and structures are not allowed to contain horns. We in every case enter `++meow` through `++abut`. You'll notice that there are four (count 'em, four!) calls to `++cope` in `++abut`. If you've glanced at the ford code in general, you've probably seen cope over and over. It is called in 79 different places. We need to discuss the use of this critical function in detail, so we may as well do it here. ++ cope :: bolt along |* [hoc=(bolt) fun=(burg)] ?- -.q.hoc %2 hoc %1 hoc %0 =+ nuf=(fun p.hoc q.q.hoc) :- p=p.nuf ^= q ?- -.q.nuf %2 q.nuf %1 q.nuf %0 [%0 p=(grom `_p.q.nuf`p.q.hoc p.q.nuf) q=q.q.nuf] == == In monad-speak, this is the bind operator for the bolt monad. If monads aren't your thing, don't worry, we're going to explain the use of cope without further reference to them. Recall that there are three different types of bolt. A `%2` error bolt contains a list of tanks describing the error, a `%1` block bolt contains a set of resources we're blocked on, and a `%0` value bolt contains an actual value and the set of its dependencies. We most commonly want to perform an operation on the value in a bolt if it is a `%0` bolt. If it's not a `%0` bolt, we want to leave it alone. This requires us to write a certain amount of boilerplate between each of our operations to see if any of them produced a `%1` or a `%2` bolt. This gets tiresome, so we pull it out into a separate arm and call it `++cope`. Intuitively, we're calling the function `fun` with the value in `hoc`, where `fun` takes an argument of type whatever is the value in a `%0` case of `hoc`, and it produces a bolt of some (possibly different) type. For brevity, we will refer to the type of the of the value in the `%0` case of a bolt as the "type of the bolt". If the `hoc` bolt we're given as input to `fun` is already a `%1` or a `%2` bolt, then we simply produce that. We don't even try to run `fun` on it. Otherwise, we run `fun` with the arguments from the bolt and, if it produces a `%1` or a `%2` bolt, we simply produce that. If it produces a `%0` bolt, then we produce that with the old set of dependencies merged in with the new set. We'll see more about how the bolt monad works as we run into more interesting uses of it. For now, this is sufficient to move on with `++abut`. ++ abut :: generate |= [cof=cafe hyd=hood] ^- (bolt vase) %+ cope (apex cof hyd) |= [cof=cafe sel=_..abut] =. ..abut sel %+ cope (maim cof pit able) |= [cof=cafe bax=vase] %+ cope (chap cof bax [%fan fan.hyd]) |= [cof=cafe gox=vase] %+ cope (maim cof (slop gox bax) [%tssg (flop boy)]) |= [cof=cafe fin=vase] (fine cof fin) Our job is simple: we must assemble a hood file into a vase. Hopefully, the usage of `++cope` is fairly understandable. The correct way to read this is that it does essentially five things. First, we call `++apex` to process the structures, libraries, and body. This changes our state, so we set our context to the produced context. Second, we call `++able` to assemble the strucutres and libraries into a twig, which we slap against zuse with `++maim`. Third, we call `++chap` to process the resources in the context of the already-loaded structures and libraries. Fourth, we slap the body against the structures, libraries, and resources. Fifth and finally, we produce the resultant vase. ++ apex :: build to body |= [cof=cafe hyd=hood] ^- (bolt ,_..apex) ?. |(hol ?=(~ fan.hyd)) %+ flaw cof :_ ~ :- %leaf "horns not allowed in structures and libraries: {<[how arg]>}" %+ cope (body cof src.hyd) |= [cof=cafe sel=_..apex] =. ..apex sel %+ cope (neck cof lib.hyd) |= [cof=cafe sel=_..apex] =. ..apex sel(boy boy) %+ cope (head cof sur.hyd) |= [cof=cafe sel=_..apex] (fine cof sel) First, we make sure that if we're not allowed to have horns, we don't. Otherwise, we produce and error with `++flaw`. ++ flaw |=([a=cafe b=(list tank)] [p=a q=[%2 p=b]]) :: bolt from error This produces a `%2` error bolt from a list of tanks. Fairly trivial. We should be starting to get used to the cope syntax, so we can see that we really only do three things here. We process the body with `++body`, the libraries with `++neck`, and the structures with `++head`. ++ body :: produce functions |= [cof=cafe src=(list hoop)] ^- (bolt _..body) ?~ src (fine cof ..body) %+ cope (wilt cof i.src) |= [cof=cafe sel=_..body] ^$(cof cof, src t.src, ..body sel) We must process a list of hoops that represent our body. If there are no more hoops, we just produce our context in a `%0` bolt with `++fine`. ++ fine |* [a=cafe b=*] :: bolt from data [p=`cafe`a q=[%0 p=*(set beam) q=b]] :: In monad-speak, this is the return operator. For us, this just means that we're producing a `%0` bolt, which contains a path and a set of dependencies. We assume there are no dependencies for the given data, or that they will be added later. If there are more hoops in `++body`, we call `++wilt` to process an individual hoop and recurse. ++ wilt :: process body entry |= [cof=cafe hop=hoop] ^- (bolt _..wilt) ?- -.hop %& (fine cof ..wilt(boy [p.hop boy])) %| %+ cool |.(leaf/"ford: wilt {<[(tope p.hop)]>}") %+ cope (lend cof p.hop) |= [cof=cafe arc=arch] ?: (~(has by r.arc) %hoon) %+ cope (fade cof %hoon p.hop) |= [cof=cafe hyd=hood] %+ cope (apex(boy ~) cof hyd) |= [cof=cafe sel=_..wilt] (fine cof sel(boy [[%tssg boy.sel] boy])) =+ [all=(lark (slat %tas) arc) sel=..wilt] %+ cope |- ^- (bolt (pair (map term foot) _..wilt)) ?~ all (fine cof ~ ..wilt) %+ cope $(all l.all) |= [cof=cafe lef=(map term foot) sel=_..wilt] %+ cope ^$(all r.all, cof cof, sel sel) |= [cof=cafe rig=(map term foot) sel=_..wilt] %+ cope %= ^^^^$ cof cof ..wilt sel(boy ~) s.p.hop [p.n.all s.p.hop] == |= [cof=cafe sel=_..wilt] %+ fine cof [`(map term foot)`[[p.n.all [%ash [%tssg boy.sel]]] lef rig] sel] |= [cof=cafe mav=(map term foot) sel=_..wilt] ?~ mav (flaw cof [%leaf "source missing: {<(tope p.hop)>}"]~) (fine cof sel(boy [[%brcn mav] boy])) == In the case of a direct twig hoop, we just push it onto `boy` and we're done. In the case of an indirect hoop, we must compile the referenced file. First, we push onto the stack trace a message indicating which file exactly we're compiling at the moment with `++cool`. ++ cool :: error caption |* [cyt=trap hoc=(bolt)] ?. ?=(%2 -.q.hoc) hoc [p.hoc [%2 *cyt p.q.hoc]] If an error occurred in computing `hoc`, we put the bunt of `cyt` onto the stack trace. Thus, `cyt` is not evaluated at all unless an error occurred. Next in `++wilt`, we load the information about the filesystem node referenced by the hoop with `++lend`. ++ lend :: load arch |= [cof=cafe bem=beam] ^- (bolt arch) =+ von=(ska %cy (tope bem)) ?~ von [p=cof q=[%1 [bem ~] ~ ~]] (fine cof ((hard arch) (need u.von))) This is a simple call to the namespace. If the resource does not yet exist, we block on it by producing a `%1` bolt. Otherwise, we cast it to an arch and produce this. Continuing in `++wilt`, we examine the produced arch. If the referenced filesystem node has a `hoon` child node, then we've found the required source, so we parse it with `++fade`. Recall that we referred earlier to `++fade`. The salient point there is that it takes a beam, reads in the hook file there, and parses it into a hood file with `++fair`. Now, we simply recurse on `++apex` to compile the new hood. Note that, while we do clear the `boy` list, we do not clear the other lists. Thus, we are accumulating all the structures and libraries referenced in all the referenced hook files in one group, which we will put at the top of the product. After this, we put the new list of body twigs into a `=~`, push this onto our old list of body twigs, and produce the result. If there is no hoon file here, then we descend into each of our children until we find a hoon file. First, we produce a list of all our children whose names are terms with `++lark`. ++ lark :: filter arch names |= [wox=$+(span (unit ,@)) arc=arch] ^- (map ,@ span) %- ~(gas by *(map ,@ span)) =| rac=(list (pair ,@ span)) |- ^+ rac ?~ r.arc rac =. rac $(r.arc l.r.arc, rac $(r.arc r.r.arc)) =+ gib=(wox p.n.r.arc) ?~(gib rac [[u.gib p.n.r.arc] rac]) We traverse the children map of `arc` to filter out those children whose names aren't accepted by `wox` and produce a map from the product of `wox` to the original name. `++lark` is used in many cases to parse names into other types, like numbers or dates, ignoring those which do not fit the format. In `++wilt`, though, we simply want to filter out those children whose names are not terms. Next, we will produce a map from terms to feet. Each of these feet will be placed in a core named by the child name, and it will contain arms according to its children. Thus, if the indirect hoop references `/path`, then to access the twig defined in `/path/to/twig/hoon`, our body must refer to `twig:to`. If there are no more children, then we are done, so we produce our current context. Else, we recurse into the left and right sides of our map. Finally, we process our current entry in the map. We first recurse by calling `++wilt` one level down. Thus, in the previous example, the first time we get to this point we are processing `/path`, so we recurse on `++wilt` with path `/path/to`. We also remove our current body from the recursion, so that we may add it back in later the way we want to. After recursing, we push the new body onto our map, keyed by its name. We also produce the new context so that all external structures, libraries, and resources are collected into the same place. Finally, we have a map of names to feet. If this map is empty, then there were no twigs at the requested path, so we give an error with `++flaw`. If the map is nonempty, then we finally produce our context with with one thing pushed onto the front: a core made out of the map we just produced. This concludes our discussion of `++wilt` and `++body`. Thus, it remains in `++apex` to discuss `++neck` and `++head`. ++ neck :: consume libraries |= [cof=cafe bir=(list hoof)] ^- (bolt ,_..neck) ?~ bir (fine cof ..neck) ?: (~(has in zeg) p.i.bir) (flaw cof [%leaf "circular library dependency: {}"]~) =+ gez=(~(put in zeg) p.i.bir) =+ byf=(~(get by bil) p.i.bir) ?^ byf ?. =(`hoof`i.bir `hoof`p.u.byf) (flaw cof [%leaf "library mismatch: {<~[p.u.byf i.bir]>}"]~) $(bir t.bir) =+ bem=(hone %core %lib i.bir) %+ cope (fade cof %hook bem) |= [cof=cafe hyd=hood] %+ cope (apex(zeg gez, hol |, boy ~) cof hyd) |= [cof=cafe sel=_..neck] =. ..neck %= sel zeg zeg hol hol lot [p.i.bir lot] bil (~(put by bil) p.i.bir [i.bir [%tssg (flop boy.sel)]]) == ^^$(cof cof, bir t.bir) Here, we're going to consume the list of libraries and place them in `bil`. If there are no more libraries, we're done, so we just produce our current context. Otherwise, we check to see if the next library in the list is in `zeg`. If so, then this library is one of the libraries that we're already in the middle of compiling. There is a circular dependency, so we fail. Otherwise, we let `gez` be `zeg` plus the current library so that while compiling the dependencies of this library we don't later create a circular dependency. We check next to see if this library is alredy in `bil`. If so, then we have already included this library earlier, so we check to see if this is the same version of the library as we included earlier. If so, we skip it. Else, we fail since we can't include two different versions of a library. We really should allow for newer versions of a library since in kelvin versioning we assume backwards compatibility, but for now we require an exact match. If we haven't already included this library, then we're going to do that. First, we get the location of the library with `++hone`. ++ hone :: plant hoof |= [for=@tas way=@tas huf=hoof] ^- beam ?~ q.huf how(s ~[for p.huf way]) [[q.u.q.huf %main p.u.q.huf] ~[for p.huf way]] If we haven't specified the version of the library, we use the current ship, desk, and case. Otherwise, we use the given ship and case on desk `%main`. In either case, the path is `/way/p.huf/for`. In the case of `++neck`, this means `/lib/core/[library name]`. In `++neck`, we next compile the hook file at that location with `++fade`. Again, we will delay the discussion of `++fade`, noting only that it takes a beam and parses the hook file there into a hood. We recurse on this to compile the library. During the compilation, we let `zeg` be `gez` to avoid circular dependencies, we let `hol` be false since we don't allow horns in libraries, and we let `boy` be null so that we can isolate the new body twigs. Next, we reintegrate the new data into our context. We use the context created by the recursion with four changes. First, we reset `zeg` to our old `zeg`. Second, we reset `hol` to our old `hol`. Third, we put the name of our library onto the stack of libraries. This means all of a libraries dependencies will be earlier in `lot` than the library itself, making `lot` a topological ordering on the dependency graph. Fourth, we put in `bil` the library hoof and body (with all body twigs collected in a `=~`), keyed by the library name. Finally, we recurse, processing the next library in our list. To complete our disucssion of `++apex`, we must process our structures. ++ head :: consume structures |= [cof=cafe bir=(list hoot)] |- ^- (bolt ,_..head) ?~ bir (fine cof ..head) ?: (~(has in zog) p.q.i.bir) (flaw cof [%leaf "circular structure dependency: {}"]~) =+ goz=(~(put in zog) p.q.i.bir) =+ byf=(~(get by rop) p.q.i.bir) ?^ byf ?. =(`hoof`q.i.bir `hoof`p.u.byf) (flaw cof [%leaf "structure mismatch: {<~[p.u.byf q.i.bir]>}"]~) $(bir t.bir) =+ bem=(hone ?:(p.i.bir %gate %core) %sur q.i.bir) %+ cope (fade cof %hook bem) |= [cof=cafe hyd=hood] %+ cope (apex(zog goz, hol |, boy ~) cof hyd) |= [cof=cafe sel=_..head] ?. =(bil bil.sel) (flaw cof [%leaf "structures cannot include libraries: {}"]~) =. ..head %= sel boy ?: p.i.bir boy (welp boy [[[%cnzy p.q.i.bir] [%$ 1]] ~]) zog zog hol hol rop %+ ~(put by (~(uni by rop) rop.sel)) p.q.i.bir [q.i.bir [%tssg (flop boy.sel)]] == ^^$(cof cof, bir t.bir) The processing of our structures is very similar to that of our libraries. For clarity, we'll use many of the same phrases in describing the parallel natures. First, we check to see if there are more structures to process. If not, we're done, so we produce our context. Otherwise, we let `goz` be `zog` plus the current structure so that while compiling the dependencies of this structure we don't later create a circular dependency. We check next to see if this structure is alredy in `rop`. If so, then we have already included this structure earlier, so we check to see if this is the same version of the structure as we included earlier. If so, we skip it. Else, we fail since we can't include two different versions of a structure. If we haven't loaded this structure, then we call `++hone` to get the beam where the file structure should be. If the loobean in the hoot is true, then we're looking for a gate; otherwise, we're looking for a core. We parse this file with `++fade`. Now, we recurse on this to compile the structure. During the recursion, there we have threee changes. Frist, we let `zog` be `goz` so that we don't create a circular dependency. Second, we let `hol` be false since we do not allow horns in structures. Third, we let `boy` be null so that we can isolate the new body twigs. Next, we reintegrate the new data into our context. We use the context cretaed by the recursion with four changes. First, if we're including a gate structure, then we reset the body to its original body. Else we put on the top of our list of body twigs what is essentially a `=+ structure-name` to take off the face of the structure. Second, we reset `zog` to our old `zog`. Third, we reset `hol` to our old `hol`. Finally, we put in `rop` the structure hoof and body (with all body twiggs collected in a `=~`), keyed by the structure name. Finally, we recurse, processing the next structure in our list. This concludes our discussion of `++apex`. ++ abut :: generate |= [cof=cafe hyd=hood] ^- (bolt vase) %+ cope (apex cof hyd) |= [cof=cafe sel=_..abut] =. ..abut sel %+ cope (maim cof pit able) |= [cof=cafe bax=vase] %+ cope (chap cof bax [%fan fan.hyd]) |= [cof=cafe gox=vase] %+ cope (maim cof (slop gox bax) [%tssg (flop boy)]) |= [cof=cafe fin=vase] (fine cof fin) Returning to `++abut`, we have now processed the structures, libraries and body twigs. Next, we slap our preamble (structures and libraries) against zuse. First, we construct our preamble in `++able`. ++ able :: assemble preamble ^- twig :+ %tsgr ?:(=(~ rop) [%$ 1] [%brcn (~(run by rop) |=([* a=twig] [%ash a]))]) [%tssg (turn (flop lot) |=(a=term q:(need (~(get by bil) a))))] We first put the structures in `rop` into a single `|%` at the top and `=>` it onto a `=~` of our libraries, in the reverse order that they appear in `lot`. Thus, the structures are in a single core while the libraries are in consecutive cores. We slap the preamble against zuse with `++maim`. ++ maim :: slap |= [cof=cafe vax=vase gen=twig] ^- (bolt vase) %+ (clef %slap) (fine cof vax gen) |= [cof=cafe vax=vase gen=twig] =+ puz=(mule |.((~(mint ut p.vax) [%noun gen]))) ?- -.puz | (flaw cof p.puz) & %+ (coup cof) (mock [q.vax q.p.puz] (mole ska)) |= val=* `vase`[p.p.puz val] == Here we start to get into ford's caching system. We wrap our computation in a call to `++clef` so that we only actually compute it if the result is not already in our cache. First we'll discuss the computation, then we'll discuss the caching system. We call `++mule` with a call to `++mint:ut` on the type of our subject vase against the given twig. In other words, we're compiling the twig with against the subject type in the given subject vase. If compilation fails, then we produce an error bolt with the produced stack trace. Otherwise, we run the produced nock with `++mock` and our sky function. We convert the produced toon to a bolt with `++coup` and use the type from `puz` combined with the value from `mock` to produce our vase. If this process seems harder than just calling `++slap`, it's because it is. We have two requirements that `++slap` doesn't satisfy. First, we want the to use an explicit sky function for use with `.^`. With `++slap`, you get whatever sky function is available in the calling context, which in ford is none. Second, we want to explicitly handle the stack trace on failure. `++slap` would cause crash on failure. We haven't yet discussed either `++clef` or `++coup`. We'll start with `++coup` to finish the discussion of the computation. \``++ coup :: toon to bolt |= cof=cafe |* [ton=toon fun=$+(* *)] :- p=cof ^= q ?- -.ton %2 [%2 p=p.ton] %0 [%0 p=*(set beam) q=(fun p.ton)] %1 ~& [%coup-need ((list path) p.ton)] =- ?- -.faw & [%1 p=(sa (turn p.faw |=(a=beam [a *(list tank)])))] | [%2 p=p.faw] == ^= faw |- ^- (each (list beam) (list tank)) ?~ p.ton [%& ~] =+ nex=$(p.ton t.p.ton) =+ pax=(path i.p.ton) ?~ pax [%| (smyt pax) ?:(?=(& -.nex) ~ p.nex)] =+ zis=(tome t.pax) ?~ zis [%| (smyt pax) ?:(?=(& -.nex) ~ p.nex)] ?- -.nex & [%& u.zis p.nex] | nex == ==` Recall that a toon is either a `%0` value, a `%1` block, or a `%2` failure. Converting a `%2` toon failure into a `%2` bolt failure is trivial. Converting a `%0` toon value into a `%0` bolt value is easy since we assume there were no dependencies. Converting the blocks is rather more difficult. To compute `faw`, we recurse through the list of paths in the `%1` toon. At each one, we make sure with `++tome` that it is, in fact, a beam. If so, then we check to see if the later paths succeed as well. If so, we append the current path to the list of other paths. If not, we produce the error message we got from processing the rest of the paths. If this path is not a beam, then we fail, producing a list of tanks including this path and, if later paths fail too, those paths as well. If some paths were not beams, then we produce a `%2` error bolt. If all paths were correct, then we produce a `%1` blocking bolt. We will now discuss `++clef`. This is where the cache magic happens. ++ clef :: cache a result |* sem=* |* [hoc=(bolt) fun=(burg)] ?- -.q.hoc %2 hoc %1 hoc %0 =^ cux p.hoc ((calk p.hoc) sem q.q.hoc) ?~ cux =+ nuf=(cope hoc fun) ?- -.q.nuf %2 nuf %1 nuf %0 :- p=(came p.nuf `calx`[sem `calm`[now p.q.nuf] q.q.hoc q.q.nuf]) q=q.nuf == [p=p.hoc q=[%0 p=p.q.hoc q=((calf sem) u.cux)]] == If the value is already an error or a block, we just pass that through. Otherwise, we look up the request in the cache with `++calk`. ++ calk :: cache lookup |= a=cafe :: |= [b=@tas c=*] :: ^- [(unit calx) cafe] :: =+ d=(~(get by q.a) [b c]) :: ?~ d [~ a] :: [d a(p (~(put in p.a) u.d))] :: When looking up something in the cache, we mark it if we find it. This way, we have in our cache the set of all cache entries that have been referenced. While we do not at present do anything with this data, it should be used to clear out old and unused entries in the cache. Moving on in `++clef`, we check to see if we actually found anything. If we didn't find a cache entry, then we run the computation in `fun`, and examine its result. If it produced a `%2` error or `%1` block bolt, we just pass that through. Otherwise, we produce both the value and an updated cache with this new entry. We add the entry with `++came`. ++ came :: |= [a=cafe b=calx] :: cache install ^- cafe :: a(q (~(put by q.a) [-.b q.b] b)) :: We key cache entries by the type of computation (`-:calx`) and the inputs to the computation (`q:calc`). This just puts the cache line in the cache at the correct key. Back in `++clef`, if we did find a cache entry, then we just produce the value at that cache line. We convert the cache line into a value with `++calf`. ++ calf :: reduce calx |* sem=* :: a typesystem hack |= cax=calx ?+ sem !! %hood ?>(?=(%hood -.cax) r.cax) %slap ?>(?=(%slap -.cax) r.cax) %slam ?>(?=(%slam -.cax) r.cax) == This is simply a typesystem hack. Because the `sem` is passed in through a wet gate, we know at type time which of the three cases will be chosen. Thus, the correct type of the value in the cache line gets passed through to the caller. This also depends on the fact that `++clef` is wet. The type stuff here is mathematically interesting, but the action is simple: we get the value from the cache line. This concludes our discussion of `++clef` and `++maim`. Back in `++abut`, recall that we processed the structures, libraries, and body with `++apex`. Then, we slapped our preamble (structures and libraries) against zuse with `++maim`. Next, we process our resources with `++chap`. Note that we pass in the preamble so that we may refer to anything in there in our resources. `++chap` is broken up into a different case for each horn. We'll go through them one by one. ++ chap :: produce resources |= [cof=cafe bax=vase hon=horn] ^- (bolt vase) ?- -.hon %ape (maim cof bax p.hon) This is `/~`. We slap the twig against our context. %arg %+ cope (maim cof bax p.hon) |= [cof=cafe gat=vase] (maul cof gat !>([how arg])) This is `/$`. We slap the twig against our context, which we expect to produce a gate. We slam this gate with a sample of `how` and `arg`, which is our location and the heel (virtual path extension). `++maul` is similar to `++maim`, but it slams instead of slaps. ++ maul :: slam |= [cof=cafe gat=vase sam=vase] ^- (bolt vase) %+ (clef %slam) (fine cof gat sam) |= [cof=cafe gat=vase sam=vase] =+ top=(mule |.((slit p.gat p.sam))) ?- -.top | (flaw cof p.top) & %+ (coup cof) (mong [q.gat q.sam] (mole ska)) |= val=* `vase`[p.top val] == We cache slams exactly as we cache slaps. We use `++slit` to find the type of the product of the slam given the types of the gate and the sample. If this type fails, we produce the given stack trace as a `%2` error bolt. Otherwise, we produce the top produced above combined with the value we get from slamming the values in the vases with `++mong`. Back to `++chap`. %day (chad cof bax %dr p.hon) This is `/|`. We call `++chad` to convert textual names to relative dates and process the next horn against each of the discovered paths. ++ chad :: atomic list |= [cof=cafe bax=vase doe=term hon=horn] ^- (bolt vase) %+ cope ((lash (slat doe)) cof how) |= [cof=cafe yep=(map ,@ span)] =+ ^= poy ^- (list (pair ,@ span)) %+ sort (~(tap by yep) ~) |=([a=[@ *] b=[@ *]] (lth -.a -.b)) %+ cope |- ^- (bolt (list (pair ,@ vase))) ?~ poy (fine cof ~) %+ cope $(poy t.poy) |= [cof=cafe nex=(list (pair ,@ vase))] %+ cope (chap(s.how [q.i.poy s.how]) cof bax hon) |= [cof=cafe elt=vase] (fine cof [[p.i.poy elt] nex]) |= [cof=cafe yal=(list (pair ,@ vase))] %+ fine cof |- ^- vase ?~ yal [[%cube 0 [%atom %n]] 0] (slop (slop [[%atom doe] p.i.yal] q.i.yal) $(yal t.yal)) First, we call `++lash` to parse the children of the current beam and pick out those ones that are of the requested format. ++ lash :: atomic sequence |= wox=$+(span (unit ,@)) |= [cof=cafe bem=beam] ^- (bolt (map ,@ span)) %+ cope (lend cof bem) |= [cof=cafe arc=arch] (fine cof (lark wox arc)) First, we get the arch with `++lend`, as described above. We filter and parse the child names with `++lark` according to the given parser function. In `++chad`, this parser function is `(slat doe)`, which will parse a cord into an atom of the requested odor. For `%day` the odor is for relative dates. Thus, we now have a map from atoms of the given odor to the actual child names. We next turn this map into a list and sort it in increasing order by the atom. We next convert this list of pairs of atoms and spans to a list of pairs of atoms and vases. We process the given horn once at every child beam, producing the resource at that location. Finally, we convert this list of pairs of atoms and vases to a vase of a list of pairs of atoms to (well-typed) values. Each entry in the list is of type atom with the given odor combined with the type of the produced vase. Back in `++chap`, we continue parsing resources. %dub %+ cope $(hon q.hon) |= [cof=cafe vax=vase] (fine cof [[%face p.hon p.vax] q.vax]) This is `/=`. We process the given horn, giving us a vase. We put as a face on the vase so that it may be referred to later by name. %fan %+ cope |- ^- (bolt (list vase)) ?~ p.hon (fine cof ~) %+ cope ^$(hon i.p.hon) |= [cof=cafe vax=vase] %+ cope ^$(cof cof, p.hon t.p.hon) |= [cof=cafe tev=(list vase)] (fine cof [vax tev]) |= [cof=cafe tev=(list vase)] %+ fine cof |- ^- vase ?~ tev [[%cube 0 [%atom %n]] 0] (slop i.tev $(tev t.tev)) This is `/.`. We first process each of the child horns, producing a list of vases. This is done by just recursing on `++chap`. Then, we simply fold over this list to create a vase of the list of values. %for $(hon q.hon, s.how (weld (flop p.hon) s.how)) This is `/,`. We simply recurse on the horn with the given path welded onto our current beam. %hub (chad cof bax %ud p.hon) This is `/@`. This is exactly like the processing of `%day` except we expect the children to be named as unsigned integers rather than relative dates. We process the horn at each of the children's locations and produce a list of pairs of absolute dates and values. %man |- ^- (bolt vase) ?~ p.hon (fine cof [[%cube 0 [%atom %n]] 0]) %+ cope $(p.hon l.p.hon) |= [cof=cafe lef=vase] %+ cope ^$(cof cof, p.hon r.p.hon) |= [cof=cafe rig=vase] %+ cope ^^^$(cof cof, hon q.n.p.hon) |= [cof=cafe vax=vase] %+ fine cof %+ slop (slop [[%atom %tas] p.n.p.hon] vax) (slop lef rig) This is `/*`. We process each of the horns in the given map by recursion through `++chap`. Once we have these vases, we create a vase of a map from the given textual names to the produced values. %now (chad cof bax %da p.hon) This is `/&`. This is exactly like the processing of `%now` except we expect the children to be names as absolute dates rather than relative dates. We process the horn at each of the children's locations and produce a list of pairs of absolute dates and values. %nap (chai cof bax p.hon) This is `/%`. Here, we process the horn at each of our children with `++chai`. ++ chai :: atomic map |= [cof=cafe bax=vase hon=horn] ^- (bolt vase) %+ cope (lend cof how) |= [cof=cafe arc=arch] %+ cope |- ^- (bolt (map ,@ vase)) ?~ r.arc (fine cof ~) %+ cope $(r.arc l.r.arc) |= [cof=cafe lef=(map ,@ vase)] %+ cope `(bolt (map ,@ vase))`^$(cof cof, r.arc r.r.arc) |= [cof=cafe rig=(map ,@ vase)] %+ cope (chap(s.how [p.n.r.arc s.how]) cof bax hon) |= [cof=cafe nod=vase] (fine cof [[p.n.r.arc nod] lef rig]) |= [cof=cafe doy=(map ,@ vase)] %+ fine cof |- ^- vase ?~ doy [[%cube 0 [%atom %n]] 0] %+ slop (slop [[%atom %a] p.n.doy] q.n.doy) (slop $(doy l.doy) $(doy r.doy)) We get the arch at our current beam with `++lend`. Then, we process the horn at each of our children to give us a map of atoms to vases. Finally, we convert that into a vase of a map of these atoms to the values. This is very similar to `++chad` and the handling of `%man`. %see $(hon q.hon, how p.hon) This is `/:`. We process the given horn at the given beam. %saw %+ cope $(hon q.hon) |= [cof=cafe sam=vase] %+ cope (maim cof bax p.hon) |= [cof=cafe gat=vase] (maul cof gat sam) This is `/;`. First, we process the given horn. Then, we slap the given twig against our context to produce (hopefully) a gate. Finally, we slam the vase we got from processing the horn against the gate. %sic %+ cope $(hon q.hon) |= [cof=cafe vax=vase] %+ cope (maim cof bax [%bctr p.hon]) |= [cof=cafe tug=vase] ?. (~(nest ut p.tug) | p.vax) (flaw cof [%leaf "type error: {} {}"]~) (fine cof [p.tug q.vax]) This is `/^`. First, we process the given horn. Then, we slap the the bunt of the given tile against our context. This will produce a vase with the correct type. We test to see if this type nests within the type of the vase we got from processing the horn. If so, we produce the value from the horn along with the type from the tile. Otherwise, we produce a `%2` error bolt. %toy (cope (make cof %bake p.hon how ~) feel) == This is `/mark/`. Here, we simply run the `%bake` silk on the given mark, producing a cage. We convert this cage into a vase with `++feel`, which is exactly as simple as it sounds like it should be. ++ feel |=([a=cafe b=cage] (fine a q.b)) :: cage to vase This is trivial. We will discuss later `++make` and how `%bake` is processed. Suffice it to say that baking a resource with a given mark gets the resource and converts it, if necessary, to the requested mark. This concludes our discussion of `++chap`. We return once more to `++abut`. ++ abut :: generate |= [cof=cafe hyd=hood] ^- (bolt vase) %+ cope (apex cof hyd) |= [cof=cafe sel=_..abut] =. ..abut sel %+ cope (maim cof pit able) |= [cof=cafe bax=vase] %+ cope (chap cof bax [%fan fan.hyd]) |= [cof=cafe gox=vase] %+ cope (maim cof (slop gox bax) [%tssg (flop boy)]) |= [cof=cafe fin=vase] (fine cof fin) Recall that we processed our structures, libraries and body with `++apex`. We slapped our structures and libraries against zuse with `++maim`. We processed our resources with `++chap`. Now, all our body twigs are collected in a `=~` and slapped against our structures, libraries, and resources. This produces our final result. The hook file has been assembled. And there was great rejoicing. Lifecycle of a Kiss ------------------- We're now going to go through a series of lifecycle descriptions. When a user of ford sends a kiss, it is one of a dozen different types of silk. We'll go through each one, tracing through the flow of control of each of these. First, though, we'll describe the common handling to all kisses. The silk in a `%exec` kiss to ford ends up in `++apex`, so we'll enter the narrative here. ++ apex :: call |= kus=(unit silk) ^+ +> ?~ kus =+ nym=(~(get by dym.bay) hen) ?~ nym :: XX should never ~& [%ford-mystery hen] +>.$ =+ tas=(need (~(get by q.tad.bay) u.nym)) amok:~(camo zo [u.nym tas]) =+ num=p.tad.bay ?< (~(has by dym.bay) hen) =: p.tad.bay +(p.tad.bay) dym.bay (~(put by dym.bay) hen num) == ~(exec zo [num `task`[hen u.kus 0 ~]]) Recall that a `%exec` kiss actually sends a unit silk. If it's null, we're trying to cancel the request. We first look up the task number keyed by duct. If we don't find it, then we're trying to cancel a request that either was never started or has already completed. We print out `%ford-mystery` and do nothing. If we do find the task number, then we look up the task from it, call `++camo:zo` to cancel pending requests, and call `++amok:zo` to remove the task from our task lists. ++ camo :: stop requests ^+ . =+ kiz=(~(tap by q.kig) *(list ,[p=@ud q=beam])) |- ^+ +> ?~ kiz +> %= $ kiz t.kiz mow :_ mow :- hen :^ %pass [(scot %p our) (scot %ud num) (scot %ud p.i.kiz) ~] %c [%warp [our p.q.i.kiz] q.q.i.kiz ~] == Our list of blocks is in `q.kig`, so we iterate over it, cancelling our pending requests for each block. Our requests are all to clay, so we need only to send `%warp` kisses with a null instead of a rave. ++ amok %_ ..zo q.tad.bay (~(del by q.tad.bay) num) dym.bay (~(del by dym.bay) nah) == We remove the task number from the map of numbers to tasks and the duct from the map of ducts to task numbers. Back in `++apex`, if we were given a silk, we need to process it. We add the task to our maps, increment the next task number, and call `++exec:zo` on the new task. ++ exec :: execute app ^+ ..zo ?: !=(~ q.kig) ..zo |- ^+ ..zo =+ bot=(make [~ jav.bay] kas) =. ..exec (dash p.bot) ?- -.q.bot %0 amok:(expo [%made %& p.q.bot q.q.bot]) %2 amok:(expo [%made %| p.q.bot]) %1 =+ zuk=(~(tap by p.q.bot) ~) =< abet |- ^+ ..exec ?~ zuk ..exec =+ foo=`_..exec`(camp %x `beam`p.i.zuk) $(zuk t.zuk, ..exec foo) == If we're still blocked on something in `q.kig`, we don't do anything. Otherwise, we try to process the silk with `++make`. `++make` handles each individual request and will be the entire focus of the remainder of this doc after this section. It produces a bolt of a cage. We put the new cache in our state with `++dash`. ++ dash :: process cache |= cof=cafe ^+ +> %_(+> jav.bay q.cof) The cache is put in the baby so that it gets stored across calls to ford. In `++exec`, we process the bolt in three different ways according to the type of bolt produced. If we produced a `%0` value bolt, we use `++expo` to give the produced value and set of dependencies as a `%made` gift, and we remove ourselves from the task list with `++amok`. ++ expo :: return gift |= gef=gift %_(+> mow :_(mow [hen %give gef])) We simply push the gift onto our list of moves. In `++exec`, if we produced a `%2` error bolt, we produce a `%made` gift with the stack trace. If we produced a `%1` block bolt, we iterate through each of the blocks and call `++camp` to produce a clay request for the resource. ++ camp :: request a file |= [ren=care bem=beam] ^+ +> %= +> kig [+(p.kig) (~(put by q.kig) p.kig bem)] mow :_ mow :- hen :^ %pass [(scot %p our) (scot %ud num) (scot %ud p.kig) ~] %c [%warp [our p.bem] q.bem [~ %& %x r.bem s.bem]] == We put the resource in our block list in `q.kig` so that we save the fact that we're blocked. We then produce the `%warp` request to clay for the resource. Our request path has the format \`/[our-ship]/[task-number]/[block-number]'. We'll now describe how each of the individual silks are processed in `++make`. Lifecycle of a Cell ------------------- ^ %. [cof p.kas q.kas] ;~ cope ;~ coax |=([cof=cafe p=silk q=silk] ^$(cof cof, kas p.kas)) |=([cof=cafe p=silk q=silk] ^$(cof cof, kas q.kas)) == :: |= [cof=cafe bor=cage heg=cage] ^- (bolt cage) [p=cof q=[%0 ~ [%$ (slop q.bor q.heg)]]] == Silks autocons. The product of a cell of silks is a cell of the products of the silks, so we evaluate the two silks in parallel with `++coax` and slop together the results in a cell vase. We mark the product with `%$`, which means we know no more mark information than that it is a noun. ++ coax :: bolt across |* [hoc=(bolt) fun=(burg)] ?- -.q.hoc %0 =+ nuf=$:fun(..+<- p.hoc) :- p=p.nuf ^= q ?- -.q.nuf %0 [%0 p=(grom p.q.hoc p.q.nuf) q=[q.q.hoc q.q.nuf]] %1 q.nuf %2 q.nuf == %1 =+ nuf=$:fun(..+<- p.hoc) :- p=p.nuf ^= q ?- -.q.nuf %0 q.hoc %1 [%1 p=(grom p.q.nuf p.q.hoc)] %2 q.nuf == %2 hoc == If the first bolt is a value, we evaluate the burg to get the next bolt. If that also produces a value, we merge the dependency sets and produce a cell of the two values. Otherwise, we produce the block or error of the second bolt. If the first bolt is a block, we evaluate the burg to get the next bolt. If that produces a value, we just produce the block. If it produces a block, we merge the two block sets. If it produces an error, we produce that error. If the first bolt is already an error, we just pass that through. Note that `++coax` (and, indeed, `++cope`) is reasonable to use with `;~`. Lifecycle of a `%bake` ---------------------- %bake %+ cool |.(leaf/"ford: bake {} {<(tope q.kas)>}") %+ cope (lima cof p.kas q.kas r.kas) |= [cof=cafe vux=(unit vase)] ?~ vux (flaw cof (smyt (tope q.kas)) ~) (fine cof [p.kas u.vux]) This is one of the most critical silks. We are going to functionally produce the hook file at the given beam with the given heel. The result will be of the correct mark, even if we need to run conversion functions. The functionality is encapsulated in `++lime`. If it produces null, then we produce an error. Otherwise, we take the vase produced and give it the correct mark. ++ lima :: load at depth |= [cof=cafe for=mark bem=beam arg=heel] ^- (bolt (unit vase)) %+ cope (lend cof bem) |= [cof=cafe arc=arch] ^- (bolt (unit vase)) ?: (~(has by r.arc) for) (lace cof for bem(s [for s.bem]) arg) =+ haz=(turn (~(tap by r.arc) ~) |=([a=@tas b=~] a)) ?~ haz (fine cof ~) %+ cope (lion cof for -.bem haz) |= [cof=cafe wuy=(unit (list ,@tas))] ?~ wuy (fine cof ~) ?> ?=(^ u.wuy) %+ cope (make cof %bake i.u.wuy bem arg) |= [cof=cafe hoc=cage] %+ cope (lope cof i.u.wuy t.u.wuy -.bem q.hoc) |= [cof=cafe vax=vase] (fine cof ~ vax) First, we load the arch at the given beam with `++lend`. If we have a child named the mark, our job is straightforward, so we go ahead and load that with `++lace`. Otherwise, we iterate through our children. If we have no children, we produce null, signifying that we didn't find any way to convert to the requested mark. Otherwise, we call `++lion` to find a translation path from one of the available marks into the target mark. We recursively bake the child that has a path to the target mark, and then we call `++lope` to translate this mark into the target mark. We'll first discuss the direct case of when one of our children is of the correct mark. ++ lace :: load and check |= [cof=cafe for=mark bem=beam arg=heel] ^- (bolt (unit vase)) =+ bek=`beak`[p.bem q.bem r.bem] %+ cope (lend cof bem) |= [cof=cafe arc=arch] ?^ q.arc (cope (cope (liar cof bem) (lake for bek)) fest) ?: (~(has by r.arc) %hook) %+ cope (fade cof %hook bem) |= [cof=cafe hyd=hood] (cope (cope (abut:(meow bem arg) cof hyd) (lake for bek)) fest) (fine cof ~) First, we get the arch at the given beam with `++lend`. If this is a file, we load the file with `++liar` and coerce the type with `++lake`. Otherwise, we check to see if we have a hook file here. If so, we parse it with `++fade`, compile it with `++abut:meow`, and coerce the type with `++lake`. Otherwise, there is no way to translate this, so we produce null. `++fest` is one line, so we'll get that one out of the way first. ++ fest |*([a=cafe b=*] (fine a [~ u=b])) :: bolt to unit This is just `++some` for bolts. We've delayed the discussion of `++fade` far too many times. It's not complicated, we just wanted to spare a premature discussion of `++make` and the `%bake` silk. We 're now able to discuss everything in `++fade` with ease. ++ fade :: compile to hood |= [cof=cafe for=mark bem=beam] ^- (bolt hood) %+ cool |.(leaf/"ford: fade {<[(tope bem)]>}") %+ cope (make cof [%bake for bem ~]) |= [cof=cafe cay=cage] %+ (clef %hood) (fine cof bem cay) ^- (burg (pair beam cage) hood) |= [cof=cafe bum=beam cay=cage] =+ rul=(fair bem) ?. ?=(@ q.q.cay) (flaw cof ~) =+ vex=((full rul) [[1 1] (trip q.q.cay)]) ?~ q.vex (flaw cof [%leaf "syntax error: {} {}"] ~) (fine cof p.u.q.vex) We first push a line onto a stack trace to say that we're parsing into a hood file. We bake the given beam with the given mark and no heel. Recall that baking gate, core, door, hoon, and hook files produces simply an atom of the text. We check to make sure that our value is an atom, failing otherwise. The parsing step is run within `++clef` so that the result is cached. We call `++fair` with the current beam to generate the parsing rule, and we parse the file. If parsing fails, we fail giving a syntax error with the line and column number. Otherwise, we produce the value. ++ liar :: load vase |= [cof=cafe bem=beam] ^- (bolt vase) =+ von=(ska %cx (tope bem)) ?~ von [p=*cafe q=[%1 [[bem ~] ~ ~]]] ?~ u.von (flaw cof (smyt (tope bem)) ~) (fine cof ?^(u.u.von [%cell %noun %noun] [%atom %$]) u.u.von) This takes a beam and loads the file at that location. If our sky function produces null, that means the resource is currently unavailable, so we block on it. If it produces `[~ ~]`, that means our resource is permanently unavailable, so we produce an error. Otherwise, we produce the value there with a type of either a cell of two nouns or an atom, depending on whether the value is a cell or not. Back in `++lima`, recall that we call `++lion` to find a translation path. ++ lion :: translation search |= [cof=cafe too=@tas bek=beak fro=(list ,@tas)] ^- (bolt (unit (list ,@tas))) =| war=(set ,@tas) =< -:(apex (fine cof fro)) |% ++ apex |= rof=(bolt (list ,@tas)) ^- [(bolt (unit (list ,@tas))) _+>] ?. ?=(%0 -.q.rof) [rof +>.$] ?~ q.q.rof [[p.rof [%0 p.q.rof ~]] +>.$] =^ orf +>.$ (apse cof i.q.q.rof) ?. ?=(%0 -.q.orf) [orf +>.$] ?~ q.q.orf $(cof p.orf, q.q.rof t.q.q.rof) [[p.orf [%0 (grom p.q.rof p.q.orf) q.q.orf]] +>.$] :: ++ apse |= [cof=cafe for=@tas] ^- [(bolt (unit (list ,@tas))) _+>] ?: =(for too) [(fine cof [~ too ~]) +>.$] ?: (~(has in war) for) [(fine cof ~) +>] =. war (~(put in war) for) =^ hoc +>.$ (apex (lily cof for bek)) :_ +>.$ %+ cope hoc |= [cof=cafe ked=(unit (list ,@tas))] (fine cof ?~(ked ~ [~ for u.ked])) -- At a high level, we have `++apex` and `++apse`. `++apex` takes a list of marks to try in succession until we find one that can be translated into the target mark. On each one, it calls `++apse`, which takes a single mark and tries to find a translation path from this mark to the target. To do this, it sees which marks we know how to directly translate to, and calls `++apex` on this list. The result of this mututal recursion is a depth-first search of the translation graph to find the target mark. Since the translation graph is not necessarily acyclic, we maintain a set of marks that we've already tried. We kick off our search in `++apex`, starting with the given initial list of marks that we know how to get to. If `++apex` is called with a bolt other than a `%0` value bolt, we simply produce it. Otherwise, we check to see if the list of available marks to investigate is null. If so, then we're done, so we produce a `%0` bolt with a null list of accessible marks. Otherwise, we process this next mark with `++apse`, which will produce a possible list of marks from this one to the target mark. If it fails to produce a `%0` bolt, we just produce that. Otherwise, if it produces null, we can't get to our target through this mark, so we move on to the next one. If it doesn't produce null, then we have successfully found a translation path, so we produce it. In `++apse`, we first test to see if we've arrived at the target path. If so, we're done, so we produce a list including just ourself. Otherwise, we check to see if we've already tried this mark. If so, we know we can't succeed here, so we produce null. Otherwise, we put ourselves in the set of already-tried marks, and we move on. We call `++lily` to get the list of marks we can translate this one into. ++ lily :: translation targets |= [cof=cafe for=mark bek=beak] ^- (bolt (list ,@tas)) =+ raf=(fang cof for bek) ?: =(%2 -.q.raf) (fine p.raf ~) %+ cope raf |= [cof=cafe vax=vase] %+ fine cof %+ weld ^- (list ,@tas) ?. (slab %garb p.vax) ~ =+ gav=((soft (list ,@tas)) q:(slap vax [%cnzy %garb])) ?~(gav ~ u.gav) ?. (slab %grow p.vax) ~ =+ gow=(slap vax [%cnzy %grow]) (sloe p.gow) We call `++fang` to get the mark definition door. This is documented under `%vale`. If getting the mark fails, we produce null because we can't translate a non-existent mark into anything. Otherwise, we examine the door. The door may have a `++garb`, which is simply a list of marks which know how to translate from the current one. There must be a corresponding `++grab` in the definition of the other mark, though we don't check that here. The door may also have a `++grow`, which defines how to translate this mark into another one. Each arm in `++grow` is the name of a mark we can translate into. The call to `++sloe` simply produces a list of arm names in `++grow`. Back in `++apse:lion`, we take the list of translation targets we just found and call `++apex` on it. If we got back a null, we produce a null; otherwise, we produce the list of marks we got back plus the current mark. This concludes our discussion of `++lion`. The final piece of `++lima` is `++lope`, which performs the actual translation along the path we just computed. ++ lope :: translation pipe |= [cof=cafe for=mark yaw=(list mark) bek=beak vax=vase] ^- (bolt vase) ?~ yaw (fine cof vax) %+ cope (link cof i.yaw for bek vax) |= [cof=cafe yed=vase] ^$(cof cof, for i.yaw, yaw t.yaw, vax yed) We iterate through our list, calling `++link` on every adjacent pair of marks, translating from one mark to the next until we finish the list of marks. A call to `++link` is equivalent to a `%cast` silk, so we document it there. After we've called performed every step in the translation pipeline, we're done. Lifecycle of a `%boil` ---------------------- %boil %+ cool |.(leaf/"ford: boil {} {<(tope q.kas)>} {}") %+ cope (lamp cof q.kas) |= [cof=cafe bem=beam] %+ cope (lime cof p.kas bem r.kas) |= [cof=cafe vax=vase] (fine cof `cage`[p.kas vax]) At a high level, we try to bake at the given beam, and if it fails, we go up a level and try again. This is the usual semantics of ford, and this should nearly always be preferred over directly baking. First, we normalize the version case to a number with `++lamp`. This allows caching to be based on revision number rather than something more ephemeral like a particular time. ++ lamp :: normalize version |= [cof=cafe bem=beam] ^- (bolt beam) =+ von=(ska %cw (tope bem(s ~))) ?~ von [p=cof q=[%1 [bem ~] ~ ~]] (fine cof bem(r [%ud ((hard ,@) (need u.von))])) We call the sky function with `%cw`, asking clay for the revision number at this case. If the case refers to a revision that isn't there yet, we produce a `%1` blocking bolt. Otherwise, we require that the value exist and that it's a number, both of which are guaranteed by clay. We produce this number. Next for `%boil` we call `++lime` to try to load the beam. ++ lime :: load beam |= [cof=cafe for=mark bem=beam arg=heel] =+ [mob=bem mer=(flop arg)] |- ^- (bolt vase) %+ cope (lima cof for mob (flop mer)) |= [cof=cafe vux=(unit vase)] ?^ vux (fine cof u.vux) ?~ s.mob (flaw cof (smyt (tope bem)) ~) ^$(s.mob t.s.mob, mer [i.s.mob mer]) We start at the given beam and try to bake it. If it succeeds, we're good. Otherwise, we pop off the top level of the path and put it in our heel (virtual path extension). We do this recursively until either we find something we can bake or we've gone all the way up to the root path of the desk, in which case we fail. Lifecycle of a `%call` ---------------------- %call %+ cool |.(leaf/"ford: call {<`@p`(mug kas)>}") %. [cof p.kas q.kas] ;~ cope ;~ coax |=([cof=cafe p=silk q=silk] ^$(cof cof, kas p)) |=([cof=cafe p=silk q=silk] ^$(cof cof, kas q)) == :: |= [cof=cafe gat=cage sam=cage] (maul cof q.gat q.sam) :: |= [cof=cafe vax=vase] (fine cof %noun vax) == This is slam for silks. We process both of the given silks in parallel with `++coax`. We then slam the two produced vases together with `++maul` and mark the produced vase with `%noun` since we don't know any more specific mark. `++coax` is documented under Lifecycle of a Cell. Lifecycle of a `%cast` ---------------------- %cast %+ cool |.(leaf/"ford: cast {}") %+ cope $(kas q.kas) |= [cof=cafe cay=cage] %+ cope (link cof p.kas p.cay [our %main %da now] q.cay) |= [cof=cafe vax=vase] (fine cof [p.kas vax]) This is a request to convert data of one mark to another mark directly. We evaluate the given silk and pass the result into `++link`, which performs the actual translation. Note that this will not search for indirect conversion paths, so the conversion must be defined either in the `++grow` of the given mark or the `++grab` of the target mark. ++ link :: translate |= [cof=cafe too=mark for=mark bek=beak vax=vase] ^- (bolt vase) ?: =(too for) (fine cof vax) ?: |(=(%noun for) =(%$ for)) ((lake too bek) cof vax) %+ cope (fang cof for bek) |= [cof=cafe pro=vase] ?: &((slab %grow p.pro) (slab too p:(slap pro [%cnzy %grow]))) %+ cope (keel cof pro [[%& 6]~ vax]~) |= [cof=cafe pox=vase] (maim cof pox [%tsgr [%cnzy %grow] [%cnzy too]]) %+ cope (fang cof too bek) |= [cof=cafe pro=vase] =+ ^= zat ^- (unit vase) ?. (slab %grab p.pro) ~ =+ gab=(slap pro [%cnzy %grab]) ?. (slab for p.gab) ~ `(slap gab [%cnzy for]) ?~ zat (flaw cof [%leaf "ford: no link: {<[for too]>}"]~) (maul cof u.zat vax) This performs one step in the translation pipeline. If the given and target marks are the same, we're done. If we're translating from a noun or the empty mark, we coerce with `++lake` (documented in `%vale`). Otherwise, we're translating from a user-defined mark. We load the definition of the given mark with `++fang`, and we check to see if it has an arm in `++grow` named the target mark. If so, we place our data in the sample of the door with `++keel` and slap the arm. `++keel` is equivalent to a `%mute` silk, so we document it there. If there is no arm in `++grow` of the given mark named the target mark, we suppose there must be an arm in `++grab` of the target mark named the given mark. We get the definition of the target mark and check to see if it has the required arm, failing if it doesn't. Finally, we slam the data against the correct arm, producing the translated data. If you're confused as to why the handling of `++grow` and `++grab` look superficially so different, remember that the correct arm in `++grow` does not have a sample while the one in `++grab` does. This means they must be called rather differently. Lifecycle of a `%diff` ---------------------- %diff %+ cool |.(leaf/"ford: diff {<`@p`(mug p.kas)>} {<`@p`(mug q.kas)>}") (diff cof p.kas q.kas) We push debug information onto the trace and go right to `++diff`. ++ diff |= [cof=cafe kas=silk kos=silk] ^- (bolt cage) %. [cof kas kos] ;~ cope ;~ coax |=([cof=cafe p=silk q=silk] (make cof p)) |=([cof=cafe p=silk q=silk] (make cof q)) == |= [cof=cafe cay=cage coy=cage] First, we process the two given silks to get our arguments. ?. =(p.cay p.coy) %+ flaw cof :_ ~ leaf/"diff on data of different marks: {(trip p.cay)} {(trip p.coy)}" If the two cages have different marks, then we can't diff them, so we complain. %+ cope (fang cof p.cay [our %main %da now]) |= [cof=cafe pro=vase] We pull in the relevant mark's definition. ?. (slab %grad p.pro) (flaw cof leaf/"no ++grad" ~) =+ gar=(slap pro [%cnzy %grad]) ?. (slab %form p.gar) ?. (slab %sted p.gar) (flaw cof leaf/"no ++form:grad nor ++sted:grad" ~) =+ for=((soft ,@tas) q:(slap gar [%cnzy %sted])) ?~ for (flaw cof leaf/"bad ++sted:grad" ~) (make cof %diff [%cast u.for kas] [%cast u.for kos]) If there's no `++grad`, we complain. If there's no `++form:grad`, then we look for a `++sted:grad`. If we can't find either, or if `++sted:grad` isn't a term, then we complain. If `++sted:grad` exists and is a term, then it represents the mark we should use as a proxy to get our diff. So, we cast both our given cages to the new mark and start the dance again. ?. (slab %diff p.gar) (flaw cof leaf/"no ++diff:grad" ~) Otherwise, we expect a `++diff:grad`. %+ cope (keel cof pro [[%& 6]~ q.cay]~) |= [cof=cafe pox=vase] We put the first cage's data into the sample of the given mark's definition. %+ cope %^ maul cof (slap (slap pox [%cnzy %grad]) [%cnzy %diff]) q.coy |= [cof=cafe dif=vase] We run `++diff:grad` with a sample of the second cage's data. =+ for=((soft ,@tas) q:(slap gar [%cnzy %form])) ?~ for (flaw cof leaf/"bad ++form:grad" ~) (fine cof u.for dif) == We check that `++form:grad` exists, and we tag the result with it to give the final cage. Lifecycle of a `%done` ---------------------- %done [cof %0 p.kas q.kas] This is trivial. We simply produce the given cage with the given set of dependencies. This is used when we already have a cage that we want to insert into another silk that requires a silk argument. It's analogous to the return operator in a monad -- which makes it sound way more complicated than it is. Lifecycle of a `%dude` ---------------------- %dude (cool |.(p.kas) $(kas q.kas)) This simply puts a given tank on the stack trace if the given silk produces an error. This is implemented as a simple call to `++cool`. Lifecycle of a `%dune` ---------------------- %dune ?~ q.kas [cof [%2 [%leaf "no data"]~]] $(kas [%done p.kas u.q.kas]) This is a sort of a `++need` for silks. If there is no data in the unit cage, we produce an error. Else, we simply produce the data in the cage. Lifcycle of a `%mute` --------------------- %mute (kale cof p.kas q.kas) This mutates a silk by putting the values of other silks at particular axes. This is useful in, for example, replacing the sample of the door in a mark definition. ++ kale :: mutate |= [cof=cafe kas=silk muy=(list (pair wing silk))] ^- (bolt cage) %+ cope |- ^- (bolt (list (pair wing vase))) ?~ muy (fine cof ~) %+ cope (make cof q.i.muy) |= [cof=cafe cay=cage] %+ cope ^$(muy t.muy) |= [cof=cafe rex=(list (pair wing vase))] (fine cof [[p.i.muy q.cay] rex]) |= [cof=cafe yom=(list (pair wing vase))] %+ cope (make cof kas) |= [cof=cafe cay=cage] %+ cope (keel cof q.cay yom) |= [cof=cafe vax=vase] (fine cof p.cay vax) First, we process each of the silks by calling `++make` on them. We pass the resultant vase and list of pairs of wings and silks to `++keel` to do the actual mutation. We assume the mutation doesn't change the mark of the main silk, so we mark the produced vase with the original mark. ++ keel :: apply mutations |= [cof=cafe suh=vase yom=(list (pair wing vase))] ^- (bolt vase) %^ maim cof %+ slop suh |- ^- vase ?~ yom [[%atom %n] ~] (slop q.i.yom $(yom t.yom)) ^- twig :+ %cncb [%& 2]~ =+ axe=3 |- ^- (list (pair wing twig)) ?~ yom ~ :- [p.i.yom [%$ (peg axe 2)]] $(yom t.yom, axe (peg axe 3)) We first put the vases together in one big tuple starting with the subject and going through the mutations. We slap against this tuple a `%_` twig we directly construct. Since a `%_` twig takes a list of pairs of wings and twigs, we simply have to generate twigs referring to the correct axes in the subject. This is very easy since we just recur on axis 3 of whatever axis we were already at. Note the use of `%_` instead of `%=` enforces that our mutations don't change the type of the subject, which justifies our use of the original mark. Lifecycle of a `%pact` ---------------------- %pact %+ cool |.(leaf/"ford: pact {<`@p`(mug p.kas)>} {<`@p`(mug q.kas)>}") (pact cof p.kas q.kas) We push debug information onto the trace and go right to `++pact`. ++ pact :: patch |= [cof=cafe kas=silk kos=silk] ^- (bolt cage) %. [cof kas kos] ;~ cope ;~ coax |=([cof=cafe p=silk q=silk] (make cof p)) |=([cof=cafe p=silk q=silk] (make cof q)) == |= [cof=cafe cay=cage coy=cage] First, we process the two given silks to get our arguments. %+ cope (fang cof p.cay [our %main %da now]) |= [cof=cafe pro=vase] We pull in the relevant mark's definition. ?. (slab %grad p.pro) (flaw cof leaf/"no ++grad" ~) =+ gar=(slap pro [%cnzy %grad]) ?. (slab %form p.gar) ?. (slab %sted p.gar) (flaw cof leaf/"no ++form:grad nor ++sted:grad" ~) =+ for=((soft ,@tas) q:(slap gar [%cnzy %sted])) ?~ for (flaw cof leaf/"bad ++sted:grad" ~) (make cof %cast p.cay %pact [%cast u.for kas] kos) If there's no `++grad`, we complain. If there's no `++form:grad`, then we look for a `++sted:grad`. If we can't find either, or if `++sted:grad` isn't a term, then we complain. If `++sted:grad` exists and is a term, then it represents the mark we should use as a proxy to get our diff. So, we cast the first argument to the new mark, then try to patch. Afterward, we cast the result back to the original mark. =+ for=((soft ,@tas) q:(slap gar [%cnzy %form])) ?~ for (flaw cof leaf/"bad ++form:grad" ~) ?. =(u.for p.coy) %+ flaw cof :_ ~ =< leaf/"pact on data with wrong form: {-} {+<} {+>}" [(trip p.cay) (trip u.for) (trip p.coy)] If `++form:grad` isn't a term, or else our second argument isn't of that mark, we complain. ?. (slab %pact p.gar) (flaw cof leaf/"no ++pact:grad" ~) If we don't have a `++pact:grad`, we complain. %+ cope (keel cof pro [[%& 6]~ q.cay]~) |= [cof=cafe pox=vase] We put the first cage's data into the sample of the given mark's definition. %+ cope %^ maul cof (slap (slap pox [%cnzy %grad]) [%cnzy %pact]) q.coy |= [cof=cafe pat=vase] We run `++pact:grad` with a sample of the second cage's data, which is the diff. (fine cof p.cay pat) == We tag the result with the mark of our first argument. Lifecycle of a `%plan` ---------------------- %plan %+ cope (abut:(meow p.kas q.kas) cof r.kas) |= [cof=cafe vax=vase] (fine cof %noun vax) This is a direct request to compile a hood at a given beam with a heel of the given path. We comply by calling `++abut` with the given arguments and producing the vase with a mark of `%noun`. Lifecycle of a `%reef` ---------------------- %reef (fine cof %noun pit) This is one of the simplest silks. We simply produce our context, which is zuse compiled against hoon. The mark is a `%noun`. Lifcycle of a `%ride` --------------------- %ride %+ cool |.(leaf/"ford: ride {<`@p`(mug kas)>}") %+ cope $(kas q.kas) |= [cof=cafe cay=cage] %+ cope (maim cof q.cay p.kas) |= [cof=cafe vax=vase] (fine cof %noun vax) This slaps evaluates the given silk, then it slaps the result against the given twig. Since we don't know what of what mark (if any) is the result, we give it a mark of `%noun`. Lifecycle of a `%vale` ---------------------- %vale %+ cool |.(leaf/"ford: vale {} {} {<`@p`(mug r.kas)>}") %+ cope (lave cof p.kas q.kas r.kas) |= [cof=cafe vax=vase] (fine cof `cage`[p.kas vax]) This checks whether given data is of the given mark. If we don't have the definition of the mark, we check the given ship for it. We call `++lave` to perform the check, producing a vase. We produce this vase tagged with the given mark. ++ lave :: validate |= [cof=cafe for=mark his=ship som=*] ^- (bolt vase) ((lake for [our %main [%da now]]) cof [%noun som]) This is a thinly-veiled wrapper over `++lake`. Note that, contrary to documented opinion, we do not in fact check the other ship's definition of a mark. This is likely a bug. At any rate, `++lake` coerces a noun into the correct type for a mark. ++ lake :: check/coerce |= [for=mark bek=beak] |= [cof=cafe sam=vase] ^- (bolt vase) %+ cool |.(leaf/"ford: check {<[for bek `@p`(mug q.sam)]>}") ?: ?=(?(%gate %core %door %hoon %hook) for) :: ~& [%lake-easy for bek] (fine cof sam) %+ cope (fang cof for bek) |= [cof=cafe tux=vase] =+ bob=(slot 6 tux) ?: (~(nest ut p.bob) | p.sam) (fine cof sam) ?. (slab %grab p.tux) (flaw cof [%leaf "ford: no grab: {<[for bek]>}"]~) =+ gab=(slap tux [%cnzy %grab]) ?. (slab %noun p.gab) (flaw cof [%leaf "ford: no noun: {<[for bek]>}"]~) %+ cope (maul cof (slap gab [%cnzy %noun]) [%noun q.sam]) |= [cof=cafe pro=vase] ?: =(+<.q.pro q.sam) (fine cof (slot 6 pro)) (flaw cof [%leaf "ford: invalid content: {<[for bek]>}"]~) This is going to coerce the sample into the correct type for the mark. First, we push a line onto the stack trace saying that we're checking the type. If the requested mark is a gate, core, door, hoon, or hook, then we don't do any more type information than just saying it's a noun, so we're done. Otherwise, we get the mark definition from our `/=main=/mar` directory with `++fang`, which we'll describe below. We check to see if our sample type nests within the type of the sample to the door. If so, then we're already of the correct type, so we're done. Otherwise, we check to see if there's a `++grab` in the door, and a `++noun` in the `++grab`. If not, there's no way we can translate to this mark, so we fail. If we have everything we need, we slam our sample (typed as a noun) against the `++noun` in `++grab`. If the sample of the door is the same as our sample, then the check succeeded, so we produce the well-typed sample of the door. Otherwise, we fail. ++ fang :: protocol door |= [cof=cafe for=mark bek=beak] ^- (bolt vase) =+ pax=/door/[for]/mar =+ ^= bem ^- beam :_ pax ?: =(p.bek our) bek =+ oak=[our %main %da now] ?. =(~ (ska %cy (tope [oak pax]))) oak bek (cope (fade cof %hook bem) abut:(meow bem ~)) A mark's definition is generally in `/=main=/mar/[mark-name]/door/hook'. If we don't find it there, we look in`/[given-beak]/mar/[mark-name]/door/hook'. We parse the mark definition with `++fade` and assemble it with `++abut:meow`. `++fade` is defined under the `%bake` silk.