Urbit Docs
  • What is Urbit?
  • Get on Urbit
  • Build on Urbit
    • Contents
    • Environment Setup
    • Hoon School
      • 1. Hoon Syntax
      • 2. Azimuth (Urbit ID)
      • 3. Gates (Functions)
      • 4. Molds (Types)
      • 5. Cores
      • 6. Trees and Addressing
      • 7. Libraries
      • 8. Testing Code
      • 9. Text Processing I
      • 10. Cores and Doors
      • 11. Data Structures
      • 12. Type Checking
      • 13. Conditional Logic
      • 14. Subject-Oriented Programming
      • 15. Text Processing II
      • 16. Functional Programming
      • 17. Text Processing III
      • 18. Generic and Variant Cores
      • 19. Mathematics
    • App School I
      • 1. Arvo
      • 2. The Agent Core
      • 3. Imports and Aliases
      • 4. Lifecycle
      • 5. Cards
      • 6. Pokes
      • 7. Structures and Marks
      • 8. Subscriptions
      • 9. Vanes
      • 10. Scries
      • 11. Failure
      • 12. Next Steps
      • Appendix: Types
    • App School II (Full-Stack)
      • 1. Types
      • 2. Agent
      • 3. JSON
      • 4. Marks
      • 5. Eyre
      • 6. React app setup
      • 7. React app logic
      • 8. Desk and glob
      • 9. Summary
    • Core Academy
      • 1. Evaluating Nock
      • 2. Building Hoon
      • 3. The Core Stack
      • 4. Arvo I: The Main Sequence
      • 5. Arvo II: The Boot Sequence
      • 6. Vere I: u3 and the Serf
      • 7. Vere II: The Loom
      • 8. Vanes I: Behn, Dill, Kahn, Lick
      • 9. Vanes II: Ames
      • 10. Vanes III: Eyre, Iris
      • 11. Vanes IV: Clay
      • 12. Vanes V: Gall and Userspace
      • 13. Vanes VI: Khan, Lick
      • 14. Vanes VII: Jael, Azimuth
    • Runtime
      • U3
      • Conn.c Guide
      • How to Write a Jet
      • API Overview by Prefix
      • C in Urbit
      • Cryptography
      • Land of Nouns
    • Tools
      • Useful Links
      • JS Libraries
        • HTTP API
      • Docs App
        • File Format
        • Index File
        • Suggested Structure
    • Userspace
      • Command-Line App Tutorial
      • Remote Scry
      • Unit Tests
      • Software Distribution
        • Software Distribution Guide
        • Docket File
        • Glob
      • Examples
        • Building a CLI App
        • Debugging Wrapper
        • Host a Website
        • Serving a JS Game
        • Ship Monitoring
        • Styled Text
  • Urbit ID
    • What is Urbit ID?
    • Azimuth Data Flow
    • Life and Rift
    • Urbit HD Wallet
    • Advanced Azimuth Tools
    • Custom Roller Tutorial
    • Azimuth.eth Reference
    • Ecliptic.eth Reference
    • Layer 2
      • L2 Actions
      • L2 Rollers
      • L2 Roller HTTP RPC-API
      • L2 Transaction Format
  • Urbit OS
    • What is Urbit OS?
    • Base
      • Hood
      • Threads
        • Basics Tutorial
          • Bind
          • Fundamentals
          • Input
          • Output
          • Summary
        • HTTP API Guide
        • Spider API Reference
        • Strandio Reference
        • Examples
          • Child Thread
          • Fetch JSON
          • Gall
            • Poke Thread
            • Start Thread
            • Stop Thread
            • Take Facts
            • Take Result
          • Main-loop
          • Poke Agent
          • Scry
          • Take Fact
    • Kernel
      • Arvo
        • Cryptography
        • Move Trace
        • Scries
        • Subscriptions
      • Ames
        • Ames API Reference
        • Ames Cryptography
        • Ames Data Types
        • Ames Scry Reference
      • Behn
        • Behn API Reference
        • Behn Examples
        • Behn Scry Reference
      • Clay
        • Clay API Reference
        • Clay Architecture
        • Clay Data Types
        • Clay Examples
        • Clay Scry Reference
        • Filesystem Hierarchy
        • Marks
          • Mark Examples
          • Using Marks
          • Writing Marks
        • Using Clay
      • Dill
        • Dill API Reference
        • Dill Data Types
        • Dill Scry Reference
      • Eyre
        • EAuth
        • Eyre Data Types
        • Eyre External API
        • Eyre Internal API
        • Eyre Scry Reference
        • Low-Level Eyre Guide
        • Noun channels
      • Gall
        • Gall API Reference
        • Gall Data Types
        • Gall Scry Reference
      • Iris
        • Iris API Reference
        • Iris Data Types
        • Iris Example
      • Jael
        • Jael API Reference
        • Jael Data Types
        • Jael Examples
        • Jael Scry Reference
      • Khan
        • Khan API Reference
        • Khan Data Types
        • Khan Example
      • Lick
        • Lick API Reference
        • Lick Guide
        • Lick Examples
        • Lick Scry Reference
  • Hoon
    • Why Hoon?
    • Advanced Types
    • Arvo
    • Auras
    • Basic Types
    • Cheat Sheet
    • Cryptography
    • Examples
      • ABC Blocks
      • Competitive Programming
      • Emirp
      • Gleichniszahlenreihe
      • Islands
      • Luhn Number
      • Minimum Path Sum
      • Phone Letters
      • Restore IP
      • Rhonda Numbers
      • Roman Numerals
      • Solitaire Cipher
      • Water Towers
    • Generators
    • Hoon Errors
    • Hoon Style Guide
    • Implementing an Aura
    • Irregular forms
    • JSON
    • Limbs and wings
      • Limbs
      • Wings
    • Mips (Maps of Maps)
    • Parsing Text
    • Runes
      • | bar · Cores
      • $ buc · Structures
      • % cen · Calls
      • : col · Cells
      • . dot · Nock
      • / fas · Imports
      • ^ ket · Casts
      • + lus · Arms
      • ; mic · Make
      • ~ sig · Hints
      • = tis · Subject
      • ? wut · Conditionals
      • ! zap · Wild
      • Constants (Atoms and Strings)
      • --, == · Terminators
    • Sail (HTML)
    • Serialization
    • Sets
    • Standard Library
      • 1a: Basic Arithmetic
      • 1b: Tree Addressing
      • 1c: Molds and Mold-Builders
      • 2a: Unit Logic
      • 2b: List Logic
      • 2c: Bit Arithmetic
      • 2d: Bit Logic
      • 2e: Insecure Hashing
      • 2f: Noun Ordering
      • 2g: Unsigned Powers
      • 2h: Set Logic
      • 2i: Map Logic
      • 2j: Jar and Jug Logic
      • 2k: Queue Logic
      • 2l: Container from Container
      • 2m: Container from Noun
      • 2n: Functional Hacks
      • 2o: Normalizing Containers
      • 2p: Serialization
      • 2q: Molds and Mold-Builders
      • 3a: Modular and Signed Ints
      • 3b: Floating Point
      • 3c: Urbit Time
      • 3d: SHA Hash Family
      • 3e: AES encryption (Removed)
      • 3f: Scrambling
      • 3g: Molds and Mold-Builders
      • 4a: Exotic Bases
      • 4b: Text Processing
      • 4c: Tank Printer
      • 4d: Parsing (Tracing)
      • 4e: Parsing (Combinators)
      • 4f: Parsing (Rule-Builders)
      • 4g: Parsing (Outside Caller)
      • 4h: Parsing (ASCII Glyphs)
      • 4i: Parsing (Useful Idioms)
      • 4j: Parsing (Bases and Base Digits)
      • 4k: Atom Printing
      • 4l: Atom Parsing
      • 4m: Formatting Functions
      • 4n: Virtualization
      • 4o: Molds
      • 5a: Compiler Utilities
      • 5b: Macro Expansion
      • 5c: Compiler Backend & Prettyprinter
      • 5d: Parser
      • 5e: Molds and mold builders
      • 5f: Profiling support
    • Strings
    • The Engine Pattern
    • Udon (Markdown-esque)
    • Vases
    • Zuse
      • 2d(1-5): To JSON, Wains
      • 2d(6): From JSON
      • 2d(7): From JSON (unit)
      • 2e(2-3): Print & Parse JSON
      • 2m: Ordered Maps
  • Nock
    • What is Nock?
    • Decrement
    • Definition
    • Fast Hints and Jets
    • Implementations
    • Specification
  • User Manual
    • Contents
    • Running Urbit
      • Cloud Hosting
      • Home Servers
      • Runtime Reference
      • Self-hosting S3 Storage with MinIO
    • Urbit ID
      • Bridge Troubleshooting
      • Creating an Invite Pool
      • Get an Urbit ID
      • Guide to Factory Resets
      • HD Wallet (Master Ticket)
      • Layer 2 for planets
      • Layer 2 for stars
      • Proxies
      • Using Bridge
    • Urbit OS
      • Basics
      • Configuring S3 Storage
      • Dojo Tools
      • Filesystem
      • Shell
      • Ship Troubleshooting
      • Star and Galaxy Operations
      • Updates
