Lesson 5 — Sibilance
We must begin combining things we’ve learned in prior lessons, so that we can produce increasingly complex generators.
Once we begin stitching pieces together, you will discover new and interesting ways to create complex programs. You might even be ready to make some programs of your own, but stick with us nonetheless!
In this lesson, we’re going to take what we learned in the last several lessons and combine them into a silly generator that takes in a tape and produces one of two tapes.
This exercise will tie together the last two lessons in which we looked at conditionals and cores with multiple arms. Later programs will have many arms and may need to call one or another based on the inputs, which makes this structure (a conditional in a multi-arm core) fairly important.
GoalWrite a generator that checks an incoming tape for a specific ‘passphrase.’ If the passphrase is found, produce another desired tape; if not, produce a tape indicating the failure.
|=. wing hoon hoon||Change the value of a leg of the subject|
=. changes the value of one leg of the subject. Technically =. creates a new subject, identical to the old subject except with the specified change.
Tisdot takes 3 children:
- the face from the subject you intend to modify,
- the new value for that face
- the following hoon which can use the face’s new value in evaluation, until that evaluation is complete
|(list <type>)||Produces a type, which is a list of items having a specified type.|
++list is a standard library function known as “mold generator”.
++list takes one argument, a type, and produces a mold of a list of that type.
As with our last generator, and from here on, you will need to begin parsing the flow of a program as you read it. Not everything is going to operate linearly along the lines of the program. Some calls will require us to move our focus down a few lines and then back up; conditionals will require us to think about which of two optional pieces of code will be executed. Begin to practice this now. We will walk you through it again this time.
|= test-test=tape |^ ^- tape ?: =((checker test-test) &) (weld "One Two... One Two..." test-test) "*horrific feedback whine*" ++ checker |= to-check=tape =. to-check (cass to-check) ?: =(to-check "mic check") & | --
Note: This generator again unnecessarily uses an arm to perform a check which could be performed inline - the point is to demonstrate the capacities of using conditionals and arms together - this is not necessarily a well written generator. If you’re feeling adventurous, rewrite this without the use of a core, doing the check inline and see if you can make it work (solution at bottom)
|= creates a gate and requests one argument, a
|^ creates a core, with one arm called $, that is computed immediately, and at least one other arm (
checker, in this example).
^- casts the output of |^'s core as a tape.
?: =((checker test-test) &)
?: creates a test with branching outcomes — if the test is true, it will branch to the second child; if false, branch to the third child.
(weld "one two...one two..." test-test)
The second child of ?: resolves to a tape — a modified version of the input string
"*horrific feedback whine*"
The third child of ?: — the "if-false" branch — simply evaluates to a tape.
++ indicates the start of a new arm named
checker. Checker will evaluate to a gate, which expects an input tape and produces a pam(true) or bar(false).
=. to-check (cass to-check)
=. creates a copy of the subject with a modification, for the evaluation of the hoon following it. In this case, it adds to the subject a face
to-checkwith a value of a fully lowercase version of the input test.
?: =(to-check "mic check")
?: creates another test — if the incoming tape (as modified to be fully lowercase) is equal to "mic check," branch to the second child; if the test is false, branch to the third.
pam is a "loobean" (boolean but in hoon) value meaning true.
bar is a "loobean" (boolean but in hoon) value meaning false
-- is a boundary which indicates the termination of a core, and is required as there are an indeterminate number of arms / children in a |^ core.
- Form a gate and take an input tape.
- Step 2: Evaluate the buc arm of the gate
- Form a core, with one arm called $ (to be evaluated immediately) and at least one other arm, and cast it’s output as a tape.
|^ ^- tape
- Step 4: Create a branching test checking whether (checker test-test), a call to the one named arm of |^, is equal to &(true); branch based on the results
Move to the arm checker and pass the test-test tape as the input (with a face of to-check) to the gate formed by the arm
- Duplicate the subject with one modification, that of adding to the subject an all lowercase copy of our input.
=. to-check (cass to-check)
to-checkis equal to "mic check," return pam else return bar
?: =(to-check "mic check") & |
- Step 8: If the result of
(checker test-test)- steps 5,6 and 7 - is &, output “One Two… One Two…” else output “*horrific feedback whine*”
- Lus Rune Family - https://urbit.org/docs/reference/hoon-expressions/rune/lus/
- Write a generator that takes a
@udand uses the following code skeleton to call two arms that do some work and then returns a cell of those arms’ results.
- In (at least) one arm, use multiple algebraic standard library functions.
- In (at least) one arm, use a conditional expression.
|= input=@ud |^ :- (your-arm-one input) (your-arm-two input) ++ your-arm-one :: your arm code here ++ your-arm-two :: your arm code here
- Bonus Points - Write the above program, but with multiple arms to request different items from the list. Modify the gate's sample to let you select which arm to call. (There is an easier way to do this which we will see later!)
- Note: The "x" described above cannot be a face - it must be a value. Experiment with the following code to get the hang of it: