Lesson 3 – BIG IFF %.y

Introduction

Our past two lessons have covered programs that were linear and performed the same sort of manipulation with our input data, no matter what that input data looked like.  Sometimes you’ll want to perform different actions based on some criteria.

 In this lesson we will learn how to write branching code based on the truth of some condition(s).  Conditional branching will be critical in later lessons, and indeed for Hoon programming in general.

Goal


Write a generator which:

  • takes an unsigned decimal (@ud) as a sample and compares with a specified value (we’ll use 5).
  • If input is greater than the specified value, adds the input to the constant.  
  • If input is less than or equal to the constant, subtracts the input from the constant.
  • In either case, returns the result.


Runes/Standard Library


?: wut·col

SyntaxSummary
?:  test  hoon  hoonBranch on a boolean test.

Checks a condition and directs the program to one of two pathways based on the truth of that condition

Explanation
?: takes 3 children:

  1. the test
  2. hoon to evaluate if true
  3. the hoon to evaluate if false
Documentation on urbit.org

Technically, ?: doesn’t care if you use a test or just hard code true or false, as we’ll see that in the examples below

Exercise

Try this in dojo:

?:  %.y
"Result 1"
"Result 2"
"Result 1"

“Result 1” is returned because it is the second child of ?: which is the result returned when the condition is true, as stated above.

Then try:

?:  %.n
"Result 1"
"Result 2"
"Result 2"


?. wut·dot

SyntaxSummary
?.  test  hoon  hoonBranch on a boolean test, inverted.

Checks a condition and chooses one of two hoon branches, presented in the opposite order of ?:.

Explanation

?. takes 3 children:

  1. the test
  2. hoon to evaluate if false
  3. hoon to evaluate if true
Documentation on urbit.org

Exercise

Try this in dojo:

?.  %.y
"Result 2"
"Result 1"
"Result 1"

In this case, “Result 1” is returned because it is the third child of wutdot which is the result returned when the condition is true.

Now try:

?.  %.n
"Result 2"
"Result 1"
"Result 2"


.= dot·tis

SyntaxSummary
.=  hoon  hoonTest for equality with Nock 5
=(hoon hoon)

Compare two hoons. If equal, return true(%.y), otherwise return false(%.n).

Documentation on urbit.org

Note: There are several ways to represent true and false in Hoon.  The default output of this test is %.y or %.n, but you may see & for true and | for false, or 0 for true and 1 for false

Yes, this is totally unconventional to take 0 as true.  For now, we recommend not getting caught up on why that design decision was made.

Exercise

Try this in dojo:

=(1 1)
%.y

Experiment with other tests of your own - what do you see?

add

SyntaxSummary
(add a b)(add a b) adds b to a

(add a b) adds b to a


sub

SyntaxSummary
(sub a b)(sub a b) subtracts b from a.

(sub a b) subtracts b from a.

Exercise

Try this in dojo:

(sub 3 1)
2
=(1 1)
%.y

(gte a b) / (gth a b) / (lth a b) / (lte a b)

As you might expect, there are four main functions for testing expected inequality between two values: Greater Than (gth), Less Than (lth), Greater Than or Equal To (gte) and Less Than or Equal To (lte).
Each of these standard library functions takes two atoms and returns true or false.

Exercise

Try these in dojo:

(gth 3 2)
%.y
(gte 2 2)
%.y
(lth 1 1)
%.n
(lte 1 1)
%.y

Experiment with these values and make sure you’re comfortable with the way these functions compare their arguments - write some and set your expectation as to what they will return, then enter them in dojo and check to make sure you were right!

Required Concepts

Branching

  • works a lot like the IF argument in Excel, or imperative programming.
    • If you have Excel you can test this yourself using the following formula =IF(TRUE,”Branch 1”,”Branch 2”) (changing TRUE to FALSE to see the varied effect).
    • If you don’t have Excel, it’s pretty easy to imagine what happens - if the “condition” is true, then the first branch after the condition (the second child of wutcol) is executed, and if the “condition” is false, the second branch after the condition (the third child of wutcol) is executed
    • The major difference here is that Hoon has more types of IF operators (wut runes).  For instance, ?. (wutdot) allows us to reverse which branch occurs based on a TRUE or FALSE test result.  Further, there are wut runes that do specific checks on their first child, other than checks for TRUE or FALSE results.  
      • ?^ (wutket), for instance, takes three children: some data (a wing - a term you will learn later), a first branch, a second branch.  Wutket branches on whether the “some data” child is a cell - which, as we know, is basically any data other than an atom.
      • ?> (wutgar), in contrast, takes only two children: some test (assertion) and some hoon thereafter.  Wutgar either crashes if the test is not true (hence calling it an assertion) or continues on in the program with some other hoon.

Walkthrough

Backstep Indentation (Proper) Walking Indentation (Improper)
|=  test=@ud
^-  @ud
=/  constant=@ud
  5
?:  (lte test constant)
  (sub constant test)
(add test constant)
|=
  test=@ud
  ^-
    @ud
    =/
      constant=@ud
      5
      ?:
        (lte test constant)
        (sub constant test)
        (add test constant)

Note: We could have done the same comparison with either ?: or ?. --  So why do both ?: and ?. exist?   It’s a matter of code style.  For the sake of readability, it is generally better to have the simple branch first and the heavier branch last.

This convention can be justified by analogy:  if we’re writing a legal document, it would be better to say, “The following applies only to Qwghlmian citizens:” than to end with “the preceding applies only to Qwghlmian citizens.”

  • |= test=@ud  
    |= creates a gate which takes a sample with face “test” and type @ud.

    • ^- @ud
      • ?:  (lte test constant)
        ?: is our branching structure.

        • The first child, (lte test constant) is the condition to be tested.  This expression compares our input and specified constant and returns True ((%.y)) if ‘test’ is less than or equal to ‘constant’
          • (sub test constant)
            The second child of ?:  -- This branch will be evaluated if our test returns true.  Then, we return the result of subtracting ‘test’ from ‘constant’.
            Note: This is only possible if ‘test’ is less than or equal to ‘constant’.  If the ‘test’ value is greater than ‘constant’, this equation will result in a “subtraction underflow” as there are no negatives in unsigned decimal.
          • (add test constant)   The third child of ?:  -- This branch will be evaluated if our test returns false.  In that case, we return the result of adding ‘test’ and ‘constant’.

Homework

  1. Write a naked generator which takes an atom(@), checks its value against 10 and 100 and returns a tape stating “less than 10”, “at least ten but not more than 100”, or “more than 100”
  2. Write a naked generator which takes a noun (*), checks if the noun is a cell or an atom and if cell, produces a cell of [“cell” [<input cell>]].  If atom, determines if the atom is even or odd, and produces a cell of [“even” <atom>] or [“odd” <atom>]

hint: there are two runes (?@ and ?^) for checking if a noun is an atom or a cell. Use only one or the other! If you attempt to use both in series, the compiler will complain, as you will be trying to "check" for something which is already known.