Powered by GitBook

GitHub

  • Urbit ID
  • Urbit OS
  • Runtime

Resources

  • YouTube
  • Whitepaper
  • Awesome Urbit

Contact

  • X
  • Email
  • Gather
On this page
  • Useful libraries
  • /lib/default-agent.hoon
  • /lib/dbug.hoon
  • Virtual arms
  • Additional cores
  • Example
  • Summary
  • Exercises
Edit on GitHub
  1. Build on Urbit
  2. App School I

3. Imports and Aliases

In the last lesson we looked at the most basic aspects of a Gall agent's structure. Before we get into the different agent arms in detail, there's some boilerplate to cover that makes life easier when writing Gall agents.

Useful libraries

There are a couple of libraries that you'll very likely use in every agent you write. These are /lib/default-agent.hoon and /lib/dbug.hoon. In brief, /lib/default-agent.hoon provides simple default behaviours for each agent arm, and /lib/dbug.hoon lets you inspect the state and bowl of an agent from the dojo, for debugging purposes. Every example agent we look at from here on out will make use of both libraries.

Let's look at each in more detail:

/lib/default-agent.hoon

The default-agent library contains a basic agent with sane default behaviours for each arm. In some cases it just crashes and prints an error message to the terminal, and in others it succeeds but does nothing. It has two primary uses:

  • For any agent arms you don't need, you can just have them call the matching function in /lib/default-agent.hoon, rather than having to manually handle events on those arms.

  • A common pattern in an agent is to switch on the input of an arm with wutlus (?+) runes or maybe wutcol (?:) runes. For any unexpected input, you can just pass it to the relevant arm of default-agent rather than handling it manually.

