mirror of
https://github.com/urbit/shrub.git
synced 2024-12-04 14:58:10 +03:00
Merge branches 'top-down', 'claydoc-rebase', 'curtis-url-fix' and 'curtis-tutorial-3-4'
Conflicts: urb/zod/pub/docs/dev/hoon/tutorial/2-syntax.mdy added urbit.pill fixes #563 fixes #574
This commit is contained in:
commit
1c1f98591d
@ -4,10 +4,7 @@
|
|||||||
/? 314
|
/? 314
|
||||||
/- talk, sole
|
/- talk, sole
|
||||||
/+ talk, sole
|
/+ talk, sole
|
||||||
/= seed /~ !>(.)
|
/= seed /~ !>(.)
|
||||||
/= talk-doc
|
|
||||||
/; |=(a=wain (turn a |=(b=cord [%txt "? {(trip b)}"])))
|
|
||||||
/: /===/pub/doc/talk/help /txt/
|
|
||||||
::
|
::
|
||||||
::::
|
::::
|
||||||
::
|
::
|
||||||
@ -913,7 +910,7 @@
|
|||||||
%unset (unset +.job)
|
%unset (unset +.job)
|
||||||
%target (target +.job)
|
%target (target +.job)
|
||||||
%probe (probe +.job)
|
%probe (probe +.job)
|
||||||
%help (help)
|
%help help
|
||||||
%say (say +.job)
|
%say (say +.job)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
@ -921,10 +918,12 @@
|
|||||||
|= gam=telegram
|
|= gam=telegram
|
||||||
^+ ..sh-work
|
^+ ..sh-work
|
||||||
=+ tay=~(. tr man.she (~(has in settings.she) %noob) gam)
|
=+ tay=~(. tr man.she (~(has in settings.she) %noob) gam)
|
||||||
=. ..sh-work (sh-fact %tan tr-tang:tay)
|
=. ..sh-work (sh-fact tr-fact:tay)
|
||||||
sh-prod(active.she `tr-pals:tay)
|
sh-prod(active.she `tr-pals:tay)
|
||||||
::
|
::
|
||||||
++ help |=(~ (sh-fact %mor talk-doc)) :: %help
|
++ help
|
||||||
|
(sh-fact %txt "see http://urbit.org/docs/user/talk")
|
||||||
|
::
|
||||||
++ glyph
|
++ glyph
|
||||||
|= idx=@
|
|= idx=@
|
||||||
=< cha.ole
|
=< cha.ole
|
||||||
@ -1597,7 +1596,7 @@
|
|||||||
pa-report-cabal
|
pa-report-cabal
|
||||||
::
|
::
|
||||||
++ pa-cancel :: unsubscribe from
|
++ pa-cancel :: unsubscribe from
|
||||||
~& [%pa-cancel ost.hid]
|
:: ~& [%pa-cancel ost.hid]
|
||||||
%_ .
|
%_ .
|
||||||
gramsers (~(del by gramsers) ost.hid)
|
gramsers (~(del by gramsers) ost.hid)
|
||||||
groupers (~(del in groupers) ost.hid)
|
groupers (~(del in groupers) ost.hid)
|
||||||
@ -1932,6 +1931,9 @@
|
|||||||
bou=bouquet
|
bou=bouquet
|
||||||
sep=speech
|
sep=speech
|
||||||
==
|
==
|
||||||
|
++ tr-fact ^- sole-effect :: activate effect
|
||||||
|
~[%mor [%tan tr-meta] tr-body]
|
||||||
|
::
|
||||||
++ tr-line ^- tape :: one-line print
|
++ tr-line ^- tape :: one-line print
|
||||||
=+ txt=(tr-text =(who our.hid))
|
=+ txt=(tr-text =(who our.hid))
|
||||||
?: =(~ txt) ""
|
?: =(~ txt) ""
|
||||||
@ -1943,8 +1945,7 @@
|
|||||||
(~(sn-nick sn man [who (main who)]))
|
(~(sn-nick sn man [who (main who)]))
|
||||||
(weld baw txt)
|
(weld baw txt)
|
||||||
::
|
::
|
||||||
++ tr-tang ^- tang
|
++ tr-meta ^- tang
|
||||||
%+ welp tr-sep-tang
|
|
||||||
=. wen (sub wen (mod wen (div wen ~s0..0001))) :: round
|
=. wen (sub wen (mod wen (div wen ~s0..0001))) :: round
|
||||||
=+ hed=leaf/"{(scow %uv sen)} at {(scow %da wen)}"
|
=+ hed=leaf/"{(scow %uv sen)} at {(scow %da wen)}"
|
||||||
=+ =< paz=(turn (~(tap by aud)) .)
|
=+ =< paz=(turn (~(tap by aud)) .)
|
||||||
@ -1952,18 +1953,17 @@
|
|||||||
=+ bok=(turn (sort (~(tap in bou)) aor) smyt)
|
=+ bok=(turn (sort (~(tap in bou)) aor) smyt)
|
||||||
[%rose [" " ~ ~] [hed >who< [%rose [", " "to " ~] paz] bok]]~
|
[%rose [" " ~ ~] [hed >who< [%rose [", " "to " ~] paz] bok]]~
|
||||||
::
|
::
|
||||||
++ tr-sep-tang
|
++ tr-body
|
||||||
|- ^- tang
|
|- ^- sole-effect
|
||||||
=< ?+(. . [@ *] [.]~) ^- ?(tank tang) :: wrap single tanks
|
?+ -.sep tan/[>sep<]~
|
||||||
?+ -.sep [>sep<]~
|
%exp tan/~[leaf/"# {(trip p.sep)}"]
|
||||||
%exp palm/[~ "#" " " ~]^~[leaf/(trip p.sep)]
|
%lin tan/~[leaf/"{?:(p.sep "" "@ ")}{(trip q.sep)}"]
|
||||||
%lin leaf/"{?:(p.sep "" "@ ")}{(trip q.sep)}"
|
%non tan/~
|
||||||
%non ~
|
%app tan/~[rose/[": " ~ ~]^~[leaf/"[{(trip p.sep)}]" leaf/(trip q.sep)]]
|
||||||
%app rose/[": " ~ ~]^~[leaf/"[{(trip p.sep)}]" leaf/(trip q.sep)]
|
%tax tan/~[leaf/(rend-work-duty p.sep)]
|
||||||
%tax leaf/(rend-work-duty p.sep)
|
%url url/(crip (earf p.sep))
|
||||||
%url palm/[~ "/" " " ~]^~[leaf/(earf p.sep)]
|
%mor mor/(turn p.sep |=(speech ^$(sep +<)))
|
||||||
%mor ?~(p.sep ~ (weld $(p.sep t.p.sep) $(sep i.p.sep)))
|
%fat [%mor tan/(tr-rend-tors p.sep) $(sep q.sep) ~]
|
||||||
%fat (welp (tr-rend-tors p.sep) $(sep q.sep))
|
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ tr-rend-tors
|
++ tr-rend-tors
|
||||||
@ -2111,7 +2111,7 @@
|
|||||||
++ pull ::
|
++ pull ::
|
||||||
|= [pax=path]
|
|= [pax=path]
|
||||||
^- [(list move) _+>]
|
^- [(list move) _+>]
|
||||||
~& [%talk-pull src.hid ost.hid pax]
|
:: ~& [%talk-pull src.hid ost.hid pax]
|
||||||
=^ moz +>.$ ra-abet:(ra-cancel:ra src.hid pax)
|
=^ moz +>.$ ra-abet:(ra-cancel:ra src.hid pax)
|
||||||
[moz +>.$(shells (~(del by shells) ost.hid))]
|
[moz +>.$(shells (~(del by shells) ost.hid))]
|
||||||
::
|
::
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
ins=(unit (list (pair path cage))) :: inserts
|
ins=(unit (list (pair path cage))) :: inserts
|
||||||
dig=(map path cage) :: store diffs
|
dig=(map path cage) :: store diffs
|
||||||
dif=(unit (list (trel path lobe cage))) :: changes
|
dif=(unit (list (trel path lobe cage))) :: changes
|
||||||
muc=(map path cage) :: store miso
|
muc=(map path cage) :: store mutations
|
||||||
muh=(map path lobe) :: store hashes
|
muh=(map path lobe) :: store hashes
|
||||||
mut=(unit (list (trel path lobe cage))) :: mutations
|
mut=(unit (list (trel path lobe cage))) :: mutations
|
||||||
mim=(map path mime) :: mime cache
|
mim=(map path mime) :: mime cache
|
||||||
@ -492,6 +492,60 @@
|
|||||||
$(p.lem t.p.lem)
|
$(p.lem t.p.lem)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
|
:: This is the entry point to the commit flow. It deserves some
|
||||||
|
:: explaining, since it's rather long and convoluted.
|
||||||
|
::
|
||||||
|
:: We take a `++nori`, which is either a label-add request or a `++soba`,
|
||||||
|
:: which is a list of changes. If it's a label, it's easy and we just pass
|
||||||
|
:: it to `++edit:ze`.
|
||||||
|
::
|
||||||
|
:: If the given `++nori` is a list of file changes, then we our goal is to
|
||||||
|
:: convert the list of `++miso` changes to `++misu` changes. In other
|
||||||
|
:: words, turn the `++nori` into a `++nuri`. Then, we pass it to
|
||||||
|
:: `++edit:ze`, which applies the changes to our state, and then we
|
||||||
|
:: check out the new revision. XX reword
|
||||||
|
::
|
||||||
|
:: Anyhow, enough of high-level wishy-washy talk. It's time to get down to
|
||||||
|
:: the nitty-gritty.
|
||||||
|
::
|
||||||
|
:: When we get a list of `++miso` changes, we split them into four types:
|
||||||
|
:: deletions, insertions, diffs (i.e. change from diff), and mutations
|
||||||
|
:: (i.e. change from new data). We do four different things with them.
|
||||||
|
::
|
||||||
|
:: For deletions, we just fill in `del` in `++dork` with a list of the
|
||||||
|
:: deleted files.
|
||||||
|
::
|
||||||
|
:: For insertions, we distinguish bewtween `%hoon` files and all other
|
||||||
|
:: files. For `%hoon` files, we just store them to `ink` in `++dork` so
|
||||||
|
:: that we add diff them directly. `%hoon` files have to be treated
|
||||||
|
:: specially to make the bootstrapping sequence work, since the mark
|
||||||
|
:: definitions are themselves `%hoon` files.
|
||||||
|
::
|
||||||
|
:: For the other files, we make a `%tabl` compound ford request to convert
|
||||||
|
:: the data for the new file to the the mark indicated by the last span in
|
||||||
|
:: the path.
|
||||||
|
::
|
||||||
|
:: For diffs, we make a `%tabl` compound ford request to apply the diff to
|
||||||
|
:: the existing content. We also store the diffs in `dig` in `++dork`.
|
||||||
|
::
|
||||||
|
:: For mutations, we make a `%tabl` compound ford request to convert the
|
||||||
|
:: given new data to the mark of the already-existing file. Later on in
|
||||||
|
:: `++take-castify` we'll create the ford request to actually perform the
|
||||||
|
:: diff. We also store the mutations in `muc` in `++dork`. I'm pretty
|
||||||
|
:: sure that's useless because who cares about the original data.
|
||||||
|
:: XX delete `muc`.
|
||||||
|
::
|
||||||
|
:: Finally, for performance reasons we cache any of the data that came in
|
||||||
|
:: as a `%mime` cage. We do this because many commits come from unix,
|
||||||
|
:: where they're passed in as `%mime` and need to be turned back into it
|
||||||
|
:: for the ergo. We cache both `%hoon` and non-`%hoon` inserts and
|
||||||
|
:: mutations.
|
||||||
|
::
|
||||||
|
:: At this point, the flow of control goes through the three ford requests
|
||||||
|
:: back to `++take-inserting`, `++take-diffing`, and `++take-castifying`,
|
||||||
|
:: which itself leads to `++take-mutating`. Once each of those has
|
||||||
|
:: completed, we end up at `++apply-edit`, where our unified story picks up
|
||||||
|
:: again.
|
||||||
++ edit :: apply changes
|
++ edit :: apply changes
|
||||||
|= [wen=@da lem=nori]
|
|= [wen=@da lem=nori]
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -608,31 +662,7 @@
|
|||||||
==
|
==
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ silkify
|
:: See ++edit for a description of the commit flow.
|
||||||
|= [wen=@da pax=path mis=miso]
|
|
||||||
^- [duct path note]
|
|
||||||
~| [%silkifying pax -.mis]
|
|
||||||
:- hen
|
|
||||||
?+ -.mis !!
|
|
||||||
%mut
|
|
||||||
:- [%diffing (scot %p her) syd (scot %da wen) pax]
|
|
||||||
:^ %f %exec our :+ ~ [her syd %da wen]
|
|
||||||
^- silk
|
|
||||||
:+ %diff
|
|
||||||
(lobe-to-silk:ze pax (~(got by q:(aeon-to-yaki:ze let.dom)) pax))
|
|
||||||
=+ (slag (dec (lent pax)) pax)
|
|
||||||
=+ ?~(- %$ i.-)
|
|
||||||
[%cast - [%$ p.mis]]
|
|
||||||
::
|
|
||||||
%ins
|
|
||||||
:- [%casting (scot %p her) syd (scot %da wen) pax]
|
|
||||||
:^ %f %exec our :+ ~ [her syd %da wen]
|
|
||||||
^- silk
|
|
||||||
=+ (slag (dec (lent pax)) pax)
|
|
||||||
=+ ?~(- %$ i.-)
|
|
||||||
[%cast - [%$ p.mis]]
|
|
||||||
==
|
|
||||||
::
|
|
||||||
++ apply-edit
|
++ apply-edit
|
||||||
|= wen=@da
|
|= wen=@da
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -664,6 +694,7 @@
|
|||||||
([echo(dok ~)]:.(+>.$ +.hat) wen %& sim)
|
([echo(dok ~)]:.(+>.$ +.hat) wen %& sim)
|
||||||
(checkout-ankh(lat.ran lat.ran.+.hat) u.-.hat)
|
(checkout-ankh(lat.ran lat.ran.+.hat) u.-.hat)
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ take-inserting
|
++ take-inserting
|
||||||
|= [wen=@da res=gage]
|
|= [wen=@da res=gage]
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -684,6 +715,7 @@
|
|||||||
~|(%clay-take-inserting-strange-path-mark !!)
|
~|(%clay-take-inserting-strange-path-mark !!)
|
||||||
[((hard path) q.q.pax) cay]
|
[((hard path) q.q.pax) cay]
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ take-diffing
|
++ take-diffing
|
||||||
|= [wen=@da res=gage]
|
|= [wen=@da res=gage]
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -706,6 +738,7 @@
|
|||||||
=+ paf=((hard path) q.q.pax)
|
=+ paf=((hard path) q.q.pax)
|
||||||
[paf (page-to-lobe:ze [p q.q]:cay) (~(got by dig.u.dok) paf)]
|
[paf (page-to-lobe:ze [p q.q]:cay) (~(got by dig.u.dok) paf)]
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ take-castify
|
++ take-castify
|
||||||
|= [wen=@da res=gage]
|
|= [wen=@da res=gage]
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -736,6 +769,7 @@
|
|||||||
[%diff - [%$ cay]]
|
[%diff - [%$ cay]]
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ take-mutating
|
++ take-mutating
|
||||||
|= [wen=@da res=gage]
|
|= [wen=@da res=gage]
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -760,6 +794,7 @@
|
|||||||
=+ paf=((hard path) q.q.pax)
|
=+ paf=((hard path) q.q.pax)
|
||||||
`[paf (~(got by muh.u.dok) paf) cay]
|
`[paf (~(got by muh.u.dok) paf) cay]
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ take-patch
|
++ take-patch
|
||||||
|= res=gage
|
|= res=gage
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -844,6 +879,7 @@
|
|||||||
(lobe-to-silk:ze a p.-)
|
(lobe-to-silk:ze a p.-)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ take-ergo
|
++ take-ergo
|
||||||
|= res=gage
|
|= res=gage
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -870,6 +906,7 @@
|
|||||||
[(slag len pax) (~(got by can) pax)]
|
[(slag len pax) (~(got by can) pax)]
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
|
:: See ++edit for a description of the commit flow.
|
||||||
++ checkout-ankh
|
++ checkout-ankh
|
||||||
|= hat=(map path lobe)
|
|= hat=(map path lobe)
|
||||||
^+ +>
|
^+ +>
|
||||||
@ -2692,7 +2729,6 @@
|
|||||||
..^$(ruf ruf.old)
|
..^$(ruf ruf.old)
|
||||||
::
|
::
|
||||||
++ scry :: inspect
|
++ scry :: inspect
|
||||||
:: Make a request
|
|
||||||
|= [fur=(unit (set monk)) ren=@tas his=ship syd=desk lot=coin tyl=path]
|
|= [fur=(unit (set monk)) ren=@tas his=ship syd=desk lot=coin tyl=path]
|
||||||
^- (unit (unit cage))
|
^- (unit (unit cage))
|
||||||
:: ~& scry/[ren `path`[(scot %p his) syd ~(rent co lot) tyl]]
|
:: ~& scry/[ren `path`[(scot %p his) syd ~(rent co lot) tyl]]
|
||||||
|
@ -1388,7 +1388,7 @@
|
|||||||
%plan (cope (abut:(meow p.kas q.kas) cof r.kas) faun)
|
%plan (cope (abut:(meow p.kas q.kas) cof r.kas) faun)
|
||||||
%reef (faun cof pit)
|
%reef (faun cof pit)
|
||||||
%ride
|
%ride
|
||||||
%+ cool |.(leaf/"ford: ride {<`@p`(mug kas)>}")
|
%+ cool |.(leaf/"ford: build failed}")
|
||||||
%+ cope $(kas q.kas)
|
%+ cope $(kas q.kas)
|
||||||
%- tabl-run
|
%- tabl-run
|
||||||
|= [cof=cafe cay=cage]
|
|= [cof=cafe cay=cage]
|
||||||
|
@ -673,7 +673,7 @@
|
|||||||
?. (~(has by sup.ged) ost) .
|
?. (~(has by sup.ged) ost) .
|
||||||
=+ soy=(~(get by qel.ged) ost)
|
=+ soy=(~(get by qel.ged) ost)
|
||||||
?: |(?=(~ soy) =(0 u.soy))
|
?: |(?=(~ soy) =(0 u.soy))
|
||||||
~& [%ap-fill-under [our dap] q.q.pry ost]
|
:: ~& [%ap-fill-under [our dap] q.q.pry ost]
|
||||||
+
|
+
|
||||||
=. u.soy (dec u.soy)
|
=. u.soy (dec u.soy)
|
||||||
:: ~& [%ap-fill-sub [[our dap] q.q.pry ost] u.soy]
|
:: ~& [%ap-fill-sub [[our dap] q.q.pry ost] u.soy]
|
||||||
@ -1049,7 +1049,7 @@
|
|||||||
::
|
::
|
||||||
++ ap-pull :: load delete
|
++ ap-pull :: load delete
|
||||||
=+ wim=(~(get by sup.ged) ost)
|
=+ wim=(~(get by sup.ged) ost)
|
||||||
?~ wim ~&(%ap-pull-none +)
|
?~ wim + :: ~&(%ap-pull-none +)
|
||||||
=: sup.ged (~(del by sup.ged) ost)
|
=: sup.ged (~(del by sup.ged) ost)
|
||||||
qel.ged (~(del by qel.ged) ost)
|
qel.ged (~(del by qel.ged) ost)
|
||||||
==
|
==
|
||||||
@ -1062,7 +1062,7 @@
|
|||||||
+>+
|
+>+
|
||||||
::
|
::
|
||||||
++ ap-kill :: queue kill
|
++ ap-kill :: queue kill
|
||||||
~& [%ap-kill dap ost]
|
:: ~& [%ap-kill dap ost]
|
||||||
(ap-give:ap-pull %quit ~)
|
(ap-give:ap-pull %quit ~)
|
||||||
::
|
::
|
||||||
++ ap-take :: non-diff gall take
|
++ ap-take :: non-diff gall take
|
||||||
@ -1070,7 +1070,7 @@
|
|||||||
^+ +>
|
^+ +>
|
||||||
=+ cug=(ap-find cog pax)
|
=+ cug=(ap-find cog pax)
|
||||||
?~ cug
|
?~ cug
|
||||||
~& [%ap-take-none cog pax]
|
:: ~& [%ap-take-none cog pax]
|
||||||
+>.$
|
+>.$
|
||||||
=^ cam +>.$
|
=^ cam +>.$
|
||||||
%+ ap-call q.u.cug
|
%+ ap-call q.u.cug
|
||||||
|
@ -2389,6 +2389,7 @@
|
|||||||
[%mor ~] :: newline
|
[%mor ~] :: newline
|
||||||
[%sag p=path q=*] :: save to jamfile
|
[%sag p=path q=*] :: save to jamfile
|
||||||
[%sav p=path q=@] :: save to file
|
[%sav p=path q=@] :: save to file
|
||||||
|
[%url p=@t] :: activate url
|
||||||
== ::
|
== ::
|
||||||
++ dill-belt :: new belt
|
++ dill-belt :: new belt
|
||||||
$% [%aro p=?(%d %l %r %u)] :: arrow key
|
$% [%aro p=?(%d %l %r %u)] :: arrow key
|
||||||
@ -2413,6 +2414,7 @@
|
|||||||
[%out p=(list ,@c)] :: send output line
|
[%out p=(list ,@c)] :: send output line
|
||||||
[%sag p=path q=*] :: save to jamfile
|
[%sag p=path q=*] :: save to jamfile
|
||||||
[%sav p=path q=@] :: save to file
|
[%sav p=path q=@] :: save to file
|
||||||
|
[%url p=@t] :: activate url
|
||||||
== ::
|
== ::
|
||||||
++ flog :: sent to %dill
|
++ flog :: sent to %dill
|
||||||
$% [%crud p=@tas q=(list tank)] ::
|
$% [%crud p=@tas q=(list tank)] ::
|
||||||
@ -2427,6 +2429,7 @@
|
|||||||
++ gift-dill :: out result <-$
|
++ gift-dill :: out result <-$
|
||||||
$% [%bbye ~] :: reset prompt
|
$% [%bbye ~] :: reset prompt
|
||||||
[%blit p=(list blit)] :: terminal output
|
[%blit p=(list blit)] :: terminal output
|
||||||
|
[%burl p=@t] :: activate url
|
||||||
[%init p=@p] :: set owner
|
[%init p=@p] :: set owner
|
||||||
[%logo ~] :: logout
|
[%logo ~] :: logout
|
||||||
[%mass p=mass] :: memory usage
|
[%mass p=mass] :: memory usage
|
||||||
|
@ -663,6 +663,7 @@
|
|||||||
%sag +>(+> (se-blit fec))
|
%sag +>(+> (se-blit fec))
|
||||||
%sav +>(+> (se-blit fec))
|
%sav +>(+> (se-blit fec))
|
||||||
%txt $(fec [%tan [%leaf p.fec]~])
|
%txt $(fec [%tan [%leaf p.fec]~])
|
||||||
|
%url +>(+> (se-blit fec))
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ ta-dog :: change cursor
|
++ ta-dog :: change cursor
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
---
|
---
|
||||||
logo: black
|
logo: black
|
||||||
title: Hoon
|
|
||||||
sort: 2
|
sort: 2
|
||||||
|
title: Hoon
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="short">
|
<div class="short">
|
||||||
|
|
||||||
# Hoon
|
# Hoon
|
||||||
|
|
||||||
Hoon is a strict, typed, pure functional language. This site is served by an
|
Hoon is a strict, typed, pure functional language. This site is
|
||||||
urbit written in Hoon.
|
served by an urbit written in Hoon.
|
||||||
|
|
||||||
We're still developing the documentation; start with the
|
If you're interested in learning the fundamentals of Hoon from
|
||||||
bottom-up tutorial, ["Hoon 101"](hoon/tutorial).
|
the bottom up, start with [Principles of
|
||||||
|
Hoon](hoon/tutorial).
|
||||||
|
|
||||||
|
If you want to jump into building things right away and prefer to
|
||||||
|
learn from the top down, check out [Leap into
|
||||||
|
Hoon](hoon/jump-in/1-basic).
|
||||||
|
|
||||||
|
Both of these are under active development.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
420
pub/docs/dev/hoon/leap-in/1-basic.mdy
Normal file
420
pub/docs/dev/hoon/leap-in/1-basic.mdy
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
---
|
||||||
|
logo: black
|
||||||
|
sort: 1
|
||||||
|
title: Basic Hoon
|
||||||
|
---
|
||||||
|
|
||||||
|
# Basic Hoon
|
||||||
|
|
||||||
|
Our goal is to get you programming interesting and useful things
|
||||||
|
as soon as possible. To get there we have to quickly cover some
|
||||||
|
of the fundamentals of hoon. To do this we'll walk through two
|
||||||
|
simple programs: the first [Project
|
||||||
|
Euler](https://projecteuler.net/) problem and
|
||||||
|
[fizzbuzz](https://en.wikipedia.org/wiki/Fizz_buzz).
|
||||||
|
|
||||||
|
To run this code, you'll need an urbit, and you'll need the
|
||||||
|
`%examples` desk from `~wactex-ribmex`. If you haven't installed
|
||||||
|
urbit yet, check out the [installation
|
||||||
|
instructions](http://urbit.org/docs/user/install). Once urbit is
|
||||||
|
intalled, take a look at the [basic
|
||||||
|
operation](http://urbit.org/docs/user/basic) of your urbit.
|
||||||
|
|
||||||
|
If you haven't pulled the examples desk from `~wactex-ribmex`, do
|
||||||
|
so now:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> |merge %examples ~wactex-ribmex %examples
|
||||||
|
>=
|
||||||
|
; ~wactex-ribmex is your neighbor
|
||||||
|
; ~wactex-ribmex is your neighbor
|
||||||
|
[time passes...]
|
||||||
|
merged with strategy %init
|
||||||
|
|
||||||
|
The merge could take several minutes; you'll know it's done when
|
||||||
|
"merged with strategy %init" is printed. Mount the new files to
|
||||||
|
your Unix pier directory:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> |mount /=examples=
|
||||||
|
|
||||||
|
Switch desks to run commands from the `%examples` desk:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> =dir /=examples
|
||||||
|
=% /~fintud-macrep/examples/~2015.11.13..02.25.00..41e9/
|
||||||
|
|
||||||
|
Run an example:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> +euler1
|
||||||
|
233.168
|
||||||
|
|
||||||
|
## Euler 1
|
||||||
|
|
||||||
|
Let's check out the code for Euler 1. First, the problem:
|
||||||
|
|
||||||
|
```
|
||||||
|
If we list all the natural numbers below 10 that are multiples of
|
||||||
|
3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
|
||||||
|
|
||||||
|
Find the sum of all the multiples of 3 or 5 below 1000.
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the hoon solution (which should be in your pier directory
|
||||||
|
under `/examples/gen/euler1.hoon`):
|
||||||
|
|
||||||
|
```
|
||||||
|
:: project euler 1
|
||||||
|
:: https://projecteuler.net/problem=1
|
||||||
|
:: run in dojo with +euler1
|
||||||
|
::
|
||||||
|
:::: /hoon/euler1/gen
|
||||||
|
::
|
||||||
|
:- %say |= *
|
||||||
|
:- %noun
|
||||||
|
=< (sum 1.000)
|
||||||
|
::
|
||||||
|
:::: ~fintud-macrep
|
||||||
|
::
|
||||||
|
|%
|
||||||
|
++ three
|
||||||
|
|= a=@
|
||||||
|
=| b=@
|
||||||
|
|- ^- @u
|
||||||
|
?: (lth a b)
|
||||||
|
0
|
||||||
|
(add b $(b (add 3 b)))
|
||||||
|
|
||||||
|
++ five
|
||||||
|
|= a=@
|
||||||
|
=| b=@
|
||||||
|
|- ^- @
|
||||||
|
?: (lte a b)
|
||||||
|
0
|
||||||
|
?: =((mod b 3) 0)
|
||||||
|
$(b (add b 5))
|
||||||
|
(add b $(b (add b 5)))
|
||||||
|
|
||||||
|
++ sum
|
||||||
|
|= a=@u
|
||||||
|
(add (five a) (three a))
|
||||||
|
--
|
||||||
|
```
|
||||||
|
|
||||||
|
> Hoon is not generally whitespace sensitive, but we do have two
|
||||||
|
> different kinds of whitespace: a single space and a gap, which
|
||||||
|
> is two or more spaces or a linebreak. Tabs are taboo. Do not
|
||||||
|
> use them. Really. For a more detailed explanation of when to
|
||||||
|
> use spaces vs. gaps, see the syntax section before the first
|
||||||
|
> excercises.
|
||||||
|
|
||||||
|
### Lines 1-11:
|
||||||
|
|
||||||
|
Any line that begins with `::` is a comment.
|
||||||
|
|
||||||
|
:- %say |= *
|
||||||
|
:- %noun
|
||||||
|
=< (sum 1.000)
|
||||||
|
|
||||||
|
All you need to know about the lines above is that they call the
|
||||||
|
`++sum` function with an argument of `1.000`. We'll cover them in
|
||||||
|
more detail later.
|
||||||
|
|
||||||
|
### How to form expressions
|
||||||
|
|
||||||
|
Hoon does not use reserved words to form expressions. Instead,
|
||||||
|
expressions are formed with runes, which are diagraphs of two
|
||||||
|
ascii symbols. Each rune takes a specific number of
|
||||||
|
children--either expressions formed by other runes or literals
|
||||||
|
that produce their own value.
|
||||||
|
|
||||||
|
For example, the rune `?:` from line 17 is the classic
|
||||||
|
'if-then-else' statement, and thus takes three children:
|
||||||
|
|
||||||
|
?: (lth a b) :: if first child evals to true
|
||||||
|
0 :: then produce result of second
|
||||||
|
(add b $(b (add 3 b))) :: else, produce result of third
|
||||||
|
|
||||||
|
Since runes are such a fundamental structure in Hoon, we found
|
||||||
|
ourselves speaking them out loud frequently. It quickly grew
|
||||||
|
cumbersome to have to say "question mark, colon" to describe
|
||||||
|
`?:`. To alleviate this problem, we came up with our own naming
|
||||||
|
scheme: each ascii glyph has a single syllable pronunciation
|
||||||
|
phonetically designed to be both easily remembered and easily
|
||||||
|
pronounced in conjunction with the other glyphs (when forming a
|
||||||
|
rune).
|
||||||
|
|
||||||
|
See the entire naming schema below/or link to it:
|
||||||
|
|
||||||
|
```
|
||||||
|
ace [1 space] gal < pel (
|
||||||
|
bar | gap [>1 space, nl] per )
|
||||||
|
bas \ gar > sel [
|
||||||
|
buc $ hax # sem ;
|
||||||
|
cab _ hep - ser ]
|
||||||
|
cen % kel { soq '
|
||||||
|
col : ker } tar *
|
||||||
|
com , ket ^ tec `
|
||||||
|
doq " lus + tis =
|
||||||
|
dot . pam & wut ?
|
||||||
|
fas / pat @ zap !
|
||||||
|
```
|
||||||
|
|
||||||
|
Using our naming scheme `?:` is said 'wut col'.
|
||||||
|
|
||||||
|
### Lines 12-34
|
||||||
|
|
||||||
|
Now let's quickly walk through this code line-by-line. Lines
|
||||||
|
12-34 are wrapped in a `|%` (pronounced 'bar cen'), which
|
||||||
|
produces a core, a fundamental datatype in hoon similar to a
|
||||||
|
struct, class, or object. A core is just a map of names
|
||||||
|
to any kind of code, whether it be functions or data. Each
|
||||||
|
element in this map begins with a `++` followed by the name and
|
||||||
|
the corresponding code. Since `|%` takes an arbitrary number of
|
||||||
|
children, it needs to be closed with a `--`.
|
||||||
|
|
||||||
|
> `++` is not technically a rune, since it is only used in core
|
||||||
|
> syntax as shown above
|
||||||
|
|
||||||
|
Let's step into each of the three arms within our core.
|
||||||
|
|
||||||
|
### `++ sum`
|
||||||
|
|
||||||
|
++ sum
|
||||||
|
|= a=@
|
||||||
|
(add (five a) (three a))
|
||||||
|
--
|
||||||
|
|
||||||
|
`|=` produces a function, much like a lambda in lisp. It takes two children:
|
||||||
|
|
||||||
|
- A set of argument(s). In this case our argument set only
|
||||||
|
contains one: `a` which is required to be an atom or natural
|
||||||
|
number, denoted by `@`.
|
||||||
|
|
||||||
|
- The body of the function itself, which is executed when the
|
||||||
|
function is called (in this case, with `(sum 1.000)`). This
|
||||||
|
particular function adds the results of evaluating the gates `++
|
||||||
|
five` and `++three` with each of their respective input
|
||||||
|
parameters set to `a`.
|
||||||
|
|
||||||
|
### ++ three
|
||||||
|
|
||||||
|
++ three
|
||||||
|
|= a=@
|
||||||
|
=| b=@
|
||||||
|
|- ^- @u
|
||||||
|
?: (lth a b)
|
||||||
|
0
|
||||||
|
(add b $(b (add 3 b)))
|
||||||
|
|
||||||
|
As above, `++three` takes an integer argument, `a`, and then
|
||||||
|
executes the remainder of the code with `a` set to the actual
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
Similarly, `=|` pushes its first child, `b` into our context (in
|
||||||
|
other words, it declares a variable `b`) and executes the
|
||||||
|
remainder of the code. However, `b` is not an argument; `=|`
|
||||||
|
sets `b` to the default value of whatever type it is declared as.
|
||||||
|
Since the default value of an atom is `0`, b is set to `0`.
|
||||||
|
|
||||||
|
So now we have two variables: `a` is set to our input, and `b` is
|
||||||
|
initialized to `0`.
|
||||||
|
|
||||||
|
The easiest way to think about `|-` that it lays down a recursion
|
||||||
|
point. More on this later.
|
||||||
|
|
||||||
|
`^-` is just a cast that sets the result of the remainder of the
|
||||||
|
code to an unsigned integer, `@u`.
|
||||||
|
|
||||||
|
In pseudocode, the last three lines read like this: if `a` is
|
||||||
|
less than `b`, produce zero. Else, add `b` to the result of
|
||||||
|
rerunning the segment of the function following the `|-` with the
|
||||||
|
value of `b` changed to `b` plus three.
|
||||||
|
|
||||||
|
The only thing that should look completely unfamiliar to you here
|
||||||
|
is the `$(b (add 3 b))`, which causes us to recurse back to our
|
||||||
|
last recursion point with the value of `b` set to `(add 3 b)`.
|
||||||
|
Note that we only specify what changes (`b` in this case). If
|
||||||
|
you recurse by an actual function call, then you have to specify
|
||||||
|
every argument.
|
||||||
|
|
||||||
|
> If you're familiar with Clojure, `|-` is `loop` and `$()` is
|
||||||
|
> recur.
|
||||||
|
|
||||||
|
|
||||||
|
## Excercises
|
||||||
|
|
||||||
|
Please tweak your code to complete the following excercises.
|
||||||
|
|
||||||
|
There are a few runes and some syntax that we have yet to cover that
|
||||||
|
you will need to complete the excercises below. For these, please
|
||||||
|
refer to our cheatsheat at the bottom.
|
||||||
|
|
||||||
|
1. Read and understand `++five` line by line.
|
||||||
|
|
||||||
|
2. Change `++sum` to accept two variables, `a` and `b`. Pass `a`
|
||||||
|
to three and `b` to five. Then run the code with `a` set to
|
||||||
|
`1.000` and b set to `2.000`.
|
||||||
|
|
||||||
|
3. Check if this new result is under one thousand. If it is,
|
||||||
|
return the string 'result is less than one thousand'. If not,
|
||||||
|
return 'result is greater than or equal to one thousand'.
|
||||||
|
|
||||||
|
```
|
||||||
|
Review
|
||||||
|
|
||||||
|
|% start core (collection of named ++ arms)
|
||||||
|
|= define function
|
||||||
|
=| define variable from type with default value
|
||||||
|
|- drop a recursion point
|
||||||
|
^- cast
|
||||||
|
?: if-then-else
|
||||||
|
=(a b) test equality
|
||||||
|
(function args ...) call function
|
||||||
|
|
||||||
|
New material
|
||||||
|
|
||||||
|
- :- make a cell of values. The irregular wide form of this is
|
||||||
|
[a b] with two expressions separated by a single space.
|
||||||
|
|
||||||
|
- Cords are one datatype for text in hoon. They're just a big
|
||||||
|
atom formed from adjacent unicode bytes -- a "c string". To
|
||||||
|
produce a cord enclose text within single quotes. To set the type
|
||||||
|
of an argument to a cord, use @t.
|
||||||
|
|
||||||
|
- There are two syntaxes for writing Hoon: tall form and wide
|
||||||
|
form.
|
||||||
|
|
||||||
|
In tall form, expressions are formed with either two spaces or
|
||||||
|
a line break separating both a rune from its children and each
|
||||||
|
of its children from one another. We use tall form when writing
|
||||||
|
multiline expressions.
|
||||||
|
|
||||||
|
For more concise expressions, we use wideform, which is always
|
||||||
|
a single line. Wideform can be used inside tall form
|
||||||
|
expressions, but not vice versa.
|
||||||
|
|
||||||
|
Wideform expressions are formed with a rune followed by ()
|
||||||
|
containing its children, all of which are separated by a
|
||||||
|
single space. For example to make a cell of two elements:
|
||||||
|
|
||||||
|
:-(a b)
|
||||||
|
|
||||||
|
We've already seen wideform in action, for example with
|
||||||
|
=((mod b 3) 0). In this case = is actually an irregular form
|
||||||
|
of .=, which tests its two children for equality.
|
||||||
|
|
||||||
|
Another irregular form is [a b] for :-(a b)
|
||||||
|
|
||||||
|
Surrounding a function with () is an irregular wide form
|
||||||
|
syntax for calling a function with n arguments.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## The subject
|
||||||
|
|
||||||
|
Now we're going to cover the boiler plate that we skimmed over
|
||||||
|
earlier.
|
||||||
|
|
||||||
|
:- %say |= *
|
||||||
|
:- %noun
|
||||||
|
=< (sum [1.000 2.000])
|
||||||
|
|
||||||
|
This program is a cell of two elements: the first, `%say`, tells
|
||||||
|
the interpreter what to produce--in this case a value.
|
||||||
|
|
||||||
|
The second element is `|=`, which we know produces a function.
|
||||||
|
`|=`'s first child is its argument(s), which in this case is any
|
||||||
|
noun (`*`). Its second child is the remainder of the program.
|
||||||
|
|
||||||
|
Similarly, the rest of the program is a cell of the literal
|
||||||
|
`%noun`, which tells the shell that we're producing a value of
|
||||||
|
type `noun`, and the second child contains the code that we run
|
||||||
|
to actually produce our value of the type `noun`.
|
||||||
|
|
||||||
|
`=<` is a rune that takes two children. The second child is the
|
||||||
|
context against which we run the first child. So in this case, we
|
||||||
|
are running the expression `(sum 1.000)` against everything
|
||||||
|
contained within the `|%`. In Hoon, we call the code executed the
|
||||||
|
"formula" and its context the "subject".
|
||||||
|
|
||||||
|
```
|
||||||
|
::::::::::::::::::::::::::::::
|
||||||
|
=< (sum 1.000) :: formula
|
||||||
|
::::::::::::::::::::::::::::::
|
||||||
|
|% ::
|
||||||
|
++ three ::
|
||||||
|
|= a=@ ::
|
||||||
|
=| b=@ ::
|
||||||
|
|- ^- @u ::
|
||||||
|
?: (lth a b) ::
|
||||||
|
0 ::
|
||||||
|
(add b $(b (add 3 b))) ::
|
||||||
|
::
|
||||||
|
++ five ::
|
||||||
|
|= a=@ :: subject
|
||||||
|
=| b=@ ::
|
||||||
|
|- ^- @ ::
|
||||||
|
?: (lte a b) ::
|
||||||
|
0 ::
|
||||||
|
?: =((mod b 3) 0) ::
|
||||||
|
$(b (add b 5)) ::
|
||||||
|
(add b $(b (add b 5))) ::
|
||||||
|
::
|
||||||
|
++ sum ::
|
||||||
|
|= a=@u ::
|
||||||
|
(add (five a) (three a)) ::
|
||||||
|
-- ::
|
||||||
|
::::::::::::::::::::::::::::::
|
||||||
|
```
|
||||||
|
|
||||||
|
In nearly every language there is a similar concept of a
|
||||||
|
"context" in which expressions are executed. For example, in C
|
||||||
|
this includes things like the call stack, stack variables, and so
|
||||||
|
on.
|
||||||
|
|
||||||
|
Hoon is unique in that this context is a first-class value.
|
||||||
|
Scheme allows a sort of reification of the context through
|
||||||
|
continutations, and some may see a parallel to Forth's stack, but
|
||||||
|
Hoon takes takes the concept one step further.
|
||||||
|
|
||||||
|
Our starting subject is the standard library, which is defined in
|
||||||
|
`/arvo/hoon.hoon` and `/arvo/zuse.hoon`. This is where functions
|
||||||
|
like `add` are defined. When we define a core with `|%`, we
|
||||||
|
don't throw away the subject (i.e. the standard library); rather,
|
||||||
|
we stack the new core on top of the old subject so that both are
|
||||||
|
accessible.
|
||||||
|
|
||||||
|
## Exercises:
|
||||||
|
|
||||||
|
4. Pass `++sum` its arguments (`2000` and `3000`) from the
|
||||||
|
commandline.
|
||||||
|
|
||||||
|
5. Comment out all of the arms of the `|%`. Now add another arm
|
||||||
|
and call it `++add`, have it accept two arguments and procduce
|
||||||
|
42 (regardless of input). Change the `=<` line to `[(add 5 7)
|
||||||
|
(^add 5 7)]`. Can you recognize what's happening?
|
||||||
|
|
||||||
|
6. Write fizbuzz:
|
||||||
|
|
||||||
|
Write a program that prints the numbers from 1 to 100
|
||||||
|
(entered from the command line). But for multiples of three
|
||||||
|
print 'Fizz' instead of the number and for the multiples of
|
||||||
|
five print 'Buzz'. For numbers which are multiples of both
|
||||||
|
three and five print 'FizzBuzz'.
|
||||||
|
|
||||||
|
Cheatsheet:
|
||||||
|
|
||||||
|
- To pass arguments from the command line to a program, you
|
||||||
|
replace the `*` in the first line of the boiler plate to
|
||||||
|
`[^ [[arg=TYPE ~] ~]]` where `TYPE` is replaced with the
|
||||||
|
type of argument you're expecting. Then `+euler1 a` from
|
||||||
|
the dojo sets `arg` to `a`.
|
||||||
|
- A list of strings is of type `(list ,@t)`, so the result of
|
||||||
|
the fizzbuzz function is of this type (hint: you'll need to
|
||||||
|
use `^-`)
|
||||||
|
- The empty list is `~`
|
||||||
|
- Lisp-style cons (construct a cell/prepend an element) is
|
||||||
|
`[new-element list]`
|
||||||
|
- For example, the first three positive integers are `[1 2 3
|
||||||
|
~]`
|
||||||
|
- `gte` tests whether `a` is greater than or equal to `b`.
|
||||||
|
- `mod` runs the modulo operation on two atoms.
|
||||||
|
- See the [basic math section]() for more info.
|
188
pub/docs/dev/hoon/leap-in/2-network.mdy
Normal file
188
pub/docs/dev/hoon/leap-in/2-network.mdy
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
---
|
||||||
|
hide: true
|
||||||
|
next: false
|
||||||
|
sort: 3
|
||||||
|
title: Network Messages
|
||||||
|
---
|
||||||
|
|
||||||
|
Enough of pure hoonery. Let's get to the good stuff. Let's get
|
||||||
|
our planets to talk to each other.
|
||||||
|
|
||||||
|
Of course, for talking to be of any use, we need someone
|
||||||
|
listening. What we've written up until now are just shell
|
||||||
|
commands that produce a value and then disappear. We need an
|
||||||
|
actual app to listen for messages from another planet. Let's
|
||||||
|
take a look at a very basic one.
|
||||||
|
|
||||||
|
```
|
||||||
|
:: There is no love that is not an echo
|
||||||
|
::
|
||||||
|
:::: /hoon/echo/ape
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
|%
|
||||||
|
++ move ,*
|
||||||
|
--
|
||||||
|
!:
|
||||||
|
|_ [bowl state=~]
|
||||||
|
++ poke-noun
|
||||||
|
|= arg=*
|
||||||
|
^- [(list move) _+>.$]
|
||||||
|
~& [%argument arg]
|
||||||
|
[~ +>.$]
|
||||||
|
--
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a very simple app that does only one thing. If you poke
|
||||||
|
it with a value it prints that out. You have to start the app,
|
||||||
|
then you can poke it from the command line.
|
||||||
|
|
||||||
|
```
|
||||||
|
> |start %echo
|
||||||
|
>=
|
||||||
|
> :echo 5
|
||||||
|
[%argument 5]
|
||||||
|
> :echo [1 2]
|
||||||
|
[%argument [1 2]]
|
||||||
|
```
|
||||||
|
|
||||||
|
Most of the app code should be simple enough to guess its
|
||||||
|
function. The important part of this code is the definition of
|
||||||
|
`++poke-noun`.
|
||||||
|
|
||||||
|
Once an app starts, it's always on in the background, and you
|
||||||
|
interact with it by sending it messages. The most
|
||||||
|
straightforward way to do that is to poke it from the command
|
||||||
|
line. When you do that, `++poke-noun` is called from your app.
|
||||||
|
|
||||||
|
In our case, `++poke-noun` takes an argument `arg` and prints it
|
||||||
|
out with `~&`. This is an unusual rune that formally "does
|
||||||
|
nothing", but the interpreter detects it and printfs the first
|
||||||
|
child. This is a slightly hacky way of printing to the console,
|
||||||
|
and we'll get to the correct way later on.
|
||||||
|
|
||||||
|
But what does `++poke-noun` produce? The phrase to remember is
|
||||||
|
"a list of moves and our state". Urbit is a message passing
|
||||||
|
system, so whenver we want to do something that interacts with
|
||||||
|
the rest of the system we send a message. Thus, the first thing
|
||||||
|
that `++poke-noun` produces is a list of messages, called
|
||||||
|
"moves". In this case, we don't actually want the system to do
|
||||||
|
anything, so we produce the empty list, `~`.
|
||||||
|
|
||||||
|
The second thing `++poke-noun` produces is our state. `+>.$`
|
||||||
|
refers to a particular address in our subject where our formal
|
||||||
|
app state is stored. It'll become clear why this is later on,
|
||||||
|
but for now pretend that `+>.$` is a magic invocation that means
|
||||||
|
"app state".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
But what is our app state, exactly? In Unix systems, application
|
||||||
|
state is just a block of memory, which you need to serialize to
|
||||||
|
disk if you want to keep it around for very long.
|
||||||
|
|
||||||
|
In urbit, app state is a single (usually complex) value. In our
|
||||||
|
example, we don't have any special state, so we defined
|
||||||
|
`state=~`, meaning that our state is null. Of course, `state` is
|
||||||
|
just a name we're assigning to it, and you're free to use
|
||||||
|
whatever name you want.
|
||||||
|
|
||||||
|
Since urbit is purely functional, we can't just implicitly "have"
|
||||||
|
and "change" our state. Rather, it's explicitly passed to us, in
|
||||||
|
the `|_ [bowl state=~]` line, and we produce the new state with
|
||||||
|
`+>.$` in the `[~ +>.$]` line.
|
||||||
|
|
||||||
|
Two points you may be wondering about. Firstly, `bowl` is a set
|
||||||
|
of general global state that is managed by the system. It
|
||||||
|
includes things like `now` (current time), `our` (our urbit
|
||||||
|
identity), and `eny` (256 bits of guaranteed-fresh entropy). For
|
||||||
|
the full list of things in `++bowl`, search for `++ bowl` (note
|
||||||
|
the double space) in `/arvo/zuse.hoon`.
|
||||||
|
|
||||||
|
> This is a very common technique in learning hoon. While some
|
||||||
|
> documentation exists, often the easiest way to learn about an
|
||||||
|
> identifier you see in code is to search in `/arvo/zuse.hoon`
|
||||||
|
> and `/arvo/hoon.hoon` for it. These are our two "standard
|
||||||
|
> libraries", and they're usually not hard to read. Since
|
||||||
|
> urbit's codebase is relatively small (those two files are less
|
||||||
|
> than 15000 lines of code combined, and besides the standard
|
||||||
|
> library they include the hoon parser and compiler, plus the
|
||||||
|
> /arvo microkernel), you can usually use the code and the
|
||||||
|
> comments as reference doc.
|
||||||
|
|
||||||
|
Second point is that urbit needs no "serialize to disk" step.
|
||||||
|
Everything you produce in the app state is persistent across
|
||||||
|
calls to the app, restarts of the urbit, and even power failure.
|
||||||
|
If you want to write to the filesystem, you can, but it's not
|
||||||
|
needed for persistence. Urbit has transactional events, which
|
||||||
|
makes it an ACID operating system. Persistence is just another
|
||||||
|
one of those things you don't have to worry about when
|
||||||
|
programming in urbit.
|
||||||
|
|
||||||
|
As fascinating as state is, we don't actually need any state to
|
||||||
|
accomplish our immediate goal, which is to get apps on two urbits
|
||||||
|
talking to each other. We'll discuss state more in a later
|
||||||
|
chapter.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Anyway, that's a lot of theory, let's get back to coding. Let's
|
||||||
|
say we want to only accept a number, and then print out the
|
||||||
|
square of that number.
|
||||||
|
|
||||||
|
```
|
||||||
|
/? 314
|
||||||
|
|%
|
||||||
|
++ move ,*
|
||||||
|
--
|
||||||
|
!:
|
||||||
|
|_ [bowl state=~]
|
||||||
|
::
|
||||||
|
++ poke-atom
|
||||||
|
|= arg=@
|
||||||
|
^- [(list move) _+>.$]
|
||||||
|
~& [%square (mul arg arg)]
|
||||||
|
[~ +>.$]
|
||||||
|
--
|
||||||
|
```
|
||||||
|
|
||||||
|
A few things have changed. Firstly, we no longer accept
|
||||||
|
arbitrary nouns because we can only square atoms. Thus, our
|
||||||
|
argument is now `arg=@`. Secondly, it's `++poke-atom` rather
|
||||||
|
than `++poke-noun`.
|
||||||
|
|
||||||
|
Are there other `++poke`s? Definitely. In fact, `noun` and
|
||||||
|
`atom` are just two of arbitrarily many "marks". A mark is
|
||||||
|
fundamentally a type definition, and each mark is defined in the
|
||||||
|
`/mar` directory. Some marks have conversion routines to other
|
||||||
|
marks, and some have diff, path, and merge algorithms. None of
|
||||||
|
these are required for a mark to exist, though.
|
||||||
|
|
||||||
|
`noun` and `atom` are two of dozens of predefined marks, and the
|
||||||
|
user may add more at will. The type associated with `noun` is
|
||||||
|
`*`, and the type associated with `atom` is `@`.
|
||||||
|
|
||||||
|
Data constructed on the command line is by default marked with
|
||||||
|
`noun`. In this case, the app is expecting an atom, so we have
|
||||||
|
to explicitly mark the data with `atom`.
|
||||||
|
|
||||||
|
```
|
||||||
|
> |start %square
|
||||||
|
> :square 6
|
||||||
|
gall: %square: no poke arm for noun
|
||||||
|
> :square &atom 6
|
||||||
|
[%square 36]
|
||||||
|
```
|
||||||
|
|
||||||
|
Marks are powerful, and they're the backbone of urbit's data
|
||||||
|
pipeline, so we'll be getting quite used to them.
|
||||||
|
|
||||||
|
Let's write our first network message!
|
||||||
|
|
||||||
|
-don't forget to explain new hoon concepts ([])
|
||||||
|
-excercises
|
||||||
|
-then walk through more in depth
|
||||||
|
-move app state section to somewhere later
|
||||||
|
-put in interactive code snippets and explain a lil (bowl)
|
||||||
|
|
||||||
|
|
@ -281,6 +281,6 @@ Can we use mutation to build a cyclical noun? Nice try, but no:
|
|||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
Now, not only can we build a noun, we can get data out of it and
|
Now, not only can you build a noun, you can get data out of it and
|
||||||
even evolve new, related nouns. We've still seen only two very
|
even evolve new, related nouns. We've still seen only two very
|
||||||
restricted kinds of twigs: constants and legs. In the [next chapter](2-syntax), we'll actually write some interesting expressions.
|
restricted kinds of twigs: constants and legs. In the [next chapter](2-syntax), we'll actually write some interesting expressions.
|
@ -244,6 +244,16 @@ ever. In Hoon it's called a *kelp*.
|
|||||||
The head of a kelp (like `%dtzy` above) is called the *stem*.
|
The head of a kelp (like `%dtzy` above) is called the *stem*.
|
||||||
The tail (like `[%ux 42]`) is the *bulb*.
|
The tail (like `[%ux 42]`) is the *bulb*.
|
||||||
|
|
||||||
|
> Think about how to encode tagged data in a noun. It seems
|
||||||
|
obvious that the noun is a cell, where the head of the cell is
|
||||||
|
the tag (stem) and the tail is the data (bulb). Then, all these
|
||||||
|
nouns are cells whose head is an atom. This leaves two noun
|
||||||
|
shapes "out of band": atoms, and cells whose head is a cell.
|
||||||
|
|
||||||
|
> Since no twig is an atom, a cell `[twig twig]` is always a cell
|
||||||
|
whose head is a cell. So we can distinguish "autocons" from all
|
||||||
|
stem-bulb nouns, and assign it specific semantics.
|
||||||
|
|
||||||
#### Runes and stems
|
#### Runes and stems
|
||||||
|
|
||||||
A *rune* is a digraph - a sequence of two ASCII glyphs. If you
|
A *rune* is a digraph - a sequence of two ASCII glyphs. If you
|
||||||
@ -359,7 +369,7 @@ there are no fixed rules for doing it right.
|
|||||||
> Keep lines under 80 characters, though. The parser doesn't
|
> Keep lines under 80 characters, though. The parser doesn't
|
||||||
enforce this yet. But it will, so watch out!
|
enforce this yet. But it will, so watch out!
|
||||||
|
|
||||||
##### Backstep indentation
|
#### Backstep indentation
|
||||||
|
|
||||||
Note that the "variable declaration" metaphor of `=+` works
|
Note that the "variable declaration" metaphor of `=+` works
|
||||||
perfectly here. Because `[%hello planet]` -- despite being a
|
perfectly here. Because `[%hello planet]` -- despite being a
|
||||||
@ -462,4 +472,4 @@ Or with a gratuitous use of tall form:
|
|||||||
Now you know how to read Hoon! For fun, try to pronounce more of
|
Now you know how to read Hoon! For fun, try to pronounce more of
|
||||||
the code on this page. Please don't laugh too hard at yourself.
|
the code on this page. Please don't laugh too hard at yourself.
|
||||||
|
|
||||||
In the next chapter, we'll actually write a real program...
|
In the [next chapter](3-program), we'll actually write a real program...
|
350
pub/docs/dev/hoon/principles/3-program.mdy
Normal file
350
pub/docs/dev/hoon/principles/3-program.mdy
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
---
|
||||||
|
title: Hoon 101.3: an algorithm
|
||||||
|
sort: 3
|
||||||
|
hide: true
|
||||||
|
next: false
|
||||||
|
---
|
||||||
|
# Hoon 101.3: an algorithm
|
||||||
|
|
||||||
|
In [chapter 0](0-nouns), we read about nouns. In [chapter 1](1-twigs),
|
||||||
|
we discovered twigs and legs. In [chapter 2](2-syntax), we learned
|
||||||
|
Hoon syntax and created our first source file.
|
||||||
|
|
||||||
|
Now it's time for an actual, meaningful *algorithm*.
|
||||||
|
|
||||||
|
## How to use this tutorial
|
||||||
|
|
||||||
|
Ideally, you've installed an Urbit planet (if you have a ticket)
|
||||||
|
or comet (if you don't). See the [user doc](../../../user).
|
||||||
|
|
||||||
|
We recommend opening up the dojo and just typing the examples;
|
||||||
|
you don't know a language until you know it in your fingers.
|
||||||
|
Also, make sure you've worked through the chapters in order.
|
||||||
|
|
||||||
|
## Goal: a decrement generator
|
||||||
|
|
||||||
|
Our algorithm is the classic Urbit example: decrement.
|
||||||
|
|
||||||
|
If you learned [Nock](../../nock) before Hoon, you've already
|
||||||
|
written decrement. If not, all you need to know is that the only
|
||||||
|
built-in arithmetic operator in Nock is increment. To decrement,
|
||||||
|
we need to count up to the result with a simple loop.
|
||||||
|
|
||||||
|
## A practical subject
|
||||||
|
|
||||||
|
As we've seen, Hoon works by running a twig against a subject.
|
||||||
|
In chapter 1, we made a simple test subject that was just a few
|
||||||
|
constants, and applied some simple twigs to it.
|
||||||
|
|
||||||
|
This time, we'll actually put useful working data in the subject,
|
||||||
|
building up more and more complex subjects as we go.
|
||||||
|
|
||||||
|
## Subject: nil
|
||||||
|
|
||||||
|
We'll start with the empty subject `~`:
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
=> ~
|
||||||
|
[%hello %world]
|
||||||
|
```
|
||||||
|
The `=>` rune ("tisgar", `%tsgr`)), for `=>(p q)` executes `p`
|
||||||
|
against the subject, then uses that product as the subject of
|
||||||
|
`q`.
|
||||||
|
|
||||||
|
> We've already used an irregular form of `=>`, or to be more
|
||||||
|
precise its mirror `=<` ("tisgal", `%tsgl`). In chapter 1, when we wrote
|
||||||
|
`+3:test`, we meant `=>(test +3)` or `=<(+3 test)`.
|
||||||
|
|
||||||
|
What is `~`? It's Hoon `nil`, a zero atom:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ?? ~
|
||||||
|
[%cube 0 [%atom %n]]
|
||||||
|
~
|
||||||
|
```
|
||||||
|
We use `~` for list terminators and the like. Obviously, since
|
||||||
|
`[%hello %world]` is just a constant, a nil subject works as
|
||||||
|
well as any:
|
||||||
|
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test
|
||||||
|
[%hello %world]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Subject: `arg=@`
|
||||||
|
|
||||||
|
Above, we used an empty subject to avoid the very complex,
|
||||||
|
interesting subject your generator twig gets by default.
|
||||||
|
|
||||||
|
But a decrement generator needs to get an argument from the
|
||||||
|
command line -- the number we're trying to decrement.
|
||||||
|
|
||||||
|
This involves changing the `test.hoon` boilerplate a little.
|
||||||
|
Again, don't worry about the boilerplate line just yet:
|
||||||
|
```
|
||||||
|
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=> arg=arg
|
||||||
|
[%hello arg]
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
[%hello 42]
|
||||||
|
```
|
||||||
|
> `=> arg=arg` looks a little odd. We wouldn't ordinarily do
|
||||||
|
this; we're just trying to keep the subject simple.
|
||||||
|
|
||||||
|
In case there's any doubt about the subject (remember from
|
||||||
|
chapter 1 that `.` means `+1`, the whole subject):
|
||||||
|
```
|
||||||
|
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=> arg=arg
|
||||||
|
.
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
arg=42
|
||||||
|
```
|
||||||
|
|
||||||
|
## An increment generator
|
||||||
|
|
||||||
|
We can even write a trivial increment generator using `.+`,
|
||||||
|
or even better its irregular form `+`:
|
||||||
|
```
|
||||||
|
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=> arg=arg
|
||||||
|
+(arg)
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
43
|
||||||
|
```
|
||||||
|
We'll assume these same first two lines for the rest of the
|
||||||
|
examples in this chapter.
|
||||||
|
|
||||||
|
## Cores: one more kind of span
|
||||||
|
|
||||||
|
To decrement, we need a loop. To write a loop, we need yet
|
||||||
|
another kind of noun: the *core*. Briefly, a core is a `[code
|
||||||
|
data]` pair. Our new `span` mold:
|
||||||
|
```
|
||||||
|
++ span
|
||||||
|
$% [%atom p=@tas]
|
||||||
|
[%cell p=span p=span]
|
||||||
|
[%core p=span q=(map ,@tas twig)]
|
||||||
|
[%cube p=* q=span]
|
||||||
|
[%face p=@tas q=span]
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structure of a core
|
||||||
|
|
||||||
|
The core is a cell `[battery payload]`. The payload is data, the
|
||||||
|
battery is code -- a tree of Nock formulas.
|
||||||
|
|
||||||
|
In the `%core` span, the payload is described by an arbitrary
|
||||||
|
span. The battery is described by a map of symbol to twig.
|
||||||
|
|
||||||
|
> Yes, the span contains the complete AST of the whole core.
|
||||||
|
This is a very different approach from most typed languages,
|
||||||
|
which compute function signatures. To infer the product of an
|
||||||
|
arm, we retrace the code in theory, but cache in practice.
|
||||||
|
|
||||||
|
### Arms are computed attributes
|
||||||
|
|
||||||
|
How do we use a core? Remember that Nock is a function
|
||||||
|
```
|
||||||
|
Nock(subject formula) -> product
|
||||||
|
```
|
||||||
|
To activate a core, we pull one formula from the battery, and
|
||||||
|
execute with the whole core as the subject. Coincidentally,
|
||||||
|
Nock has one macro instruction, `9`, that does just this.
|
||||||
|
|
||||||
|
So the formula defines an arbitrary function on the core. The
|
||||||
|
name of this function (as defined in the twig map) is a computed
|
||||||
|
attribute, or *arm*.
|
||||||
|
|
||||||
|
> Is a core an object? Not quite, because an arm is not a method.
|
||||||
|
Methods in an OO language have arguments. Arms are functions
|
||||||
|
only of the payload. (A method in Hoon is an arm that produces a
|
||||||
|
gate, which is another core -- but we're getting too far ahead.)
|
||||||
|
However, the battery does look a lot like a classic "vtable."
|
||||||
|
|
||||||
|
### Arms and wing resolution
|
||||||
|
|
||||||
|
It might be a good time to brush up on your [chapter 1](1-twigs).
|
||||||
|
The wing resolution algorithm gets a little more complicated.
|
||||||
|
|
||||||
|
Hoon overloads computed attributes (arms) and literal attributes
|
||||||
|
(legs) in the same namespace. A label in a wing may refer to
|
||||||
|
either. when searching a core, we look for a matching arm. If
|
||||||
|
we find it we're done. If we don't, or if a `^` mark makes us
|
||||||
|
skip, we search into the payload.
|
||||||
|
|
||||||
|
Only the last limb in the wing can activate an arm. If a name
|
||||||
|
resolves to a core arm, but it's not the last limb in the wing,
|
||||||
|
the arm produces the core itself. Similarly, when the wing is
|
||||||
|
not an access but a mutation, the arm refers to the core. Only
|
||||||
|
the end of the wing is activated.
|
||||||
|
|
||||||
|
> Suppose `foo` is a core. `bar` is an arm on foo. Then `bar.foo`
|
||||||
|
computes the `bar` arm. Perhaps the product is another core.
|
||||||
|
Even if `moo` is an arm in this new core, `bar.foo`, the wing
|
||||||
|
`moo.bar.foo` does not compute `moo` on the core `bar.foo`.
|
||||||
|
Instead, it looks for `moo` within the payload of the core `foo`.
|
||||||
|
(You're looking for `moo:bar.foo`, ie, `=>(bar.foo moo)`.)
|
||||||
|
|
||||||
|
Does this sound too tricky? Hopefully not, but it's about the
|
||||||
|
most complicated semantic rule you'll find in Hoon.
|
||||||
|
|
||||||
|
## Increment with a core
|
||||||
|
|
||||||
|
Keeping our two-line boilerplate, let's increment with a core:
|
||||||
|
```
|
||||||
|
=< inc
|
||||||
|
|%
|
||||||
|
++ inc
|
||||||
|
+(arg)
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
43
|
||||||
|
```
|
||||||
|
What's going on? We used the `|%` rune ("barcen") to produce a
|
||||||
|
core. (There are a lot of runes which create cores; they all
|
||||||
|
start with `|`, and are basically macros that turn into `|%`.)
|
||||||
|
|
||||||
|
The payload of a core produced with `|%` is the subject with
|
||||||
|
which `|%` is compiled. We might say that `|%` wraps a core
|
||||||
|
around its subject. In this case, the subject of the `|%`,
|
||||||
|
and thus payload, is our `arg=@ud` argument.
|
||||||
|
|
||||||
|
Then we used this core as the subject of the simple wing `inc`.
|
||||||
|
|
||||||
|
> Remember that `=<(a b)` is just `=>(b a)`. The core is heavy
|
||||||
|
and `inc` is light, so we use `=<` to put the heavy part last.
|
||||||
|
|
||||||
|
The prettyprinter can even print a core, sort of. Take out the
|
||||||
|
`=< inc`:
|
||||||
|
```
|
||||||
|
|%
|
||||||
|
++ inc
|
||||||
|
+(arg)
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
<1.bgq arg=42>
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ? +test 42
|
||||||
|
<1.bgq arg=@ud>
|
||||||
|
<1.bgq arg=42>
|
||||||
|
```
|
||||||
|
In this notation, `1` means the number of arms in the core, `bgq`
|
||||||
|
is a very short fingerprint, and `arg=42` is the payload (and
|
||||||
|
`arg=@ud` is the payload span).
|
||||||
|
|
||||||
|
> Cores can be large and complex, and we obviously can't render all
|
||||||
|
the data in them, either when printing a type or a value. At
|
||||||
|
some point, you'll probably make the mistake of printing a big
|
||||||
|
core, maybe even the whole kernel, as an untyped noun. Just
|
||||||
|
press ^C.
|
||||||
|
|
||||||
|
## Adding a counter
|
||||||
|
|
||||||
|
To decrement, we need to count up to the argument. So we need a
|
||||||
|
counter in our subject, because where else would it go?
|
||||||
|
|
||||||
|
Let's change the subject to add a counter, `pre`:
|
||||||
|
```
|
||||||
|
=> [pre=0 .]
|
||||||
|
=< inc
|
||||||
|
|%
|
||||||
|
++ inc
|
||||||
|
+(arg)
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
43
|
||||||
|
```
|
||||||
|
Once again, `.` is the whole subject, so we're wrapping it in a
|
||||||
|
cell whose head is `pre=0`. This doesn't change the way we use
|
||||||
|
`arg`, even though it's one level deeper in the subject tree.
|
||||||
|
Let's look at the subject again:
|
||||||
|
```
|
||||||
|
=> [pre=0 .]
|
||||||
|
.
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
[pre=0 arg=42]
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ? +test 42
|
||||||
|
[pre=@ud arg=@ud]
|
||||||
|
[pre=0 arg=42]
|
||||||
|
```
|
||||||
|
There's a less cluttered way to write `=>([a .] b)`. In wide
|
||||||
|
mode, `=+(a b)`. A tall example:
|
||||||
|
```
|
||||||
|
=+ pre=0
|
||||||
|
.
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
[pre=0 arg=42]
|
||||||
|
```
|
||||||
|
This rune `=+`, "tislus", `%tsls`, is of course the "variable
|
||||||
|
declaration" we saw in chapter 2.
|
||||||
|
|
||||||
|
## We actually decrement
|
||||||
|
|
||||||
|
Now we can write our actual decrement program, combining the
|
||||||
|
features we've explored above:
|
||||||
|
```
|
||||||
|
=+ pre=0
|
||||||
|
=< dec
|
||||||
|
|%
|
||||||
|
++ dec
|
||||||
|
?: =(arg +(pre))
|
||||||
|
pre
|
||||||
|
dec(pre +(pre))
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
`=(a b)` is an irregular form of `.=(a b)`, ie, "dottis" or the
|
||||||
|
noun `[%dtts a b]`. Likewise, `+(a)` is `.+(a)`, ie, "dotlus"
|
||||||
|
or `[%dtls a]`.
|
||||||
|
|
||||||
|
`?:`, "wuttis", `%wtts`, does exactly what it does in C.
|
||||||
|
|
||||||
|
The real action is in `dec(pre +(pre))`. We saw this same
|
||||||
|
mutation form in chapter 1. It's an irregular form of the `%=`
|
||||||
|
rune, "centis", `%cnts`.
|
||||||
|
|
||||||
|
Here, of course, we're computing an arm of a mutated core. We're
|
||||||
|
recomputing the loop arm, `dec`, against a new core with an
|
||||||
|
incremented `pre` leg. Basically, everything interesting in Hoon
|
||||||
|
happens when you compute an arm of a mutated core.
|
||||||
|
|
||||||
|
The whole program, using only regular forms, wide and tall:
|
||||||
|
```
|
||||||
|
=+ ^=(pre 0)
|
||||||
|
=< dec
|
||||||
|
|%
|
||||||
|
++ dec
|
||||||
|
?: .=(arg .+(pre))
|
||||||
|
pre
|
||||||
|
%= dec
|
||||||
|
pre .+(pre)
|
||||||
|
==
|
||||||
|
--
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
A good illustration of why we need irregular forms.
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
Now we've actually done something useful. Well, if you count
|
||||||
|
O(n) decrement as something useful.
|
||||||
|
|
||||||
|
We've actually seen most of the major tools in Hoon's toolbox,
|
||||||
|
just in a super-minimalist selection. But we'll need a few more
|
||||||
|
runes to get to something that looks like real programming.
|
||||||
|
|
||||||
|
For instance, we have a decrement algorithm, but we don't have a
|
||||||
|
decrement *function* - in the Hoon sense of the word, anyway. We
|
||||||
|
don't see `(dec arg)` in this code. That'll be the next chapter.
|
327
pub/docs/dev/hoon/principles/4-functions.mdy
Normal file
327
pub/docs/dev/hoon/principles/4-functions.mdy
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
---
|
||||||
|
title: Hoon 101.4: functions
|
||||||
|
sort: 4
|
||||||
|
hide: true
|
||||||
|
next: false
|
||||||
|
---
|
||||||
|
# Hoon 4: toward actual functions
|
||||||
|
|
||||||
|
In [chapter 0](0-nouns), we read about nouns. In [chapter 1](1-twigs),
|
||||||
|
we discovered twigs and legs. In [chapter 2](2-syntax), we learned
|
||||||
|
Hoon syntax and created our first source file. In [chapter 3](3-algorithm)
|
||||||
|
we wrote an actual algorithm.
|
||||||
|
|
||||||
|
Now we'll go a step farther and actually define a *function*.
|
||||||
|
|
||||||
|
## Goal: `(decrement arg)`
|
||||||
|
|
||||||
|
Of course, Hoon is a functional language and every twig defines a
|
||||||
|
function of the subject. But we haven't seen the Hoon equivalent
|
||||||
|
of a function *declaration*. Where is Lisp `defun`?
|
||||||
|
|
||||||
|
Also, the Hoon equivalent of a normal function call can't just
|
||||||
|
involve compiling the function into a Nock formula, whose subject
|
||||||
|
is the function's argument. As Einstein said, everything must be
|
||||||
|
as simple as possible, but no simpler.
|
||||||
|
|
||||||
|
A function that worked this way would, by definition, have no
|
||||||
|
access to data or code other than the argument. Where would the
|
||||||
|
standard library go? Would we have to pass it in the argument?
|
||||||
|
|
||||||
|
## Form of the solution
|
||||||
|
|
||||||
|
Let's get the boilerplate out of the way. We'll keep the same
|
||||||
|
external generator interface, but our solution will look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
(decrement arg)
|
||||||
|
=> ~
|
||||||
|
!!
|
||||||
|
```
|
||||||
|
In place of the `!!`, we'll put a core, effectively a library,
|
||||||
|
that exports a function `decrement`. We'll then call that
|
||||||
|
function with `(decrement arg)`, an irregular form of
|
||||||
|
`%-(decrement arg)`. Again, this core uses `=> ~` to clear
|
||||||
|
the subject, so we can't cheat and call `(dec arg)`.
|
||||||
|
|
||||||
|
> `!!`, or "zapzap" or `[%zpzp ~]`, is a twig that
|
||||||
|
always crashes. Because it produces the empty span (`%void`), it
|
||||||
|
doesn't cause type inference problems.
|
||||||
|
|
||||||
|
## Not quite a function call
|
||||||
|
|
||||||
|
As usual, we'll get to our goal gradually. A first try:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=decrement
|
||||||
|
=<(run gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ decrement
|
||||||
|
=+ sam=0
|
||||||
|
=+ pre=0
|
||||||
|
|%
|
||||||
|
++ run
|
||||||
|
?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
run(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
This works, but it's hardly concise or pretty. We'll get there.
|
||||||
|
|
||||||
|
We replaced the `!!` with a `|%` twig that produces a library
|
||||||
|
core. The payload of our library core is `~`, because that's the
|
||||||
|
subject we sent in. The battery contains one arm, `decrement`.
|
||||||
|
|
||||||
|
The `decrement` arm produces an inner core. The payload of this
|
||||||
|
inner core is `[pre=0 sam=0 +>]`, where `+>` is the library core.
|
||||||
|
The battery of the inner core has one arm, `run`, which computes
|
||||||
|
our algorithm from the last chapter.
|
||||||
|
|
||||||
|
To run our algorithm, we put the core that `decrement` produces
|
||||||
|
into a new leg, `gat`. Mutating this core to set `sam` to our
|
||||||
|
argument `arg`, we evaluate the `run` arm.
|
||||||
|
|
||||||
|
> Why do we need this `gat` leg? Why can't we just write `=<(run
|
||||||
|
decrement(sam arg))`? Remember our `moo:bar.foo` problem from
|
||||||
|
the previous chapter. `decrement(sam arg)` mutates not the
|
||||||
|
*product* of the decrement arm, but the core that computes it.
|
||||||
|
There's no `sam` in that subject, so the twig won't compile.
|
||||||
|
|
||||||
|
## Better is worse
|
||||||
|
|
||||||
|
To make this code look simpler, we need to make it more complex.
|
||||||
|
Our next try uses not two nested cores, but *three*:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=decrement
|
||||||
|
=<(run gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ decrement
|
||||||
|
=+ sam=0
|
||||||
|
|%
|
||||||
|
++ run
|
||||||
|
=+ pre=0
|
||||||
|
=< loop
|
||||||
|
|%
|
||||||
|
++ loop
|
||||||
|
?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
loop(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--
|
||||||
|
```
|
||||||
|
This is actually the final structure of our function, but uses
|
||||||
|
none of the syntactic tricks we'll use to make it pretty.
|
||||||
|
|
||||||
|
> Why do we need to make the code more complex? Because we need
|
||||||
|
it to use the regular form that our synthetic runes, ie macros,
|
||||||
|
use to express function definitions and calls.
|
||||||
|
|
||||||
|
## Loop sugar
|
||||||
|
|
||||||
|
Look at little `loop`. It works just like our old `run`. But
|
||||||
|
there's something nice about it: we don't use the symbol `loop`
|
||||||
|
anywhere outside these 7 lines of code. It's not exported.
|
||||||
|
|
||||||
|
Therefore, the `loop` name is useless and redundant. Making up
|
||||||
|
names is one of the hard problems in computer science, so why
|
||||||
|
solve it for no reason?
|
||||||
|
|
||||||
|
So Hoon has an *empty name*: `$`. As a constant, `$` is a
|
||||||
|
zero-length symbol (`%$` instead of `%foo`) As a limb is the
|
||||||
|
`buc` symbol (`$`).
|
||||||
|
|
||||||
|
Replacing `loop` with `$`, our loop becomes:
|
||||||
|
```
|
||||||
|
=< $
|
||||||
|
|%
|
||||||
|
++ $
|
||||||
|
?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(sam +(run))
|
||||||
|
--
|
||||||
|
```
|
||||||
|
|
||||||
|
This may not seem like a huge improvement. It's not. But it
|
||||||
|
lets us match and move to the synthetic rune `|-`, "barhep":
|
||||||
|
```
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
```
|
||||||
|
`|-` is simply the canonical Hoon loop. All we've done to use a
|
||||||
|
core as a loop is to name our arm `$`, and evaluate it on the
|
||||||
|
core we just made.
|
||||||
|
|
||||||
|
Our program now:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=decrement
|
||||||
|
=<(run gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ decrement
|
||||||
|
=+ sam=0
|
||||||
|
|%
|
||||||
|
++ run
|
||||||
|
=+ pre=0
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
```
|
||||||
|
|
||||||
|
## Almost to lambda
|
||||||
|
|
||||||
|
Could we use `$` for `++run`? It certainly sounds like the same
|
||||||
|
kind of thing as `++loop`. The word "run" just means "do it".
|
||||||
|
Why should we be saying "do it" all the time?
|
||||||
|
|
||||||
|
Our third try, with this change:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=decrement
|
||||||
|
=<($ gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ decrement
|
||||||
|
=| sam=@ud
|
||||||
|
|%
|
||||||
|
++ $
|
||||||
|
=+ pre=0
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
> Besides `run` to `$`, we changed `=+ sam=0` to `=| sam=@ud`.
|
||||||
|
This is some mold magic we'll explain in the next chapter, but
|
||||||
|
it basically does the same thing: pushes a leg named `sam`, whose
|
||||||
|
value is a number we'll later overwrite.
|
||||||
|
|
||||||
|
This use of `$` in a core, very similar to the way we used `$` in
|
||||||
|
our `|-` loop, smells like it too should have a rune. It does:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=decrement
|
||||||
|
=<($ gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ decrement
|
||||||
|
|= sam=@ud
|
||||||
|
=+ pre=0
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
Doesn't `decrement` look like a function? Indeed, we're done with
|
||||||
|
the `decrement` arm. This is what it should look like.
|
||||||
|
|
||||||
|
> If you squint a little, `|=` ("bartis") might even be a strange,
|
||||||
|
deformed ASCII-art lambda.
|
||||||
|
|
||||||
|
Since it's doing something simple, we might well even compress
|
||||||
|
the whole body of `decrement` into one wide-form line:
|
||||||
|
```
|
||||||
|
=+(pre=0 |-(?:(=(sam +(pre)) pre $(pre +(pre)))))
|
||||||
|
```
|
||||||
|
This is a bit tight for most. But we can get really compact by
|
||||||
|
dropping the labels and going to full geometry:
|
||||||
|
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=decrement
|
||||||
|
=<($ gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ decrement |=(@ud =+(0 |-(?:(=(+>+< +<) +< $(+< +(+<))))))
|
||||||
|
--
|
||||||
|
```
|
||||||
|
> It's not a good idea to actually program this way. But it does
|
||||||
|
strengthen your Hoon tree-geometry muscles. To use tree
|
||||||
|
geometry, you have to really know the shape of your subject.
|
||||||
|
|
||||||
|
## Structure of a gate
|
||||||
|
|
||||||
|
What is the noun produced by `decrement`? In true Hoonese it's
|
||||||
|
a *gate*, but nobody will hate you for saying "function." And
|
||||||
|
while we *slam* our gates, you can feel free to just "call" them.
|
||||||
|
|
||||||
|
Every gate is a core; not every core is a gate. All cores are
|
||||||
|
shaped like `[battery payload]`. A core is a gate if its payload
|
||||||
|
is a cell, and its battery has one arm, `$`. So a gate is shaped
|
||||||
|
like `[formula [sample context]]`.
|
||||||
|
|
||||||
|
To slam (call) a gate, you replace its sample (`+6` or `+<`,
|
||||||
|
"lusgal" or "dish") with your argument. "Multiple arguments" are
|
||||||
|
a single sample that's a tuple. So our code should actually read
|
||||||
|
```
|
||||||
|
=+ gat=decrement
|
||||||
|
=<($ gat(+< arg))
|
||||||
|
```
|
||||||
|
This `=+` sequence remains ugly. Once again, `decrement(+< arg)`
|
||||||
|
is not a mutation to the product of `decrement`, but a mutation
|
||||||
|
to the core that produces `decrement`. Fortunately, there's a
|
||||||
|
sugary rune that combines the semantics of both these lines:
|
||||||
|
```
|
||||||
|
%*($ decrement +< arg))
|
||||||
|
```
|
||||||
|
This mutates the product of `decrement` as specified. (`+< arg`
|
||||||
|
in the wide form of `$*` is just the first of n comma-separated
|
||||||
|
pairs; we could write `%*($ decrement +< arg, +> foo, moo 42)`.
|
||||||
|
|
||||||
|
> Arguably, %* is oversweetened; $:(decrement +< arg)
|
||||||
|
would be simpler and better. Hoon isn't perfect.
|
||||||
|
|
||||||
|
## Slamming the gate
|
||||||
|
|
||||||
|
Anyway, it shouldn't come as a surprise that a synonym for
|
||||||
|
`%*($ function +< argument)` is `%-(function argument)`. And an
|
||||||
|
irregular form of this is just `(function argument)`.
|
||||||
|
|
||||||
|
The irregular syntax constructs multiple-argument syntax as a
|
||||||
|
tuple; `(add 2 2)` is `(add [2 2])`, `(rsh 3 1 5)` is `(rsh [3 1 5])`.
|
||||||
|
If there are no arguments, `(function)`, this means `$:function`,
|
||||||
|
ie, `=>(function $)`.
|
||||||
|
|
||||||
|
We can also take the `=> ~` out, since we now know what we're
|
||||||
|
doing. The final program:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
(decrement arg)
|
||||||
|
|%
|
||||||
|
++ decrement
|
||||||
|
|= sam=@ud
|
||||||
|
=+ pre=0
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
```
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
In most functional languages, a function is a primitive. In Hoon
|
||||||
|
it's a macro, and a pretty complex macro at that. We spent four
|
||||||
|
chapters building the concept up out of much smaller pieces.
|
||||||
|
|
||||||
|
Fortunately, once you have functions you can do anything. We
|
||||||
|
still have a fair bit to learn, but we've now seen all the
|
||||||
|
fundamental building blocks of programming in Hoon. In the next
|
||||||
|
chapter, we'll get back into molds and spans.
|
@ -1,322 +0,0 @@
|
|||||||
---
|
|
||||||
title: Hoon 101.3: our first program
|
|
||||||
sort: 3
|
|
||||||
hide: true
|
|
||||||
next: false
|
|
||||||
---
|
|
||||||
# Hoon 101.3: our first program
|
|
||||||
|
|
||||||
It's time for us to do some actual programming. In this section,
|
|
||||||
we'll work through that classic Urbit pons asinorum, decrement.
|
|
||||||
|
|
||||||
If you learned Nock before Hoon, you've already done decrement.
|
|
||||||
If not, all you need to know is that the only arithmetic
|
|
||||||
intrinsic in Nock is increment -- in Hoon, the unary `.+` rune.
|
|
||||||
So an actual decrement function is required.
|
|
||||||
|
|
||||||
In chapter 3, we write a decrement builder: more or less the
|
|
||||||
simplest nontrivial Urbit program. We should be able to run this
|
|
||||||
example:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
|
|
||||||
## What's in that subject?
|
|
||||||
|
|
||||||
As we've seen, Hoon works by running a twig against a subject.
|
|
||||||
We've been cheerfully running twigs through three chapters while
|
|
||||||
avoiding the question: what's in the subject? To avoid the issue
|
|
||||||
we've built a lot of constants, etc.
|
|
||||||
|
|
||||||
Of course your twig's subject comes from whoever runs it. There
|
|
||||||
is no one true subject. Our twigs on the command line are not
|
|
||||||
run against the same subject as our generator code, even though
|
|
||||||
they are both run by the same `:dojo` appliance.
|
|
||||||
|
|
||||||
But the short answer is that both command-line and builder get
|
|
||||||
*basically* the same subject: some ginormous noun containing all
|
|
||||||
kinds of bells and whistles and slicers and dicers, including a
|
|
||||||
kernel library which can needless to say decrement in its sleep.
|
|
||||||
|
|
||||||
As yet you have only faced human-sized nouns. We need not yet
|
|
||||||
acquaint you with this mighty Yggdrasil, Mother of Trees. First
|
|
||||||
we need to figure out what she could even be made of.
|
|
||||||
|
|
||||||
## Clearing the subject
|
|
||||||
|
|
||||||
We'll start by clearing the subject:
|
|
||||||
```
|
|
||||||
:- %say |= * :- %noun
|
|
||||||
=> ~
|
|
||||||
[%hello %world]
|
|
||||||
```
|
|
||||||
The `=>` rune ("tisgar"), for `=>(p q)` executes `p` against
|
|
||||||
the subject, then uses that product as the subject of `q`.
|
|
||||||
|
|
||||||
(We've already used an irregular form of `=>`, or to be more
|
|
||||||
precise its mirror `=<` ("tisgal"). In chapter 1, when we wrote
|
|
||||||
`+3:test`, we meant `=>(test +3)` or `=<(+3 test)`.)
|
|
||||||
|
|
||||||
What is this `~`? It's Hoon `nil`, a zero atom with this span:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo/sandbox> ?? ~
|
|
||||||
[%cube 0 %atom %n]
|
|
||||||
~
|
|
||||||
```
|
|
||||||
We use it for list terminators and the like. Obviously, since
|
|
||||||
our old test code is just a constant, a null subject works fine:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test
|
|
||||||
[%hello %world]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting an argument
|
|
||||||
|
|
||||||
Obviously, if we want to write a decrement builder, we'll have to
|
|
||||||
get an argument from the command line. This involves changing
|
|
||||||
the `test.hoon` boilerplate a little:
|
|
||||||
```
|
|
||||||
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=> arg=arg
|
|
||||||
[%hello arg]
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
[%hello 42]
|
|
||||||
```
|
|
||||||
`=> arg=arg` looks a little odd. We wouldn't ordinarily do
|
|
||||||
this. We're just replacing a very interesting subject that
|
|
||||||
contains `arg` with a very boring one that contains only `arg`,
|
|
||||||
for the same reason we cleared the subject with `~`.
|
|
||||||
|
|
||||||
In case there's any doubt about the subject (`.` is limb syntax
|
|
||||||
for `+1`, ie, the whole noun):
|
|
||||||
```
|
|
||||||
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=> arg=arg
|
|
||||||
.
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
arg=42
|
|
||||||
```
|
|
||||||
|
|
||||||
We can even write a trivial increment function using `.+`:
|
|
||||||
```
|
|
||||||
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=> arg=arg
|
|
||||||
+(arg)
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
43
|
|
||||||
```
|
|
||||||
Below we'll skip both boilerplate lines in our examples.
|
|
||||||
|
|
||||||
## A core is a code-data cell
|
|
||||||
|
|
||||||
But how do we actually, like, code? The algorithm for decrement
|
|
||||||
is clear. We need to count up to 41. (How do we run useful
|
|
||||||
programs on a computer with O(n) decrement? That's an
|
|
||||||
implementation detail.)
|
|
||||||
|
|
||||||
We'll need another kind of noun: the *core*. Briefly, the core
|
|
||||||
is always a cell `[battery payload]`. The payload is data, the
|
|
||||||
battery is code -- one or more Nock formulas, to be exact.
|
|
||||||
|
|
||||||
Consider a simple core with a one-formula battery. Remember, we
|
|
||||||
create Nock formulas by compiling a twig against a subject. The
|
|
||||||
subject is dynamic data, but its span is static. What span do we
|
|
||||||
give the compiler, and what noun do we give the formula?
|
|
||||||
|
|
||||||
A core formula always has the core as its subject. The formula
|
|
||||||
is essentially a computed attribute on the payload. But if the
|
|
||||||
subject was just the payload, the formula couldn't recurse.
|
|
||||||
|
|
||||||
Of course, there is no need to restrict ourselves to one computed
|
|
||||||
attribute. We can just stick a bunch of formulas together and
|
|
||||||
call them a battery. The source twigs in this core are called
|
|
||||||
"arms," which have labels just like the faces we saw earlier.
|
|
||||||
|
|
||||||
Hoon overloads computed attributes (arms) and literal attributes
|
|
||||||
(legs) in the same namespace. A label in a wing may refer to
|
|
||||||
either. To extend the name-resolution tree search described in
|
|
||||||
chapter 1, when searching a core, we look for a matching arm.
|
|
||||||
If we find it we're done. If we don't, or if a `^` mark makes us
|
|
||||||
skip, we search into the payload.
|
|
||||||
|
|
||||||
If a name resolves to a core arm, but it's not the last limb in the
|
|
||||||
wing, the arm produces the core itself. Similarly, when the
|
|
||||||
wing is not an access but a mutation, the arm refers to the core.
|
|
||||||
|
|
||||||
This demands an example: if `foo` produces some core `c`, and
|
|
||||||
`bar` is an arm in that `c` (which may be `foo` itself, or some
|
|
||||||
leg within `foo`), `bar.foo` runs the arm formula with `c` as the
|
|
||||||
subject. You might think that `moo.bar.foo` would compute
|
|
||||||
`bar.foo`, then search for `moo` within that result. Instead, it
|
|
||||||
searches for `moo` within `c`. (You can get the other result
|
|
||||||
with `moo:bar.foo`.)
|
|
||||||
|
|
||||||
Does this sound too tricky? It should - it's about the most
|
|
||||||
complicated feature of Hoon. It's all downhill once you
|
|
||||||
understand cores.
|
|
||||||
|
|
||||||
Let's again extend our `++span` mold:
|
|
||||||
```
|
|
||||||
++ span
|
|
||||||
$% [%atom @tas]
|
|
||||||
[%cell span span]
|
|
||||||
[%core span (map ,@tas twig)]
|
|
||||||
[%cube * span]
|
|
||||||
[%face @tas span]
|
|
||||||
==
|
|
||||||
```
|
|
||||||
This definition of `%core` is somewhat simplified from the
|
|
||||||
reality, but basically conveys it. (Moreover, this version of
|
|
||||||
`span` describes every kind of noun we build.) In our `%core` we
|
|
||||||
see a payload span and a name-to-twig arm table, as expected.
|
|
||||||
|
|
||||||
Is a core an object? Not quite, because an arm is not a method.
|
|
||||||
Methods in an OO language have arguments. Arms are functions
|
|
||||||
only of the payload. (A method in Hoon is an arm that produces a
|
|
||||||
gate, which is another core -- but we're getting too far ahead.)
|
|
||||||
However, the battery does look a lot like a classic "vtable."
|
|
||||||
|
|
||||||
## Increment with a core
|
|
||||||
|
|
||||||
Let's increment with a core:
|
|
||||||
```
|
|
||||||
=< inc
|
|
||||||
|%
|
|
||||||
++ inc
|
|
||||||
+(arg)
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
43
|
|
||||||
```
|
|
||||||
What's going on? We used the `|%` rune ("barcen") to produce a
|
|
||||||
core. (There are a lot of runes which create cores; they all
|
|
||||||
start with `|`, and are basically macros that turn into `|%`.)
|
|
||||||
|
|
||||||
The payload of a core produced with `|%` is the subject with
|
|
||||||
which `|%` is compiled. We might say that `|%` wraps a core
|
|
||||||
around its subject. In this case, the subject of the `|%`,
|
|
||||||
and thus payload, is our `arg=@ud` argument.
|
|
||||||
|
|
||||||
Then we used this core as the subject of the simple wing `inc`.
|
|
||||||
(Remember that `=<(a b)` is just `=>(b a)`.)
|
|
||||||
|
|
||||||
We can actually print out a core. Take out the `=< inc`:
|
|
||||||
```
|
|
||||||
|%
|
|
||||||
++ inc
|
|
||||||
+(arg)
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
!!!
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> ? +test 42
|
|
||||||
!!!
|
|
||||||
```
|
|
||||||
Cores can be large and complex, and we obviously can't render all
|
|
||||||
the data in them, either when printing a type or a value. At
|
|
||||||
some point, you'll probably make the mistake of printing a big
|
|
||||||
core, maybe even the whole kernel, as an untyped noun. Just
|
|
||||||
press ^C.
|
|
||||||
|
|
||||||
## Adding a counter
|
|
||||||
|
|
||||||
To decrement, we need to count up to the argument. So we need a
|
|
||||||
counter in our subject, because where else would it go? Let's
|
|
||||||
change the subject to add a counter, `pre`:
|
|
||||||
```
|
|
||||||
=> [pre=0 .]
|
|
||||||
=< inc
|
|
||||||
|%
|
|
||||||
++ inc
|
|
||||||
+(arg)
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
43
|
|
||||||
```
|
|
||||||
Once again, `.` is the whole subject, so we're wrapping it in a
|
|
||||||
cell whose head is `pre=0`. Through the magic of labels, this
|
|
||||||
doesn't change the way we use `arg`, even though it's one level
|
|
||||||
deeper in the subject tree. Let's look at the subject again:
|
|
||||||
```
|
|
||||||
=> [pre=0 .]
|
|
||||||
.
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
[pre=0 arg=42]
|
|
||||||
~tasfyn-partyv:dojo/sandbox> ? +test 42
|
|
||||||
[pre=@ud arg=@ud]
|
|
||||||
[pre=0 arg=42]
|
|
||||||
```
|
|
||||||
There's actually a simpler way to write this. We've seen it
|
|
||||||
already. It's not exactly a variable declaration:
|
|
||||||
```
|
|
||||||
=+ pre=0
|
|
||||||
.
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
[pre=0 arg=42]
|
|
||||||
```
|
|
||||||
|
|
||||||
## We actually decrement
|
|
||||||
|
|
||||||
Now we can write our actual decrement program:
|
|
||||||
```
|
|
||||||
=+ pre=0
|
|
||||||
=< dec
|
|
||||||
|%
|
|
||||||
++ dec
|
|
||||||
?: =(arg +(pre))
|
|
||||||
pre
|
|
||||||
dec(pre +(pre))
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
`=(a b)` is an irregular form of `.=(a b)`, ie, "dottis" or the
|
|
||||||
noun `[%dtts a b]`. Likewise, `+(a)` is `.+(a)`, ie, "dotlus"
|
|
||||||
or `[%dtls a]`.
|
|
||||||
|
|
||||||
`?:` is a regular rune which does exactly what you think it does.
|
|
||||||
Bear in mind, though, that in Hoon 0 (`&`, "pam") is true and 1
|
|
||||||
(`|`, "bar") is false.
|
|
||||||
|
|
||||||
The real action is in `dec(pre +(pre))`. This is obviously an
|
|
||||||
irregular form -- it's the same mutation form we saw before.
|
|
||||||
Writing it out in full regular form:
|
|
||||||
```
|
|
||||||
=+ pre=0
|
|
||||||
=< dec
|
|
||||||
|%
|
|
||||||
++ dec
|
|
||||||
?: =(arg +(pre))
|
|
||||||
pre
|
|
||||||
%= dec
|
|
||||||
pre +(pre)
|
|
||||||
==
|
|
||||||
--
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
`%=`, "centis", is the rune which almost every use of a wing
|
|
||||||
resolves to. It might be called "evaluate with changes."
|
|
||||||
|
|
||||||
When we evaluate with changes, we take a wing (`dec`) here and
|
|
||||||
evaluate it as described above. Searching in the subject, which
|
|
||||||
is of course our core, we find an arm called `dec` and run it.
|
|
||||||
|
|
||||||
The changes (replacing `pre` with `+(pre)`) are always applied
|
|
||||||
relative to the core we landed on (or the leg we landed on).
|
|
||||||
The change wing is relative to this target; the subject of the
|
|
||||||
replacement (`+(pre)`) is the original subject.
|
|
||||||
|
|
||||||
So, in English, we compute the `dec` arm again, against a new
|
|
||||||
core with a new payload that contains an incremented `pre`.
|
|
||||||
And thus, we decrement. Doesn't seem so hard, does it?
|
|
@ -1,267 +0,0 @@
|
|||||||
---
|
|
||||||
title: Hoon 101.4: functions
|
|
||||||
sort: 4
|
|
||||||
hide: true
|
|
||||||
next: false
|
|
||||||
---
|
|
||||||
# Hoon 4: toward actual functions
|
|
||||||
|
|
||||||
Okay, we've programmed. We've achieved decrement. We've written
|
|
||||||
what is in some sense a loop. What next?
|
|
||||||
|
|
||||||
Well... we're still feeling vaguely disappointed. Because we're
|
|
||||||
supposed to be doing *functional programming*. And we haven't
|
|
||||||
yet written any *functions*.
|
|
||||||
|
|
||||||
After all, in Hoon we don't really write a command-line utility
|
|
||||||
to decrement `42`. We write `(dec 42)`. You probably realize
|
|
||||||
that on the inside, this is not the same thing as a function in a
|
|
||||||
normal functional language. The Tasmanian tiger is not a tiger.
|
|
||||||
On the other hand, it certainly *looks* like a function call.
|
|
||||||
|
|
||||||
So how do we write the function?
|
|
||||||
|
|
||||||
In this chapter, we'll modify `+test` to extend the subject so
|
|
||||||
that we can write our result as `(dec arg)`. Or rather, `(duck
|
|
||||||
arg)`, because we want to get out of training wheels and stop
|
|
||||||
clearing the subject soon.
|
|
||||||
|
|
||||||
## Form of the solution
|
|
||||||
|
|
||||||
```
|
|
||||||
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
(duck arg)
|
|
||||||
!! :: some interesting core
|
|
||||||
```
|
|
||||||
`!!`, or "zapzap" or `[%zpzp ~]`, can go anywhere a twig can and
|
|
||||||
always crashes. Because its span is the empty set (`%void`), it
|
|
||||||
doesn't cause type inference problems.
|
|
||||||
|
|
||||||
In place of the `!!`, we'll put a core, effectively a library,
|
|
||||||
that provides our new, improved decrement function `duck`. We'll
|
|
||||||
then call it with the irregular form, `(duck arg)`, which looks
|
|
||||||
like a function call but is in fact some mysterious macro.
|
|
||||||
|
|
||||||
## Some interesting core
|
|
||||||
|
|
||||||
Translated into imperative programming, what we did in chapter 3
|
|
||||||
was more like computing a function of a global variable. Now,
|
|
||||||
we have to actually pass an argument to a function.
|
|
||||||
|
|
||||||
Here's our first try:
|
|
||||||
```
|
|
||||||
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=+ gat=duck
|
|
||||||
=<(run gat(sam arg))
|
|
||||||
=> ~
|
|
||||||
|%
|
|
||||||
++ duck
|
|
||||||
=+ sam=0
|
|
||||||
=+ pre=0
|
|
||||||
|%
|
|
||||||
++ run
|
|
||||||
?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
run(pre +(pre))
|
|
||||||
--
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
We step back and contemplate our handiwork. Is it good? Well...
|
|
||||||
it works. Reading programs written without syntactic sugar is
|
|
||||||
about as fun as eating raw chocolate nibs.
|
|
||||||
|
|
||||||
What did we do? In the `duck` arm (we often write `++duck`, for
|
|
||||||
obvious reasons) we produce a core whose payload is `[pre=0 num=0
|
|
||||||
~]`, and whose battery contains `++run`.
|
|
||||||
|
|
||||||
In the result twig, we first use `++duck` to extend our subject
|
|
||||||
with a core named `gat`. We then use `run` on that gate. Why do
|
|
||||||
we need this `gat`? Why can't we just write `=<(run duck(sam
|
|
||||||
arg))`?
|
|
||||||
|
|
||||||
Because the arm is computed *after* the mutation. But here we
|
|
||||||
need the mutated *result* of `++duck`. Instead, what this code
|
|
||||||
is doing is trying to mutate `sam` within the core that contains
|
|
||||||
`++duck`. Where it doesn't exist, so your code won't compile.
|
|
||||||
|
|
||||||
And note that with `=<`, we've placed our library structurally
|
|
||||||
between the original subject and the program we're writing,
|
|
||||||
but lexically at the bottom with zero left margin. We also
|
|
||||||
clear the subject to keep things simple.
|
|
||||||
|
|
||||||
## A more regular structure
|
|
||||||
|
|
||||||
It actually gets worse. To make this code look simpler, we need
|
|
||||||
to make it more complex. While "function calls" actually fit
|
|
||||||
quite well into the Hoon architecture, they're also a nontrivial
|
|
||||||
synthetic construction. We'll build the desugared form the hard
|
|
||||||
way, then show you where we put the sugar in.
|
|
||||||
|
|
||||||
The desugared canonical decrement:
|
|
||||||
```
|
|
||||||
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=+ gat=duck
|
|
||||||
=<(run gat(sam arg))
|
|
||||||
=> ~
|
|
||||||
|%
|
|
||||||
++ duck
|
|
||||||
=+ sam=0
|
|
||||||
|%
|
|
||||||
++ run
|
|
||||||
=+ pre=0
|
|
||||||
=< loop
|
|
||||||
|%
|
|
||||||
++ loop
|
|
||||||
?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
loop(pre +(pre))
|
|
||||||
--
|
|
||||||
--
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
Yuck. Okay, let's fix this.
|
|
||||||
|
|
||||||
## Art of the loop
|
|
||||||
|
|
||||||
First, look at our little `++loop`. It works just like our old
|
|
||||||
`++run` loop. We notice that there's actually something nice
|
|
||||||
about it: we don't use the symbol `loop` anywhere outside these 7
|
|
||||||
lines of code. It's not exported at all.
|
|
||||||
|
|
||||||
Actually, the symbol `loop` name is useless and redundant.
|
|
||||||
Making up names is one of the hard problems in computer science,
|
|
||||||
so why solve it? For just this reason, Hoon has an *empty name*,
|
|
||||||
which as a constant is a zero-length symbol (`%$` instead of
|
|
||||||
`%foo`), and as a limb is the `buc` symbol (`$`). With `$`,
|
|
||||||
our loop becomes:
|
|
||||||
```
|
|
||||||
=< $
|
|
||||||
|%
|
|
||||||
++ $
|
|
||||||
?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
$(sam +(run))
|
|
||||||
--
|
|
||||||
```
|
|
||||||
This may not seem like a huge improvement. It's not. But it's
|
|
||||||
exactly equivalent to the synthetic rune `|-`, "bardas":
|
|
||||||
```
|
|
||||||
|- ?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
$(pre +(pre))
|
|
||||||
```
|
|
||||||
This is obviously the canonical Hoon loop. It leaves us with
|
|
||||||
```
|
|
||||||
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=+ gat=duck
|
|
||||||
=<(run gat(sam arg))
|
|
||||||
=> ~
|
|
||||||
|%
|
|
||||||
++ duck
|
|
||||||
=+ sam=0
|
|
||||||
|%
|
|
||||||
++ run
|
|
||||||
=+ pre=0
|
|
||||||
|- ?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
$(pre +(pre))
|
|
||||||
--
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
|
|
||||||
## Is this a lambda?
|
|
||||||
|
|
||||||
Could we use `$` for `++run`? It certainly sounds like the same
|
|
||||||
kind of thing as `++loop` -- just a word we invented to mean "do
|
|
||||||
it." Should the programmer have to invent these kinds of words?
|
|
||||||
```
|
|
||||||
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=+ gat=duck
|
|
||||||
=<($ gat(sam arg))
|
|
||||||
=> ~
|
|
||||||
|%
|
|
||||||
++ duck
|
|
||||||
=| sam=@ud
|
|
||||||
|%
|
|
||||||
=+ pre=0
|
|
||||||
++ $
|
|
||||||
|- ?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
$(pre +(pre))
|
|
||||||
--
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
(Besides `run` to `$`, we changed `=+ sam=0` to `=| sam=@ud`.
|
|
||||||
Let's just remember that there's some magic here. We'll come
|
|
||||||
back and explain it later.)
|
|
||||||
|
|
||||||
This is still kind of ugly -- but it's exactly equivalent to
|
|
||||||
```
|
|
||||||
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
||||||
=+ gat=duck
|
|
||||||
=<($ gat(sam arg))
|
|
||||||
=> ~
|
|
||||||
|%
|
|
||||||
++ duck
|
|
||||||
|= sam=@ud
|
|
||||||
=+ pre=0
|
|
||||||
|- ?: =(sam +(pre))
|
|
||||||
pre
|
|
||||||
$(pre +(pre))
|
|
||||||
--
|
|
||||||
|
|
||||||
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
||||||
41
|
|
||||||
```
|
|
||||||
Doesn't that look like a function? Indeed, we're done with
|
|
||||||
`++duck` -- that's what a Hoon decrement should look like.
|
|
||||||
If you squint a little, `|=` ("bartis") might even be a strange,
|
|
||||||
deformed lambda rune.
|
|
||||||
|
|
||||||
Since it's doing something simple, we might well even compress
|
|
||||||
the whole body of the function into one wide-form line:
|
|
||||||
```
|
|
||||||
=+(pre=0 |-(?:(=(sam +(pre)) pre $(pre +(pre)))))
|
|
||||||
```
|
|
||||||
(According, of course, to taste -- this is a bit tight for some.)
|
|
||||||
|
|
||||||
## Gates and how to call them
|
|
||||||
|
|
||||||
Our call site remains a disaster, though. We'll need moar sugar.
|
|
||||||
|
|
||||||
But first, let's look at this lambda-thing we've made. What is
|
|
||||||
the noun produced by `++duck`? Our term for it is a "gate," but
|
|
||||||
nobody will hate you for saying "function." And while we "slam"
|
|
||||||
our gates, you can feel free to just "call" them.
|
|
||||||
|
|
||||||
A gate is a core, of course, but a special kind of core. All
|
|
||||||
cores are shaped like `[battery payload]`. A gate is shaped like
|
|
||||||
`[formula [sample context]]`. A gate has one arm, `$`, so its
|
|
||||||
battery is just a formula. To slam a gate, you replace its
|
|
||||||
sample (`+6` or `+<`, "luslit" or "lust") with your own noun,
|
|
||||||
and apply the formula to the mutated gate.
|
|
||||||
|
|
||||||
As we explained earlier, `duck(sam arg)` is not the right way to
|
|
||||||
mutate the gate we make with `duck`, because it's actually
|
|
||||||
trying to mutate the core we used to make `duck`. But there has
|
|
||||||
to be some sugar to do this, and there is: `%*`, "centar". We
|
|
||||||
can replace our call site with `%*($ duck sam arg)`.
|
|
||||||
|
|
||||||
This is also not quite orthodox, because the whole point of a
|
|
||||||
gate is the canonical shape that defines a calling convention.
|
|
||||||
We can and should say: `%*($ duck +< arg)`.
|
|
||||||
|
|
||||||
Unsurprisingly, this in turn is `%-(duck arg)` in regular form,
|
|
||||||
or `(duck arg)`
|
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
logo: black
|
logo: black
|
||||||
title: User doc
|
|
||||||
sort: 1
|
sort: 1
|
||||||
|
title: User doc
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="short">
|
<div class="short">
|
||||||
@ -10,9 +10,9 @@ sort: 1
|
|||||||
|
|
||||||
Read the [introduction](user/intro) for a summary of Urbit.
|
Read the [introduction](user/intro) for a summary of Urbit.
|
||||||
The [installation guide](user/install) gets you ready to run.
|
The [installation guide](user/install) gets you ready to run.
|
||||||
The [launch procedure](user/launch) holds your hand as you create
|
Launch with [basic operation](user/basic) and get a feel for the
|
||||||
your server image. Once your urbit is live, the [quickstart
|
system. Once your urbit is live, the [walking tour](user/tour)
|
||||||
page](user/start) is all you need if in a hurry.
|
shows how to do some common tasks.
|
||||||
|
|
||||||
For power users, the [appliance handbook](user/appliance) explains
|
For power users, the [appliance handbook](user/appliance) explains
|
||||||
your apps and how to control them. The [filesystem handbook](user/clay)
|
your apps and how to control them. The [filesystem handbook](user/clay)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Appliance handbook
|
|
||||||
sort: 5
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 5
|
||||||
|
title: Appliance handbook
|
||||||
---
|
---
|
||||||
|
|
||||||
# Appliance handbook
|
# Appliance handbook
|
||||||
|
143
pub/docs/user/basic.mdy
Normal file
143
pub/docs/user/basic.mdy
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
next: true
|
||||||
|
sort: 3
|
||||||
|
title: Basic Operation
|
||||||
|
---
|
||||||
|
|
||||||
|
# Basic Operation
|
||||||
|
|
||||||
|
Once urbit is installed, if you have an invitation, it's a planet
|
||||||
|
like `~fintud-macrep` and a ticket like
|
||||||
|
`~fortyv-tombyt-tabsen-sonres`. Run
|
||||||
|
|
||||||
|
bin/urbit -w fintud-macrep -t fortyv-tombyt-tabsen-sonres
|
||||||
|
|
||||||
|
(You can leave the `~` on, but it annoys some Unix shells.)
|
||||||
|
|
||||||
|
If you don't have an invitation, pick a nickname for your comet,
|
||||||
|
like `mycomet`. Urbit will randomly generate a 128-bit plot:
|
||||||
|
|
||||||
|
bin/urbit -c mycomet
|
||||||
|
|
||||||
|
Either way, creating your urbit will take some time. Some of
|
||||||
|
this time involves generating keys; some of it involves
|
||||||
|
downloading code over Urbit itself.
|
||||||
|
|
||||||
|
If you turn off your pier at any time (either "nicely" with `^D`
|
||||||
|
from talk or the dojo or by power-failure, oom-killer, or other
|
||||||
|
calamity), then you can start it up again with the path to your
|
||||||
|
pier directory (your planet name if you have an invitation,
|
||||||
|
otherwise the name you chose after `-c`):
|
||||||
|
|
||||||
|
bin/urbit fintud-macrep
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
bin/urbit mycomet
|
||||||
|
|
||||||
|
## Basic operation
|
||||||
|
|
||||||
|
Out of the box, your urbit is running two default appliances,
|
||||||
|
`:dojo` (a shell or REPL) and `:talk`. Switch between them with
|
||||||
|
`^X`. Note that all apps share an output log, but `^X` switches
|
||||||
|
the prompt.
|
||||||
|
|
||||||
|
`^D` from either default appliance exits the urbit process.
|
||||||
|
|
||||||
|
If your plot is `~fintud-macrep`, the dojo prompt is
|
||||||
|
|
||||||
|
~fintud-macrep:dojo>
|
||||||
|
|
||||||
|
Type any Hoon expression at the command line and see the result:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> (add 2 2)
|
||||||
|
|
||||||
|
You'll see:
|
||||||
|
|
||||||
|
> (add 2 2)
|
||||||
|
4
|
||||||
|
~fintud-macrep:dojo>
|
||||||
|
|
||||||
|
While there are some tools for interacting with urbit's
|
||||||
|
filesystem direclty, often it's useful to edit urbit files from
|
||||||
|
Unix. Use `|mount` to set up a Dropbox-style sync directory in
|
||||||
|
your pier directory:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> |mount %
|
||||||
|
>=
|
||||||
|
~fintud-macrep:dojo> |unmount %
|
||||||
|
>=
|
||||||
|
|
||||||
|
Look inside your pier directory to find your files, and edit them
|
||||||
|
with your favorite Unix text editor.
|
||||||
|
|
||||||
|
Sometimes you want to get files from another urbit. There's two
|
||||||
|
main commands to do this.
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> |merge %examples ~wactex-ribmex %examples
|
||||||
|
>=
|
||||||
|
; ~wactex-ribmex is your neighbor
|
||||||
|
; ~wactex-ribmex is your neighbor
|
||||||
|
[time passes...]
|
||||||
|
merged with strategy %init
|
||||||
|
|
||||||
|
This pulls a bunch of examples from `~wactex-ribmex`'s `%examples`
|
||||||
|
desk and puts them in our `%examples` desk. A desk is a
|
||||||
|
branch (in the git branch sense) of our filesystem.
|
||||||
|
|
||||||
|
You can, of course, merge the examples into your `%home`
|
||||||
|
(default) desk if you wish. Merging into a new desk creates it,
|
||||||
|
while merging into an existing desk does an intelligent git-style
|
||||||
|
merge, with conflict resolution and stuff.
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> |sync %examples ~wactex-ribmex %examples
|
||||||
|
>=
|
||||||
|
activated sync from %examples on ~wactex-ribmex to %examples
|
||||||
|
sync succeeded from %examples on ~wactex-ribmex to %examples
|
||||||
|
~fintud-macrep:dojo> |unsync %examples ~wactex-ribmex %examples
|
||||||
|
ended autosync from %examples on ~wactex-ribmex to %examples
|
||||||
|
|
||||||
|
`|sync` does an initial `|merge`, then listens for changes on the
|
||||||
|
foreign desk and runs `|merge` automatically whenever it changes.
|
||||||
|
By default your `%home` desk is synced to your parent's `%kids`
|
||||||
|
desk, so the entire network is upgraded at once. You may, of
|
||||||
|
course, unsync it, but this voids your warranty.
|
||||||
|
|
||||||
|
You can change which desk is your current working directory with:
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> =dir /=examples
|
||||||
|
|
||||||
|
> `dir` is a special dojo variable, but you can also create your
|
||||||
|
> own variables with the same syntax. `=my-var (add 2 3)` allows
|
||||||
|
> you to use `my-var` for `5` anywhere later on.
|
||||||
|
|
||||||
|
When you change your desk all commands will use the files from
|
||||||
|
that desk. Unfortunately, changing your working directory to
|
||||||
|
anywhere but the top directory is currently
|
||||||
|
[broken](http://github.com/urbit/urbit/issues/565). If this is
|
||||||
|
fixed when you're reading this, please submit a pull request for
|
||||||
|
this document explaining how to do it!
|
||||||
|
|
||||||
|
The last command we'll discuss in this quickstart is `+moon`,
|
||||||
|
which only works on planets.
|
||||||
|
|
||||||
|
~fintud-macrep:dojo> +moon
|
||||||
|
"moon: ~ramnec-rossug-fintud-macrep; ticket: ~holtyl-mognyl-dostyp-moslud"
|
||||||
|
|
||||||
|
This generates a random moon and ticket. A moon is a subidentity
|
||||||
|
of your planet, so it's a separate urbit that's associated with
|
||||||
|
your planet. If your planet is off, then your moon won't
|
||||||
|
function very well over the network (it'll work just fine
|
||||||
|
locally, of coures).
|
||||||
|
|
||||||
|
You can start up your moon with `bin/urbit -w moon-name -t
|
||||||
|
ticket`. We recommend doing this for development because there
|
||||||
|
are still some instabilities that have the potential to explode
|
||||||
|
your planet if you develop on it. If this happens, then your
|
||||||
|
planet will be unusable until the next continuity breach. If you
|
||||||
|
tell us how you exploded your planet, we may give you a
|
||||||
|
replacement, but it's still a hassle for you.
|
||||||
|
|
||||||
|
Don't worry too much about exploding your moons -- you have four
|
||||||
|
billion of them, and they get replenished after every continuity
|
||||||
|
breach. Still do file a bug, though.
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Filesystem handbook
|
|
||||||
sort: 6
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 6
|
||||||
|
title: Filesystem handbook
|
||||||
---
|
---
|
||||||
|
|
||||||
# Filesystem handbook
|
# Filesystem handbook
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Dojo manual
|
|
||||||
sort: 8
|
sort: 8
|
||||||
|
title: Dojo manual
|
||||||
---
|
---
|
||||||
|
|
||||||
# `:dojo` manual
|
# `:dojo` manual
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Install
|
|
||||||
sort: 2
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 2
|
||||||
|
title: Install
|
||||||
---
|
---
|
||||||
|
|
||||||
# Installation guide
|
# Installation guide
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Introduction
|
|
||||||
sort: 1
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 1
|
||||||
|
title: Introduction
|
||||||
---
|
---
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: Launch
|
hide: true
|
||||||
sort: 3
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 10
|
||||||
|
title: Launch
|
||||||
---
|
---
|
||||||
|
|
||||||
# Launch procedure
|
# Launch procedure
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Talk manual
|
|
||||||
sort: 7
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 7
|
||||||
|
title: Talk manual
|
||||||
---
|
---
|
||||||
|
|
||||||
# `:talk` manual
|
# `:talk` manual
|
||||||
|
@ -1,20 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Quickstart
|
|
||||||
sort: 4
|
|
||||||
next: true
|
next: true
|
||||||
|
sort: 4
|
||||||
|
title: Walking Tour
|
||||||
---
|
---
|
||||||
|
|
||||||
# Quickstart
|
# Walking Tour
|
||||||
|
|
||||||
To start your already-launched urbit, just run `urbit` with one
|
|
||||||
argument, which is the pier directory (`$PIER`). This is your
|
|
||||||
planet name if you have a planet, or the name you used with `-c`
|
|
||||||
if you have a comet:
|
|
||||||
|
|
||||||
bin/urbit fintud-macrep
|
|
||||||
or
|
|
||||||
|
|
||||||
bin/urbit mycomet
|
|
||||||
|
|
||||||
Piers are portable. You can move a pier anywhere. But never,
|
Piers are portable. You can move a pier anywhere. But never,
|
||||||
*ever* run the same urbit in two places at once. (If you try to
|
*ever* run the same urbit in two places at once. (If you try to
|
||||||
@ -24,31 +14,6 @@ pier, Urbit will kill the old process.)
|
|||||||
(Also, don't let the Unix filesystem it's on run out of disk.
|
(Also, don't let the Unix filesystem it's on run out of disk.
|
||||||
This is a known way to corrupt your urbit! Sorry.)
|
This is a known way to corrupt your urbit! Sorry.)
|
||||||
|
|
||||||
## Basic operation
|
|
||||||
|
|
||||||
Out of the box, your urbit is running two default appliances,
|
|
||||||
`:dojo` (a shell or REPL) and `:talk`. Switch between them with
|
|
||||||
`^X`. Note that all apps share an output log, but `^X` switches
|
|
||||||
the prompt.
|
|
||||||
|
|
||||||
`^D` from any default appliance exits the urbit process.
|
|
||||||
|
|
||||||
## Compute: your `:dojo` appliance
|
|
||||||
|
|
||||||
If your plot is `~fintud-macrep`, the dojo prompt is
|
|
||||||
|
|
||||||
~fintud-macrep:dojo>
|
|
||||||
|
|
||||||
Type any Hoon expression at the command line and see the result:
|
|
||||||
|
|
||||||
~fintud-macrep:dojo> (add 2 2)
|
|
||||||
|
|
||||||
You'll see:
|
|
||||||
|
|
||||||
> (add 2 2)
|
|
||||||
4
|
|
||||||
~fintud-macrep:dojo>
|
|
||||||
|
|
||||||
### Dojo expressions, generators and operators
|
### Dojo expressions, generators and operators
|
||||||
|
|
||||||
`:dojo` is of course a command-line REPL or shell. But it
|
`:dojo` is of course a command-line REPL or shell. But it
|
||||||
@ -96,7 +61,8 @@ You're on the air! You should see some backlog to give you
|
|||||||
context. Please remember our code of conduct: don't be rude.
|
context. Please remember our code of conduct: don't be rude.
|
||||||
Also, `urbit-meta` is politically correct and safe for work.
|
Also, `urbit-meta` is politically correct and safe for work.
|
||||||
|
|
||||||
For more instructions on how to use `:talk`, see the [`:talk` manual](http://urbit.org/docs/user/talk)
|
For more instructions on how to use `:talk`, see the [`:talk`
|
||||||
|
manual](http://urbit.org/docs/user/talk)
|
||||||
|
|
||||||
## Using the filesystem
|
## Using the filesystem
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
::
|
::
|
||||||
:::: /hoon/sole/sur
|
:::: /hoon/sole/sur
|
||||||
!:
|
!:
|
||||||
|%
|
|%
|
||||||
@ -36,6 +36,7 @@
|
|||||||
[%tan p=(list tank)] :: classic tank
|
[%tan p=(list tank)] :: classic tank
|
||||||
:: [%taq p=tanq] :: modern tank
|
:: [%taq p=tanq] :: modern tank
|
||||||
[%txt p=tape] :: text line
|
[%txt p=tape] :: text line
|
||||||
|
[%url p=@t] :: activate url
|
||||||
== ::
|
== ::
|
||||||
++ sole-command :: command state
|
++ sole-command :: command state
|
||||||
$: pos=@ud :: cursor position
|
$: pos=@ud :: cursor position
|
||||||
|
Loading…
Reference in New Issue
Block a user