Lesson 2 — Making Faces

Introduction

In the previous lesson, we learned that a Naked Generator explicitly requests all of its inputs at the beginning of its gate.  You’ll recall that in the previous lesson our generator’s input was given a face of “my-name”.  That face is completely arbitrary, and it could just as easily have been called “a”, “planet-name”, or any other string you’d prefer (though, note that when we have two-word-faces we like to separate them with hep). 

In our last example, the only value with a face was our input.  In this lesson, we’ll look at using faces for other values, and even computations in our programs. In other languages, using a name/variable for a static value takes quite a different syntax from using a name for a function.

In Hoon, a face is simply an address in the subject.  As we've discussed in class, addresses in the subject can be atoms (leaves), data structures (branches - this and the former being "legs" of the subject), or even functional code ("arms" - the other type of "wing).  Therefore we can give faces to values and code in exactly the same manner.

Goal

Write a generator that takes in one piece of information, assigns it a face, and internally creates two other faces and assigns them values based on the first. Print the output as a triplet (3-tuple) cell.

Rune List


=/ tis·fas

SyntaxSummary
=/  name=type  value  hoonCombine a named (optionally typed) noun to the subject.

Adds to the subject a noun with a specified face and often a specified type.  Functions much like a typical variable declaration in other languages.

  • =/ takes three children.
  • The first child is the face given to the noun with optional metadata explicitly stating the noun’s type.
  • The second child is the value of the new noun,
  • The third child is some hoon to run, in light of our new subject.
Documentation on urbit.org

Exercise

Try this in dojo:

=/  foo=@ud  2
foo
2

Note: Hoon will immediately dispose of this element of the subject once =/ has been computed.  That is, you cannot type foo again in your dojo to get 2 back out of it, per the example above.

foo
-find.foo
dojo: hoon expression failed


=| tis·bar

SyntaxSummary
=|  name=type  hoonCombine a default type value with the subject.

Creates a face with a (required) type cast

  • =| takes 2 children
    1. the face and type
    2. the following hoon which can use its face in evaluation, until that evaluation is complete
Documentation on urbit.org

Exercise

Try this in dojo:

=|  foo=@ud
foo
0

Note:  We have not given foo a value, but it returns 0.  All types in Hoon have default or bunt values. In this case, our type is the aura @ud, which has a bunt value of 0.

Also note:  Often, =|  will be used to create a container for the final product of a generator.  The face named in our tisbar will often be our output value, in the specified type, as modified by the code we ran.


=. tis·dot

SyntaxSummary
=.  wing  hoon  hoonChange 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:

  1. the face from the subject you intend to modify,
  2. the new value for that face
  3. the following hoon which can use the face’s new value in evaluation, until that evaluation is complete
Documentation on urbit.org

Exercise

Try this in dojo

=|  foo=@ud
=.  foo  2
foo
2

Note: Just like before, at the end of the line with tisbar, the value of foo was actually 0.  We used tisdot to modify that value for production in the third line.


:+ col·lus

SyntaxSummary
:+  hoon  hoon  hoonproduce a triple(3-tuple)

Produces a triple cell.  

Like :-  :+ is used to produce a cell.  In this case, however, it takes 3 values and produces a cell of the three.  Technically, the last two values in a :+ expression are in a cell of their own.

collus takes 3 children, all values, for the production of its cell

  1. First Element
  2. Second Element
  3. Third Element
Documentation on urbit.org

Exercise

Try this in dojo:

:+  1  2  3
[1 2 3]

Note: Cells can only ever contain two values.  So how are we doing the above “triple cell” shenanigans?  The printed format [1 2 3] in fact is representative of two cells [1 *] and [2 3] where * is [2 3].


Exercise

Try this in dojo:

:-(1  [2  3])
[1 2 3]

And using collus:

:+  1  2  3
[1 2 3]

But note that the following is a duple, not a triple, of [1 2] and 3:

:+  [1 2]  3
 

Note: The caret at the end of your prompt is facing backwards to indicate that there is an open expression without having completed the evaluation.  The cell [1 2] is treated as one value in the evaluation of collus, and so the atom 3 is only the second child.  Urbit expects a third one:

4
[[1 2] 3 4]


Required Concepts

(add x y)

This is our first Standard Library function.  Runes are the basic building blocks of Hoon, but some rune constructions have been built into Standard Library functions.  It’s pretty easy to guess what (add x y) does, but later we will encounter some less trivial Standard Library functions.

Exercise

Try this in dojo

(add 1 2)
3

Tall / Wide Forms

Most runes have both Tall and Wide syntax. You have already seen both.

:-(1 2) is wide. :-  1  2  is tall.

In wide form, rune children are enclosed in parentheses separated by a single space (ace).

In tall form, elements are separated by a gap, i.e. 2 spaces or more including line breaks.  Terminators are not used, except for n-ary runes.

The following are all valid Tall Form expressions.

:-  1  2

:-
  1
2

:-  1
2

On the other hand, :-(1 2) is the only valid wide form of the same.

Temporary Subject

The tis family of runes generally mutate the subject, however that subject only persists until the final product is evaluated.

On the other hand, in the dojo, we pin faces to our subject (with =face) which persist permanently until removed.  Note that =face is special dojo syntax and not proper Hoon.