The default-agent library lives in /lib/default-agent/hoon of the %base desk, and you would typically include a copy in any new desk you created. It's imported at the beginning of an agent with the faslus (/+) rune.

The library is a wet gate which takes two arguments: agent and help. The first is your agent core itself, and the second is a ?. If help is %.y (equivalently, %&), it will crash in all cases. If help is %.n (equivalently, %|), it will use its defaults. You would almost always have help as %.n.

The wet gate returns an +agent:gall door with a sample of $bowl:gall - a typical agent core. Usually you would define an alias for it in a virtual arm (explained below) so it's simple to call.

/lib/dbug.hoon

The dbug library lets you inspect the state and $bowl of your agent from the dojo. It includes an +agent:dbug function which wraps your whole +agent:gall door, adding its extra debugging functionality while transparently passing events to your agent for handling like usual.

To use it, you just import /lib/dbug.hoon with a faslus (/+) rune at the beginning, then add the following line directly before the door of your agent:

%-  agent:dbug

With that done, you can poke your agent with the +dbug generator from the Dojo and it will pretty-print its state, like:

> :your-agent +dbug

The generator also has a few useful optional arguments:

  • %bowl: Print the agent's bowl.

  • [%state 'some code']: Evaluate some code with the agent's state as its subject and print the result. The most common case is [%state %some-face], which will print the contents of the wing with the given face.

  • [%incoming ...]: Print details of the matching incoming subscription, one of:

    • [%incoming %ship ~some-ship]

    • [%incoming %path /part/of/path]

    • [%incoming %wire /part/of/wire]

  • [%outgoing ...]: Print details of the matching outgoing subscription, one of:

    • [%outgoing %ship ~some-ship]

    • [%outgoing %path /part/of/path]

    • [%outgoing %wire /part/of/wire]

    • [outgoing %term %agent-name]

