Introduction

Early on, we told you that we’d help you build a generator that did several fun text manipulations like taking a tape and returning a tape with the inverse-alphabet letters in the appropriate positions (“abc” -> “zyx”), or the letter’s number position (“abc” -> “01/02/03”) or even just the SpongeBob sarcasm meme text format of alternating uppercase and lowercase letters (“abcdef” -> “AbCdEf”).

Today we’ll finally deliver on that promise.  Due to the length of this generator, and your experience with nearly all of the code being used, we’re going to abbreviate some portions of the walkthrough. You should be able to read most of this on your own by now. After all, you’re a Hooner now (instructor cocks head, raises one eyebrow), aren’t you?

Goal

Write a generator that can take an incoming tape and an instruction on how to manipulate that tape and then, using a series of other arms, manipulate the tape in the selected manner.

Syntax

(~(put by <map>) <key> <value>)

Uses %~ to call the put arm of the by core (that deals with maps).  put:by requires the identification of a map on which it should operate, and then two arguments.  The two arguments will be added to the map as a pair of a key and a value.

Atomic Type “?”

? (wut) as a type indicates a boolean option of either %.y or %.n

$%

Creates a tagged union -- like $?, except the union is tagged by head atom.
$% creates a union of types, each of which has a head of some @tas atom. When normalizing to this type, a noun's head is is compared to the head of each type.
For example, if we define our type as: $%([%1 @] [%2 [@ @]] [%3 [@ @ @]])
Valid nouns include: [%1 42], [%2 [34 12]], [%3 [1 2 3]]

Note - Some of the runes in the following generator have not been covered explicitly, but you should have had some exposure to them through your readings.  You may need to review the lus and buc family of runes; take this opportunity to familiarize yourself with referencing the docs on https://urbit.org when you have a question.

Control+/ allows you to search the documentation from anywhere on urbit.org
Remember, you can always ask in our chat room, as well.

Generator


|=  [incoming=tape style=?(%reverse %number-substitution %sponge-bob)]
=<  ^-  product
?-  style
    %reverse
  [style (reverse incoming)]
    %sponge-bob
  [style (sponge-bobber incoming)]
    %number-substitution
  [style (number-substitution incoming)]
==
|%
+$  product  $%([%reverse tape] [%number-substitution (list @ud)] [%sponge-bob tape])
++  alpha  "abcdefghijklmnopqrstuvwxyz"
++  ab-to-zy
  =/  alp=tape  alpha
  =/  zyx=tape  (flop alp)
  =|  rev=(map @t @t)
  ^-  (map @t @t)
  |-
  ?:  |(?=(~ alp) ?=(~ zyx))
    rev
  $(rev (~(put by rev) i.alp i.zyx), alp t.alp, zyx t.zyx)
++  ab-to-onetwo
  =/  alp=tape  alpha
  =/  num=@ud  1
  =|  abone=(map @t @ud)
  ^-  (map @t @ud)
  |-
  ?~  alp
    (~(put by abone) ' ' 0)
  $(abone (~(put by abone) i.alp num), alp t.alp, num +(num))
++  number-substitution
  |=  recip=tape
  =/  my-map=(map @t @ud)  ab-to-onetwo
  =|  output=(list @ud)
  =.  recip  (cass recip)
  |-  ^-  (list @ud)
  ?~  recip
    (flop output)
  $(output [(~(got by my-map) i.recip) output], recip t.recip)
++  reverse
   |=  recip=tape
  =/  my-map=(map @t @t)  ab-to-zy
  =|  output=tape
  =.  recip  (cass recip)
  |-  ^-  tape
  ?~  recip
    (flop output)
  ?:  =(' ' i.recip)
    $(output [' ' output], recip t.recip)
  $(output [(~(got by my-map) i.recip) output], recip t.recip)
++  sponge-bobber
  |=  sponge=tape
  =/  alt=?  ?~(sponge ~|("incorrect call" !!) ?:((gte 97 i.sponge) %.y %.n))
  =|  output=tape
  =.  sponge  (cass sponge)
  |-  ^-  tape
  ?~  sponge
    (flop output)
  ?:  =(' ' i.sponge)
    $(output [' ' output], sponge t.sponge)
  ?:  =(alt %.y)
    $(output [`@t`(sub i.sponge 32) output], alt %.n, sponge t.sponge)
  $(output [i.sponge output], alt %.y, sponge t.sponge)
--