Often in Hoon, we mutate our subject (create a new mutated subject) for a specific purpose, and that subject is dropped after evaluation.

Cell Nesting and Pretty Printing

Recall from our introduction and the Urbit walkthrough that all cells are “read from the right” and that the printer automatically removes [ ]s that are unnecessary, given this reading strategy.  Let’s do some examples in the dojo together (c’mon do it!  Your muscle memory will thank us later):

[1 2 [3 4]]
[1 2 3 4]
[%foo [%bar %baz] %bang]
[%foo [%bar %baz] %bang]
[%foo [[%bar %baz] %bang]]
[%foo [%bar %baz] %bang]
[[1 %head] [2 %tail]]
[[1 %head] 2 %tail]
Backstep Indentation (Proper) Walking Indentation (Improper)
|=  initial=@ud
=/  init-plus-one=@ud
  (add 1 initial)
=|  init-plus-two=@ud
=.  init-plus-two
  (add 1 init-plus-one)
^-  [@ud @ud @ud]
:+  initial
  init-plus-one
init-plus-two
|=   
 initial=@ud
 =/
  init-plus-one=@ud
  (add 1 initial)
  =|
   init-plus-two=@ud
   =.  
    init-plus-two  
    (add 1 init-plus-two)
    ^-
     [@ud @ud @ud]
     :+
       initial
       init-plus-one
       init-plus-two

Again, note that ‘walking indentation’ may feel more natural/familiar and easier to parse. However this example is beginning to show the problem with walking indentation.  Any non-trivial example will quickly be lost off the right-hand side of the page.

Demo

+nakedgenerator 1
[1 2 3]

Walkthrough

  • |=  initial=@ud
    |=  creates a gate that requests an argument with a face of “initial”, that should be an @ud, or an “unsigned decimal”.
    Note: An unsigned decimal, in common language, is a regular number with no + or - signs, no decimals, no fractions. Think of how you teach a child to count:  1, 2, 3, 4, and so on.  Stated differently it is an integer in the base 10 system that is not given a negative or positive ‘sign’.  You cannot represent a fractional number (¾) as an @ud (e.g. .75), though we will show you ways of representing fractional numbers later.

    • =/  init-plus-one=@ud  (add 1 initial)
      =/  declares a face of init-plus-one, that should be an @ud, and gives it a value of 1 plus the value of our incoming argument, initial.

      • =|  init-plus-two=@ud
        =|  declares a face of init-plus-two, that should be an @ud.
        Importantly, at this point, init-plus-two is not given a value, and so would (were it to be called here) provide a bunt value of 0, as discussed previously.

        • =.  init-plus-two  (add 1 init-plus-one)
          =.  modifies (or to be explicit copies and mutates and replaces for evaluation) the value corresponding to init-plus-two (which is currently 0) to be now equal to the value of init-plus-one, plus 1.

          • ^-  [@ud @ud @ud]
            ^-  creates a cast for the output of our gate.  Our output should be three @uds in a cell.  Casting performs two main functions:

            1. It identifies for a reader what the program is intending to produce
            2. It identifies for Hoon what the product should look like - if, at the end of evaluation, the product Hoon has does not look like the cast, it will generate an error message

            Note: Technically there are two cells here.  As you may recall [@ud [@ud @ud]] is the same as [@ud @ud @ud] because it is always understood that the right-most nouns constitute a cell.

          • :+  initial
            :+  will create a cell of 3 values (or two cells, as it were).
            The first child of collus is our input value, initial

            •   init-plus-one
              init-plus-one is the second child of  :+  above, and will have a value equal to our initial argument plus 1
              Note: init-plus-one is indented 2 spaces.  Runes can be written in both long and tall form.  For the most part, we will use tall form in our guides.  In tall form, each subsequent line of code should be two spaces closer to the origin (left hand margin), thus, for  :+, our second line should be two spaces in, and our last line should be one, and in fact is
            • init-plus-two
              init-plus-two is the third and final child of :+  above, and will have a value equal to init-plus-one, plus 1 (or our initial value plus 2)
              Note: To complete our tall form writing of :+ , init-plus-two, it’s last child, is back against the origin (left hand margin)

Just for kicks, let us look at the same generator written entirely in wide form:

=long |=(init=@ud =/(init-one=@ud (add 1 init) =|(init-two=@ud =.(init-two (add 1 init-one) :+(init init-one init-two)))))
(long 1)
[1 2 3]

This still works, but is cumbersome.  Word wrapping becomes a problem in terms of visual digestibility, and overall this is simply harder to read.

Typically, you will see Tall form used for the bulk of Hoon code, with chunks written in wide form for the sake of density.

Readings:

Homework

Please submit your work to us by the deadline on the Schedule (see schedule in the dropdown menu above) via this form.  Please submit your homework with all exercises from one week (two lessons) as a single pastebin or code-sharing platform of your choice.

  1. Write a generator in tall form that takes one @ud input and adds a constant of your choice to it.  Set that constant using =/.  Review the Style Guide before submitting.
  2. Write your same generator from exercise 1 in long form.  Confirm that it works the same way.  Submit this as well.
  3. Write a generator that takes some input, pins a value to the subject with =/, and outputs a cell of the input and the pinned value.