By default it will retrieve your agent's state by using its +on-save arm, but if your app implements a scry endpoint with a path of /x/dbug/state, it will use that instead.

We haven't yet covered some of the concepts described here, so don't worry if you don't fully understand /lib/dbug.hoon's functionality - you can refer back here later.

Virtual arms

An agent core must have exactly ten arms. However, there's a special kind of "virtual arm" that can be added without actually increasing the core's arm count, since it really just adds code to the other arms in the core. A virtual arm is created with the lustar (+*) rune, and its purpose is to define "deferred expressions". It takes a list of pairs of names and Hoon expressions. When compiled, the deferred expressions defined in the virtual arm are implicitly inserted at the beginning of every other arm of the core, so they all have access to them. Each time a name in a +* is called, the associated Hoon is evaluated in its place, similar to lazy evaluation except it is re-evaluated whenever needed. See the tistar reference for more information on deferred expressions.

A virtual arm in an agent often looks something like this:

+*  this  .
    def   ~(. (default-agent this %.n) bowl)

.this and .def are the deferred expressions, and next to each one is the Hoon expression it evaluates whenever called. Notice that unlike most things that take n arguments, a virtual arm is not terminated with a ==. You can define as many aliases as you like. The two in this example are conventional ones you'd use in most agents you write. Their purposes are:

this  .

Rather than having to return +on-init like we did in the last lesson, instead our arms can just refer to .this whenever modifying or returning the agent core.

def  ~(. (default-agent this %.n) bowl)

This sets up the default-agent library we described above, so you can easily call its arms like +on-poke:def, +on-agent:def, etc.

Additional cores

While Gall expects a single 10-arm agent core, it's possible to include additional cores by composing them into the subject of the agent core itself. The contents of these cores will then be available to arms of the agent core.

Usually to compose cores in this way, you'd have to do something like insert tisgar (=>) runes in between them. However, Clay's build system implicitly composes everything in a file by wrapping it in a tissig (=~) expression, which means you can just butt separate cores up against one another and they'll all still get composed.

You can add as many extra cores as you'd like before the agent core, but typically you'd just add one containing type definitions for the agent's state, as well as any other useful structures. We'll look at the state in more detail in the next lesson.

Example

Here's the /app/skeleton.hoon dummy agent from the previous lesson, modified with the concepts discussed here:

/app/skeleton (modified)
/+  default-agent, dbug
|%
+$  card  card:agent:gall
--
%-  agent:dbug
^-  agent:gall
|_  =bowl:gall
+*  this  .
    def   ~(. (default-agent this %.n) bowl)