Walkthrough

  • |= |= creates a gate, taking two children  
    • [incoming=tape style=?(%reverse %number-substitution %sponge-bob)]
      Our sample spec, a cell of a tape and one of three speified %terms.
    • =<Evalute the second child (|%) and use as subject for evaluation of the first child:
      • ^- Cast our output
        • product to the custom type, product.
        • ?-  styleBranch based on the type of style.
          • %reverseIf style=%reverse,
          • [style (reverse incoming)]Produce a cell of %reverse and the product of the ++reverse arm.
          • %sponge-bobIf style=%sponge-bob,
          • [style (sponge-bobber incoming)]Produce a cell of %sponge-bob and the product of the ++sponge-bobber arm.
          • %number-substitution If style=%number-substitution
          • [style (number-substitution incoming)] Produce a cell of %number-substitution and the product of the ++number-substitution arm.
          • ==
      • |% The second child of =<. Note this is evaluated first, before the code above, and used as subject for the same.
        • +$  product Define a custom type,
          • $%([%reverse tape] [%number-substitution (list @ud)] [%spongebob tape])Which is a union of the three cell types inside $%
        • ++  alpha  "abcdefghijklmnopqrstuvwxyz"
          Define an arm which evalutates to a tape of all alpha characters.
        • ++  ab-to-zy Arm which evaluates to a map between the alpha tape and its reverse.
          • =/  alp=tape  alpha Our alpha tape.
            • =/  zyx=tape  (flop alp) Reversed alpha tape.
              • =|  rev=(map @t @t) Instantiate an empty map, to be populated with [@t @t] pairs.
                • ^-  (map @t @t) Cast output to (map @t @t)
                  • |- Create trap for recursion.
                    • ?:
                      • |(?=(~ alp) ?=(~ zyx)) If both tapes are ~, we have finished mapping.
                      • rev Produce our reversed alpha map.
                      • $(rev (~(put by rev) i.alp i.zyx), alp t.alp, zyx t.zyx)
                        Recurse back to trap, inserting into the map, a pair of the heads of our two tapes. Keep the tails of each tape for further map entries.
        • ++  ab-to-onetwo Arm which evaluates to a map between the alpha tape and numbers 1-26. This arm operates exactly like the arm above, except incrementing num=@ud for the second value of each pair in the map.
          • =/  alp=tape  alpha
            • =/  num=@ud  1
                • =|  abone=(map @t @ud)
                  • ^-  (map @t @ud)
                    • |-
                      • ?~  alp
                        • (~(put by abone) ' ' 0)
                        • $(abone (~(put by abone) i.alp num), alp t.alp, num +(num))
          • ++  number-substitution Arm which substitues numbers for letters.
            • |=  recip=tape Our incoming tape.
              • =/  my-map=(map @t @ud)  ab-to-onetwo Our map between letters and numbers.
                • =|  output=(list @ud) Our output will be (list @ud)
                  • =.  recip  (cass recip) A copy of our tape with only lowercase letters, using standard library cass.
                    • |- Trap for recursion
                      • ^-  (list @ud)
                        • ?~  recip When recip=~, we've completed substitution,
                          • (flop output) Reverse the order of our list (since we created it by adding to the head).
                          • $(output [(~(got by my-map) i.recip) output], recip t.recip) Recurse into trap, updating output by adding (to the head) the value from our map which matches the head of our tape. Keep the tail of our tape for further substitution.
        • ++  reverse
          Arm which substitues letters with their opposites. This arm functions exactly as the arm above, except that it produces a tape instead of (list @ud).

          • |=  recip=tape
            • =/  my-map=(map @t @t)  ab-to-zy
              • =|  output=tape
                • =.  recip  (cass recip)
                  • |-
                    • ^-  tape
                      • ?~  recip
                        • (flop output)
                        • ?:  =(' ' i.recip)
                          • $(output [' ' output], recip t.recip)
                          • $(output [(~(got by my-map) i.recip) output], recip t.recip)
        • ++  sponge-bobber Arm for generating sPoNgE bOb mEmE tExT.
          • |=  sponge=tape Our incoming tape.
            • =/  alt=? alt is a flag(either %.y or %.n) to be used for alternating capitalization flipping.
              • ?~(sponge ~|("incorrect call" !!) ?:((gte 97 i.sponge) %.y %.n)) If our incoming tape is ~, crash. Otherwise, the starting value for alt is %.y if the first character is capital or %.n otherwise.
              • =|  output=tape Empty tape which will become our output.
                • =.  sponge  (cass sponge) Lowercase-only copy of our input tape.
                  • |- Trap for recursion.
                    • ^-  tape Cast output as tape.
                      • ?~  sponge If sponge is ~,
                        • (flop output) Produce our output, flopped as in prior arms.
                        • ?:  =(' ' i.sponge) If the head of sponge is a space,
                          • $(output [' ' output], sponge t.sponge) add a space to our output, keeping the tail of sponge for further parsing.
                          • ?:  =(alt %.y) If our flag, alt, is %.y,
                            • $(output [`@t`(sub i.sponge 32) output], alt %.n, sponge t.sponge)
                              Add a capital letter copy of the head of sponge to output, flip the parity of our flag(alt), and keep the tail of our tape for further parsing.
                            • $(output [i.sponge output], alt %.y, sponge t.sponge)
                              Add a lowercase copy of the head of sponge to output, flip the parity of our flag, and keep the rest of the tape for further parsing.
        • --

Flow

  1. Request an input of a cell of a tape and an @tas - the @tas will specify which conversion to run against the tape provided
    • |=  [incoming=tape style=@tas]
  2. Evaluate two expressions, inverted
    • The first expression uses ?- to branch into the appropriate arm based on our sample.
  3. The second expression is the |% core which contains arms for the various manipulations, and is evaluated first
  4. The first few arms evaluate to static data: A tape of alpha characters, and maps between characters.
  5. The final three arms produce our manipulated outputs, by taking our input tape, and recursing through from head to tail, each time producing an additional item for our output.

Homework

  1. Option 1:
    Write a naked generator of your choice. The level of complexity is up to you. It should demonstrate an understanding of the concepts learned in this course, including arms and cores, conditionals, and recursion. Line-by-line comments are not necessary, but do comment with basic program flow.

    Option 2: (If your creativity is failing you)
    Write a naked generator which takes a tape parses it by spaces, producing a number of words and list of tapes. e.g. "I love Hoon" > [3 ~["I" "love" "Hoon"]]
     

  2. Provide your T-Shirt size and shipping address.
  3. Share any honest course feedback.