on f3ca521 help sign in
185 lines 7.9 kB history raw
1+++
2title = "2.3.1 Walkthrough: Libraries"
3weight = 28
4template = "doc.html"
5aliases = ["/docs/learn/hoon/hoon-tutorial/libraries/"]
6+++
8In Hoon, like in other languages, it's often useful to create a library for other code to access. In this example, we look at one example of a library that can be used to represent a deck of 52 playing cards. The core below builds such a library, and can be accessed by programs.
10```hoon
11|%
12+$ suit ?(%hearts %spades %clubs %diamonds)
13+$ darc [sut=suit val=@ud]
14+$ deck (list darc)
15++ make-deck
16 ^- deck
17 =| mydeck=deck
18 =/ i 1
19 |-
20 ?: (gth i 4)
21 mydeck
22 =/ j 2
23 |-
24 ?. (lte j 14)
25 ^$(i +(i))
26 %= $
27 j +(j)
28 mydeck [(num-to-suit i) j]^mydeck
29 ==
30++ num-to-suit
31 |= val=@ud
32 ^- suit
33 ?+ val !!
34 %1 %hearts
35 %2 %spades
36 %3 %clubs
37 %4 %diamonds
38 ==
39++ shuffle-deck
40 |= [unshuffled=deck entropy=@]
41 ^- deck
42 =| shuffled=deck
43 =/ random ~(. og entropy)
44 =/ remaining (lent unshuffled)
45 |-
46 ?: =(remaining 1)
47 :_ shuffled
48 (snag 0 unshuffled)
49 =^ index random (rads:random remaining)
50 %= $
51 shuffled (snag index unshuffled)^shuffled
52 remaining (dec remaining)
53 unshuffled (oust [index 1] unshuffled)
54 ==
55++ draw
56 |= [n=@ud d=deck]
57 ^- [hand=deck rest=deck]
58 :- (scag n d)
59 (slag n d)
60--
61```
63On the very first line, we create a core with `|%`. This core contains all of our library code, and is closed by the `--` on the final line.
65To create three types we're going to need, we use `+$`, which is an arm used to define a type.
68```hoon
69+$ suit ?(%hearts %spades %clubs %diamonds)
70+$ darc [sut=suit val=@ud]
71+$ deck (list darc)
72```
74The first is `suit`, which can be either `%hearts`, `%spades`, `%clubs`, or `%diamonds`.
76Next is a `darc`, which which is a pair of `suit` and a `@ud`. By pairing a suit and a number, it forms a particular card, such as "nine of hearts." Why do we call it `darc` and not `card`? Because `card` already has a meaning in gall, the Arvo app module, where one would likely to use this (or any) library. It's worthwhile to avoid any confusion over names.
78Our final type is `deck`, which is simply a `list` of `darc`.
80One way to get a feel for how a library works is to skim the `++` arm-names before diving into any specific arm. In this library, the arms are `make-deck`, `num-to-suit`, `shuffle-deck`, and `draw`. These names should be very clear, with the exception of `num-to-suit` though you could probably hazard a guess at what it does. Let's take a closer look at that one first.
82```hoon
83++ num-to-suit
84 |= val=@ud
85 ^- suit
86 ?+ val !!
87 %1 %hearts
88 %2 %spades
89 %3 %clubs
90 %4 %diamonds
91 ==
92```
94We can see this is a gate that takes a single `@ud` and produces a `suit`.
96The `?+` rune is the rune to switch against a value with a default. The default here is to crash with `!!`. Then we have the four options of 1 through 4, based on the value of `val`, which each resulting in a different suit.
98```hoon
99++ make-deck
100 ^- deck
101 =| mydeck=deck
102 =/ i 1
103 |-
104 ?: (gth i 4)
105 mydeck
106 =/ j 1
107 |-
108 ?. (lte j 13)
109 ^$(i +(i))
110 %= $
111 j +(j)
112 mydeck [(num-to-suit i) j]^mydeck
113 ==
114```
116`num-to-suit` is used in the `make-deck` arm shown above. This arm should be quite readable to you at this point. Here we simply have two loops where we use counters to build up the full set of 52 cards, by cycling through every possible suit and number and combining them. Once we have reached the point where `j` is greater than 14, we're going to jump back out to the "suit" outer-loop and increment `i`. `?.` may be an unfamiliar rune; it is simply the inverted version of `?:`, so the first branch is actually the "no" branch and the second is the "yes" branch. This is done to keep the "heaviest" branch at the bottom.
119```hoon
120++ draw
121 |= [n=@ud d=deck]
122 ^- [hand=deck rest=deck]
123 :- (scag n d)
124 (slag n d)
125```
127`draw` takes two arguments: `n`, a number; and `d`, a `deck`. It's going to produce a cell of two `decks`. The cell is created using `scag` and `slag`. [`scag`](https://urbit.org/docs/reference/library/2b/#scag) is a standard-library gate produces the first `n` elements from a list, and [`slag`](https://urbit.org/docs/reference/library/2b/#slag) is a standard-library gate that produces the remaining elements of a list starting after the `n`th element. So we use `scag` to produce the drawn hand of `n` cards in the head of the cell as `hand`, and `slag` to produce the remaining deck in the tail of the cell as `rest`.
129```hoon
130++ shuffle-deck
131 |= [unshuffled=deck entropy=@]
132 ^- deck
133 =| shuffled=deck
134 =/ random ~(. og entropy)
135 =/ remaining (lent unshuffled)
136 |-
137 ?: =(remaining 1)
138 :_ shuffled
139 (snag 0 unshuffled)
140 =^ index random (rads:random remaining)
141 %= $
142 shuffled (snag index unshuffled)^shuffled
143 remaining (dec remaining)
144 unshuffled (oust [index 1] unshuffled)
145 ==
146```
148Finally, in the code above, we come to `shuffle-deck`. This gate takes two arguments: a `deck`, and a `@` as a bit of `entropy` to seed the `og` core. It's going to produce a `deck`.
150`=|` adds a default-valued `deck` to the subject.
152Next, with `=/ random ~(. og entropy)`, we feed the `og` core the entropy it needs in preparation for using it. Then, with `=/ remaining (lent unshuffled)`, we get the length of the unshuffled deck with `lent`.
154`?: =(remaining 1)` checks if have only one card remaining. If that's true, we produce a cell of `shuffled` and the one card left in `unshuffled`. We use the `:_` rune here, so that the "heavier" hoon is at the bottom of the expression.
156If the above conditional evaluates to false, we are going to do a little work. `=^` is a rune that pins the head of a pair and changes the leg with a tail. It's useful for interacting with the `og` core arms, as many of them produce a pair of a random numbers and the next state of the core. We're going to put the random number in the subject with the face `index` and change `random` to be the next core.
158With completed, we use `%^` to call `$` to recurse back up to `|-` with a few changes.
160`shuffled` gets the `darc` from `unshuffled` at `index` added to the front of it.
162`remaining` gets decremented. Why are we using a counter here instead of just checking the length of `unshuffled` on each loop? `lent` traverses the entire list every time it's called so maintaining a counter in this fashion is much faster.
164`unshuffled` becomes the result of using `oust` to remove 1 `darc` at `index` on `unshuffled`.
166This is a very naive shuffling algorithm, and you could imagine a better one. Implementation of a better algorithm is left as an exercise for the reader.
168## Using the Library
171So now that we have this library, how do we actually use it? Let's look at a very simple `say` generator that takes advantage of what we built.
173```hoon
174/+ playing-cards
175:- %say
176|= [[* eny=@uv *] *]
177:- %noun
178(shuffle-deck:playing-cards make-deck:playing-cards eny)
179```
181If you save our the library as `playing-cards.hoon` in the `lib` directory, you can import the library with the `/+` rune. Our above `say` -- let's call it `cards.hoon` -- imports our library in this way. It should be noted that `/+` is not a Hoon rune, but instead a rune used by Ford, the Urbit build-system. When `cards.hoon` gets built, Ford will pull in the requested library and also build that. It will also create a dependency so that if `playing-cards.hoon` changes, this file will also get rebuilt.
183Below `/+ playing-cards`, you have the standard `say` generator boilerplate that allows us to get a bit of entropy from `arvo` when the generator is run. Then we feed the entropy and a `deck` created by `make-deck` into `shuffle-deck` to get back a shuffled `deck`.
185### [Next Up: Reading -- Trees, Sets, and Maps](../trees-sets-and-maps)