++  on-init
  ^-  (quip card _this)
  `this
++  on-save   on-save:def
++  on-load   on-load:def
++  on-poke   on-poke:def
++  on-watch  on-watch:def
++  on-leave  on-leave:def
++  on-peek   on-peek:def
++  on-agent  on-agent:def
++  on-arvo   on-arvo:def
++  on-fail   on-fail:def
--

The first line uses the faslus (/+) Ford rune to import /lib/default-agent.hoon and /lib/dbug.hoon, building them and loading them into the subject of our agent so they're available for use. You can read more about Ford runes in the Fas section of the rune documentation.

Next, we've added an extra core. Notice how it's not explicitly composed, since the build system will do that for us. In this case we've just added a single $card arm, which makes it simpler to reference the $card:agent:gall type.

After that core, we call +agent:dbug with our whole agent core as its argument. This allows us to use the /lib/dbug.hoon features described earlier.

Inside our agent door, we've added an extra virtual arm and defined a couple deferred expressions:

+*  this  .
    def   ~(. (default-agent this %.n) bowl)

In most of the arms, you see we've been able to replace the dummy code with simple calls to the corresponding arms of default-agent, which we set up as a deferred expression named .def in the virtual arm. We've also replaced the old ..on-init with our deferred expression named .this in the +on-init arm as an example - it makes things a bit simpler.

You can save the code above in /app/skeleton.hoon of your %base desk like before and |commit %base in the dojo. Additionally, you can start the agent so we can try out +dbug. To start it, run the following in the dojo:

> |rein %base [& %skeleton]

For details of using the |rein generator, see the Dojo Tools documentation.

Now our agent should be running, so let's try out +dbug. In the dojo, let's try poking our agent with the +dbug generator:

>   ~
> :skeleton +dbug
>=

It just printed out ~. Our dummy %skeleton agent doesn't have any state defined, so it's printing out null as a result. Let's try printing the $bowl instead:

>   [ [our=~zod src=~zod dap=%skeleton sap=/gall/dojo]
  [wex=~ sup=~ sky=~]
  act=5
    eny
  0v209.tg795.bc2e8.uja0d.11eq9.qp3b3.mlttd.gmf09.q7ro3.6unfh.16jiu.m9lh9.6jlt8.4f847.f0qfh.up08t.3h4l2.qm39h.r3qdd.k1r11.bja8l
  now=~2024.5.9..13.28.24..e20e
  byk=[p=~zod q=%base r=[%da p=~2024.5.9..12.02.22..f99b]]
]
> :skeleton +dbug %bowl
>=

We'll use +dbug more throughout the guide, but hopefully you should now have an idea of its basic usage.

Summary

The key takeaways are:

  • Libraries are imported with /+.

  • /lib/default-agent.hoon is a library that provides default behaviors for Gall agent arms.

  • /lib/dbug.hoon is a library that lets you inspect the state and $bowl of an agent from the dojo, with the +dbug generator.

  • Convenient deferred expressions for Hoon expressions can be defined in a virtual arm with the lustar (+*) rune.

  • .this is a conventional deferred expression name for the agent core itself.

  • .def is a conventional deferred expression name for accessing arms in the /lib/default-agent.hoon library.

  • Extra cores can be composed into the subject of the agent core. The composition is done implicitly by the build system. Typically we'd include one extra core that defines types for our agent's state and maybe other useful types as well.

Exercises

  • Run through the example yourself on a fake ship if you've not done so already.

  • Have a read through the Ford rune documentation for details about importing libraries, structures and other things.

  • Try the +dbug generator out on some other agents, like :settings +dbug, :contacts +dbug, etc, and try some of its options described above.

  • Have a quick look over the source of the /lib/default-agent.hoon library, located at /lib/default-agent.hoon in the %base desk. We've not yet covered what the different arms do but it's still useful to get a general idea, and you'll likely want to refer back to it later.

Previous2. The Agent CoreNext4. Lifecycle

Last updated 1 day ago