Urbit Developers
  • Lightning Tutorials

    • Introduction
    • Build a Groups App
    • Build a Chat App
    • Build a Voting App
    • Core Curriculum

      • Hoon School

        • Introduction
        • 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

          • Introduction
          • 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)

            • Introduction
            • 1. Types
            • 2. Agent
            • 3. JSON
            • 4. Marks
            • 5. Eyre
            • 6. React app setup
            • 7. React app logic
            • 8. Desk and glob
            • 9. Summary
          • Environment Setup
          • Additional Guides

            • Hoon Workbook

              • Competitive Programming
              • Gleichniszahlenreihe
              • Rhonda Numbers
              • Roman Numerals
              • Solitaire Cipher
              • App Workbook

                • %ahoy Ship Monitoring
                • %dbug Debugging Wrapper
                • %flap JS Client
                • %feature Page Hosting
                • Threads

                  • Fundamentals
                  • Bind
                  • Input
                  • Output
                  • Summary
                • Aqua Tests
                • Command-Line Apps
                • HTTP API
                • JSON
                • Parsing Text
                • Sail (HTML)
                • Software Distribution
                • Strings
                • Unit Tests
                • Vases
                Urbit Developers
                • Lightning Tutorials

                  • Introduction
                  • Build a Groups App
                  • Build a Chat App
                  • Build a Voting App
                  • Core Curriculum

                    • Hoon School

                      • Introduction
                      • 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

                        • Introduction
                        • 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)

                          • Introduction
                          • 1. Types
                          • 2. Agent
                          • 3. JSON
                          • 4. Marks
                          • 5. Eyre
                          • 6. React app setup
                          • 7. React app logic
                          • 8. Desk and glob
                          • 9. Summary
                        • Environment Setup
                        • Additional Guides

                          • Hoon Workbook

                            • Competitive Programming
                            • Gleichniszahlenreihe
                            • Rhonda Numbers
                            • Roman Numerals
                            • Solitaire Cipher
                            • App Workbook

                              • %ahoy Ship Monitoring
                              • %dbug Debugging Wrapper
                              • %flap JS Client
                              • %feature Page Hosting
                              • Threads

                                • Fundamentals
                                • Bind
                                • Input
                                • Output
                                • Summary
                              • Aqua Tests
                              • Command-Line Apps
                              • HTTP API
                              • JSON
                              • Parsing Text
                              • Sail (HTML)
                              • Software Distribution
                              • Strings
                              • Unit Tests
                              • Vases
                              Guides/Additional Guides/App Workbook

                              %dbug Debugging Wrapper

                              The /lib/dbug.hoon agent wrapper adds support to view the state of a Gall agent. It is applied to an existing Gall agent as a single drop-in line, %- agent:dbug.

                              Before we look at the code, let's consider the functionality it exposes. By supplying %- agent:dbug, an associated +dbug generator can be invoked against the agent state.

                              For instance, using the %azimuth agent, we can expose the current state of the agent:

                              > :azimuth +dbug
                              [ %7
                              url=~.
                              net=%default
                              refresh=~m5
                              whos={}
                              nas=[%0 points={} operators={} dns=<||>]
                              own={}
                              spo={}
                              logs=~
                              sap=[%0 id=[hash=0x0 number=0] nas=[%0 points={} operators={} dns=<||>] owners={} sponsors={}]
                              ]
                              > :azimuth +dbug %bowl
                              > [ [our=~zod src=~zod dap=%azimuth]
                              [wex={} sup={}]
                              act=3
                              eny
                              0v1rn.n49dr.2u8t5.h7be5.6dcq7.9hon5.6m3pr.3hcb8.u7tmv.qddpq.kent7.1ftc7.9tao6.hfsht.4i0c3.ak3t7.t8d8j.nn4eb.b7eh3.4d5pr.t8ftg
                              now=~2023.2.3..20.03.23..f60e
                              byk=[p=~zod q=%base r=[%da p=~2023.1.26..02.41.25..926a]]
                              ]
                              > :azimuth +dbug [%incoming ~]
                              > no matching subscriptions
                              > :azimuth +dbug [%state '(lent whos)']
                              > 0

                              There are four actions exposed by the wrapper via the +dbug generator:

                              1. :app +dbug exposes the entire state, just dumping the current agent state.

                              2. :app +dbug %bowl shows the agent's bowl. The Gall bowl consists of:

                                +$ bowl :: standard app state
                                $: $: our=ship :: host
                                src=ship :: guest
                                dap=term :: agent
                                == ::
                                $: wex=boat :: outgoing subs
                                sup=bitt :: incoming subs
                                == ::
                                $: act=@ud :: change number
                                eny=@uvJ :: entropy
                                now=@da :: current time
                                byk=beak :: load source
                                == == ::
                              3. :app +dbug [%state 'hoon'] exposes data in the state, including evaluated Hoon like (lent values).

                              4. :app +dbug [?(%incoming outgoing) specifics] reveals details about the subscriptions.

                              The Code

                              /gen/dbug.hoon:

                              Click to expand

                              /+ *dbug
                              :- %say
                              |= $: :: environment
                              *
                              :: inline arguments
                              args=?(~ [what=?(%bowl %state) ~] [=poke ~])
                              :: named arguments
                              ~
                              ==
                              :- %dbug
                              ?- args
                              ~ [%state '']
                              [@ ~] ?-(what.args %bowl [%bowl ~], %state [%state ''])
                              [[@ *] ~] poke.args
                              ==

                              /lib/dbug.hoon:

                              Click to expand

                              :: dbug: agent wrapper for generic debugging tools
                              ::
                              :: usage: %-(agent:dbug your-agent)
                              ::
                              |%
                              +$ poke
                              $% [%bowl ~]
                              [%state grab=cord]
                              [%incoming =about]
                              [%outgoing =about]
                              ==
                              ::
                              +$ about
                              $@ ~
                              $% [%ship =ship]
                              [%path =path]
                              [%wire =wire]
                              [%term =term]
                              ==
                              ::
                              ++ agent
                              |= =agent:gall
                              ^- agent:gall
                              !.
                              |_ =bowl:gall
                              +* this .
                              ag ~(. agent bowl)
                              ::
                              ++ on-poke
                              |= [=mark =vase]
                              ^- (quip card:agent:gall agent:gall)
                              ?. ?=(%dbug mark)
                              =^ cards agent (on-poke:ag mark vase)
                              [cards this]
                              =/ dbug
                              !<(poke vase)
                              =; =tang
                              ((%*(. slog pri 1) tang) [~ this])
                              ?- -.dbug
                              %bowl [(sell !>(bowl))]~
                              ::
                              %state
                              =? grab.dbug =('' grab.dbug) '-'
                              =; product=^vase
                              [(sell product)]~
                              =/ state=^vase
                              :: if the underlying app has implemented a /dbug/state scry endpoint,
                              :: use that vase in place of +on-save's.
                              ::
                              =/ result=(each ^vase tang)
                              (mule |.(q:(need (need (on-peek:ag /x/dbug/state)))))
                              ?:(?=(%& -.result) p.result on-save:ag)
                              %+ slap
                              (slop state !>([bowl=bowl ..zuse]))
                              (ream grab.dbug)
                              ::
                              %incoming
                              =; =tang
                              ?^ tang tang
                              [%leaf "no matching subscriptions"]~
                              %+ murn
                              %+ sort ~(tap by sup.bowl)
                              |= [[* a=[=ship =path]] [* b=[=ship =path]]]
                              (aor [path ship]:a [path ship]:b)
                              |= [=duct [=ship =path]]
                              ^- (unit tank)
                              =; relevant=?
                              ?. relevant ~
                              `>[path=path from=ship duct=duct]<
                              ?: ?=(~ about.dbug) &
                              ?- -.about.dbug
                              %ship =(ship ship.about.dbug)
                              %path ?=(^ (find path.about.dbug path))
                              %wire %+ lien duct
                              |=(=wire ?=(^ (find wire.about.dbug wire)))
                              %term !!
                              ==
                              ::
                              %outgoing
                              =; =tang
                              ?^ tang tang
                              [%leaf "no matching subscriptions"]~
                              %+ murn
                              %+ sort ~(tap by wex.bowl)
                              |= [[[a=wire *] *] [[b=wire *] *]]
                              (aor a b)
                              |= [[=wire =ship =term] [acked=? =path]]
                              ^- (unit tank)
                              =; relevant=?
                              ?. relevant ~
                              `>[wire=wire agnt=[ship term] path=path ackd=acked]<
                              ?: ?=(~ about.dbug) &
                              ?- -.about.dbug
                              %ship =(ship ship.about.dbug)
                              %path ?=(^ (find path.about.dbug path))
                              %wire ?=(^ (find wire.about.dbug wire))
                              %term =(term term.about.dbug)
                              ==
                              ==
                              ::
                              ++ on-peek
                              |= =path
                              ^- (unit (unit cage))
                              ?. ?=([@ %dbug *] path)
                              (on-peek:ag path)
                              ?+ path [~ ~]
                              [%u %dbug ~] ``noun+!>(&)
                              [%x %dbug %state ~] ``noun+!>(on-save:ag)
                              [%x %dbug %subscriptions ~] ``noun+!>([wex sup]:bowl)
                              ==
                              ::
                              ++ on-init
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent on-init:ag
                              [cards this]
                              ::
                              ++ on-save on-save:ag
                              ::
                              ++ on-load
                              |= old-state=vase
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent (on-load:ag old-state)
                              [cards this]
                              ::
                              ++ on-watch
                              |= =path
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent (on-watch:ag path)
                              [cards this]
                              ::
                              ++ on-leave
                              |= =path
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent (on-leave:ag path)
                              [cards this]
                              ::
                              ++ on-agent
                              |= [=wire =sign:agent:gall]
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent (on-agent:ag wire sign)
                              [cards this]
                              ::
                              ++ on-arvo
                              |= [=wire =sign-arvo]
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent (on-arvo:ag wire sign-arvo)
                              [cards this]
                              ::
                              ++ on-fail
                              |= [=term =tang]
                              ^- (quip card:agent:gall agent:gall)
                              =^ cards agent (on-fail:ag term tang)
                              [cards this]
                              --
                              --

                              As we examine this code, there are two particularly interesting aspects:

                              1. How /lib/dbug.hoon modifies an agent's arms by adding functionality over the top of them.
                              2. How /gen/dbug.hoon utilizes the modified arms with an elegant and simple invocation.

                              There is also extensive use of tank/tang formatted error messaging.

                              How the library works

                              By applying this door builder using %- censig, the ++on-poke and ++on-peek arms can be modified. (In fact, all of the arms can be modified but most of the arms are pass-throughs to the modified agent.)

                              ++on-poke

                              Click to expand

                              ++ on-poke
                              |= [=mark =vase]
                              ^- (quip card:agent:gall agent:gall)
                              ?. ?=(%dbug mark)
                              =^ cards agent (on-poke:ag mark vase)
                              [cards this]
                              =/ dbug
                              !<(poke vase)
                              =; =tang
                              ((%*(. slog pri 1) tang) [~ this])
                              ?- -.dbug
                              %bowl [(sell !>(bowl))]~
                              ::
                              %state
                              =? grab.dbug =('' grab.dbug) '-'
                              =; product=^vase
                              [(sell product)]~
                              =/ state=^vase
                              :: if the underlying app has implemented a /dbug/state scry endpoint,
                              :: use that vase in place of +on-save's.
                              ::
                              =/ result=(each ^vase tang)
                              (mule |.(q:(need (need (on-peek:ag /x/dbug/state)))))
                              ?:(?=(%& -.result) p.result on-save:ag)
                              %+ slap
                              (slop state !>([bowl=bowl ..zuse]))
                              (ream grab.dbug)
                              ::
                              %incoming
                              =; =tang
                              ?^ tang tang
                              [%leaf "no matching subscriptions"]~
                              %+ murn
                              %+ sort ~(tap by sup.bowl)
                              |= [[* a=[=ship =path]] [* b=[=ship =path]]]
                              (aor [path ship]:a [path ship]:b)
                              |= [=duct [=ship =path]]
                              ^- (unit tank)
                              =; relevant=?
                              ?. relevant ~
                              `>[path=path from=ship duct=duct]<
                              ?: ?=(~ about.dbug) &
                              ?- -.about.dbug
                              %ship =(ship ship.about.dbug)
                              %path ?=(^ (find path.about.dbug path))
                              %wire %+ lien duct
                              |=(=wire ?=(^ (find wire.about.dbug wire)))
                              %term !!
                              ==
                              ::
                              %outgoing
                              =; =tang
                              ?^ tang tang
                              [%leaf "no matching subscriptions"]~
                              %+ murn
                              %+ sort ~(tap by wex.bowl)
                              |= [[[a=wire *] *] [[b=wire *] *]]
                              (aor a b)
                              |= [[=wire =ship =term] [acked=? =path]]
                              ^- (unit tank)
                              =; relevant=?
                              ?. relevant ~
                              `>[wire=wire agnt=[ship term] path=path ackd=acked]<
                              ?: ?=(~ about.dbug) &
                              ?- -.about.dbug
                              %ship =(ship ship.about.dbug)
                              %path ?=(^ (find path.about.dbug path))
                              %wire ?=(^ (find wire.about.dbug wire))
                              %term =(term term.about.dbug)
                              ==
                              ==

                              The ++on-poke arm has several branches added to it after a check to see whether it is being used through the +dbug generator. If it isn't (as determined by the associated mark), then the poke is passed through to the base agent.

                              ?. ?=(%dbug mark)
                              =^ cards agent (on-poke:ag mark vase)
                              [cards this]

                              The following ?- wuthep handles the input arguments: %state is the most interesting code in this library. The code first checks whether the base agent has a /dbug/state peek endpoint already (in which case it passes it through), otherwise it evaluates the requested Hoon expression against the agent's state (obtained via ++on-save:ag).

                              %state
                              =? grab.dbug =('' grab.dbug) '-'
                              =; product=^vase
                              [(sell product)]~
                              =/ state=^vase
                              :: if the underlying app has implemented a /dbug/state scry endpoint,
                              :: use that vase in place of +on-save's.
                              ::
                              =/ result=(each ^vase tang)
                              (mule |.(q:(need (need (on-peek:ag /x/dbug/state)))))
                              ?:(?=(%& -.result) p.result on-save:ag)
                              %+ slap
                              (slop state !>([bowl=bowl ..zuse]))
                              (ream grab.dbug)

                              This branch includes the use of a rare =? tiswut conditional leg change and the reversed =/ tisfas, =; tismic. There is also some direct compilation of cords taking place:

                              • ++sell is a vase pretty-printer.
                              • ++slop conses two vases together as a cell.
                              • ++slap compiles a Hoon expression and produces a vase of the result.
                              • ++ream parses a cord to a Hoon expression.

                              ++on-peek

                              Click to expand

                              ++ on-peek
                              |= =path
                              ^- (unit (unit cage))
                              ?. ?=([@ %dbug *] path)
                              (on-peek:ag path)
                              ?+ path [~ ~]
                              [%u %dbug ~] ``noun+!>(&)
                              [%x %dbug %state ~] ``noun+!>(on-save:ag)
                              [%x %dbug %subscriptions ~] ``noun+!>([wex sup]:bowl)
                              ==

                              The ++on-peek arm adds several peek endpoints which expose the state (via ++onsave:ag) and the subscriptions.

                              > .^(noun %gx /(scot %p our)/azimuth/(scot %da now)/dbug/subscriptions/noun)
                              [0 0]

                              How the generator works

                              The generator explicitly injects the %dbug mark in its return cask ([mark noun]). This is a valid if uncommon operation, and it works here because the mark is never used as a transforming gate but only as a marker to see whether the arms need to pass through the values. The no-argument input is routed through the %state with an empty cord.

                              :- %dbug
                              ?- args
                              ~ [%state '']
                              [@ ~] ?-(what.args %bowl [%bowl ~], %state [%state ''])
                              [[@ *] ~] poke.args
                              ==

                              Library authors should consider augmenting developer capabilities by exposing appropriate functionality using a wrapper agent similar to /lib/dbug.

                              <-

                              %ahoy Ship Monitoring

                              %flap JS Client

                              ->

                              Edit this page on GitHub

                              Last modified March 17, 2023