mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-26 08:25:04 +03:00
Remove old documentation
This commit is contained in:
parent
cffad3bf6e
commit
6f25ef4d72
@ -1,438 +0,0 @@
|
|||||||
Anatomy of Watt: 1, Molds
|
|
||||||
|
|
||||||
One of the errors most people make when they write their first
|
|
||||||
few programming languages is to specify the language before they
|
|
||||||
write the implementation. They then compound this error by
|
|
||||||
designing the language before they specify it. And worst of all,
|
|
||||||
they describe it before they design it.
|
|
||||||
|
|
||||||
Having made all these mistakes more than once myself, I was
|
|
||||||
determined to avoid them in Watt. Fortunately, I now have a
|
|
||||||
reasonably complete and working Watt, so I can describe it! The
|
|
||||||
best way to learn Watt will always be to read the code, though.
|
|
||||||
|
|
||||||
[Note that this is a DRAFT description; Watt, still, does not
|
|
||||||
exist. It will not even have a Kelvin number until it boots.
|
|
||||||
Moreover, instead of "reading the code" in Watt, you have to read
|
|
||||||
it in C and Yacc - both harder, and less rewarding. Also, I
|
|
||||||
would avoid actually playing with the build for a couple of days
|
|
||||||
- there is still some medium-scale surgery to be done. Besides,
|
|
||||||
more actual lessons will follow.]
|
|
||||||
|
|
||||||
Watt is based on Nock, of course. Most specifically, Watt is
|
|
||||||
defined as a pair of gates in the Watt kernel: the Watt grammar
|
|
||||||
and the Mill composer.
|
|
||||||
|
|
||||||
:::::::: Watt: the grammar
|
|
||||||
::
|
|
||||||
- watt
|
|
||||||
|| gene
|
|
||||||
src = text
|
|
||||||
==
|
|
||||||
:: Parse Watt source.
|
|
||||||
::
|
|
||||||
[...]
|
|
||||||
|
|
||||||
:::::::: Mill: the composer
|
|
||||||
::
|
|
||||||
- mill
|
|
||||||
|| [mol=mold noc=nock]
|
|
||||||
sub = mold
|
|
||||||
gen = gene
|
|
||||||
==
|
|
||||||
:: Generate Nock code and infer type.
|
|
||||||
::
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Here at Moron Lab, we are of the total-immersion school of
|
|
||||||
programming-language pedagogy. One of the many reasons it's
|
|
||||||
foolish to write your language documentation before your language
|
|
||||||
is that it's just impossible to keep your pseudocode up to date.
|
|
||||||
|
|
||||||
Therefore, let's dive into Watt with some straight-up code.
|
|
||||||
Everything below is working Watt or my error. I swear.
|
|
||||||
|
|
||||||
So if the syntax above is not self-explanatory to you, here is
|
|
||||||
a trivial decrement in Watt:
|
|
||||||
|
|
||||||
|| @
|
|
||||||
z = @
|
|
||||||
==
|
|
||||||
:: Transform [z] to "(z - 1)"
|
|
||||||
::
|
|
||||||
=> a 0
|
|
||||||
|-
|
|
||||||
?: (eq z (inc a))
|
|
||||||
a
|
|
||||||
$(a (inc a))
|
|
||||||
|
|
||||||
The algorithm (which is O(n)) should be obvious on inspection.
|
|
||||||
"@" is pronounced "atom."
|
|
||||||
|
|
||||||
We can also write Nock in Watt. Note that this code cheats: it
|
|
||||||
delegates basic Nock functions to... Nock. The whole exercise
|
|
||||||
is pointless, because we could express it as
|
|
||||||
|
|
||||||
|| *
|
|
||||||
z = *
|
|
||||||
==
|
|
||||||
:3.z
|
|
||||||
|
|
||||||
Nonetheless, this code closely resembles the present spec and
|
|
||||||
functions perfectly as written below. "*" is pronounced "blur,"
|
|
||||||
meaning "any noun."
|
|
||||||
|
|
||||||
|| *
|
|
||||||
z = *
|
|
||||||
==
|
|
||||||
:: Compute "Nock(a)" - 9K.
|
|
||||||
::
|
|
||||||
?- z
|
|
||||||
- [a 0 b=@]
|
|
||||||
$(z :3.[a.z 0 b.z]) :: cheating
|
|
||||||
|
|
||||||
- [a 1 b=*]
|
|
||||||
b
|
|
||||||
|
|
||||||
- [a 2 b=* c=* d=*]
|
|
||||||
$(z [a.z 3 [0 1] 3 [1 c.z d.z] [1 0] 3 [1 2 3] [1 0] 5 5 b.z])
|
|
||||||
|
|
||||||
- [a 3 b=*]
|
|
||||||
$(z $(z [a.z b.z]))
|
|
||||||
|
|
||||||
- [a 4 b=*]
|
|
||||||
$(z :3.[a.z 4 b.z]) :: cheating
|
|
||||||
|
|
||||||
- [a 5 b=*]
|
|
||||||
$(z :3.[a.z 5 b.z]) :: cheating
|
|
||||||
|
|
||||||
- [a 6 b=*]
|
|
||||||
$(z :3.[a.z 6 b.z]) :: cheating
|
|
||||||
|
|
||||||
- [[a=* b=*] c=*]
|
|
||||||
[$(z [a.z b.z]) $(z c.z)]
|
|
||||||
==
|
|
||||||
|
|
||||||
We could also, of course, write:
|
|
||||||
|
|
||||||
- [a 2 b=* c=* d=*]
|
|
||||||
?: $(z [a.z b.z])
|
|
||||||
$(z a.z c.z)
|
|
||||||
$(z a.z d.z)
|
|
||||||
|
|
||||||
Which should give the semantics of 2 away!
|
|
||||||
|
|
||||||
But this is an entirely intuitive introduction. It should under
|
|
||||||
no circumstances be mistaken for a definition, or even a tutorial.
|
|
||||||
It is just meant to explain what I mean when I use Watt syntax,
|
|
||||||
in a paralinguistic Rosetta-Stone sort of way.
|
|
||||||
|
|
||||||
To understand Watt in a concrete way, let's see those gates again:
|
|
||||||
|
|
||||||
:::::::: Watt: the reader (ie, syntax)
|
|
||||||
::
|
|
||||||
- watt
|
|
||||||
|| gene
|
|
||||||
src = text
|
|
||||||
==
|
|
||||||
:: Parse source.
|
|
||||||
::
|
|
||||||
[...]
|
|
||||||
|
|
||||||
:::::::: Mill: the composer (ie, compiler)
|
|
||||||
::
|
|
||||||
- mill
|
|
||||||
|| [mol=mold noc=nock]
|
|
||||||
sub = mold
|
|
||||||
gen = gene
|
|
||||||
==
|
|
||||||
:: Generate code and infer type.
|
|
||||||
::
|
|
||||||
[...]
|
|
||||||
|
|
||||||
In programmer-English, Watt is a function that transforms a text
|
|
||||||
file (as a Nock atom) into a Watt AST (as a Nock noun). Mill is
|
|
||||||
a function that transforms a Watt type (again a Nock noun, of
|
|
||||||
course) into a Watt type and a Nock formula.
|
|
||||||
|
|
||||||
A Watt type is called a "mold." The abstract syntax tree (ie,
|
|
||||||
your code read in, with minimal processing, as a data structure)
|
|
||||||
is called a "gene."
|
|
||||||
|
|
||||||
(Why this unusual language? Why not call a type a type? Because,
|
|
||||||
as a programmer, the word "type" already means something to you.
|
|
||||||
Moreover, there is actually more than one concept in Watt for
|
|
||||||
which we might use the word "type." It's entirely possible that
|
|
||||||
people will just end up calling molds "types," but anyone you see
|
|
||||||
instantiating this meme should be vigorously chastised! As for
|
|
||||||
the superiority of "gene" over "AST", I will admit no argument.)
|
|
||||||
|
|
||||||
Mill is a function that transforms an ordered pair of
|
|
||||||
two nouns, [sub gen], to another ordered pair, [mol noc].
|
|
||||||
|
|
||||||
Let's look at these mysterious nouns, the molds. Here is the
|
|
||||||
definition of a mold:
|
|
||||||
|
|
||||||
:::::::: mold
|
|
||||||
::
|
|
||||||
- mold
|
|
||||||
|~
|
|
||||||
**
|
|
||||||
%atom
|
|
||||||
%blot
|
|
||||||
%blur
|
|
||||||
[%cell p=mold q=mold]
|
|
||||||
[%cone p=mold q=(bush mark gene)]
|
|
||||||
[%cube p=*]
|
|
||||||
[%face p=mark q=mold]
|
|
||||||
[%fork p=mold q=mold]
|
|
||||||
[%fuse p=mold q=mold]
|
|
||||||
[%hold p=mold q=gene]
|
|
||||||
==
|
|
||||||
|
|
||||||
Peculiarly enough, this gate is both the definition of a mold and
|
|
||||||
its implementation. For instance, if you have a random noun, n,
|
|
||||||
which you're hoping might be a mold,
|
|
||||||
|
|
||||||
(mold n)
|
|
||||||
|
|
||||||
will produce n if n is a mold, and exit if n is not a mold. The
|
|
||||||
result, of course, will be in mold [mold] - ie, it will be typed.
|
|
||||||
(We say "in mold foo" instead of "of type foo.")
|
|
||||||
|
|
||||||
This is called "rectifying" n. Rectification is quite useful in
|
|
||||||
the context of network communication - it is common to receive
|
|
||||||
generic, unvalidated data from foreign sources, and need to
|
|
||||||
import it into a strictly-typed programming language, a task
|
|
||||||
commonly done ad hoc or not at all.
|
|
||||||
|
|
||||||
But I digress. Let's describe the type system - I mean, the
|
|
||||||
mold system.
|
|
||||||
|
|
||||||
First, the philosophy of this system is the same minimalism
|
|
||||||
you've already seen in Nock. Unlike Nock, Watt is not quite
|
|
||||||
complete - there are a few bells and whistles I want to add,
|
|
||||||
which will result in two or three more forks of mold. However,
|
|
||||||
the definition above is sufficient for quite a rich programming
|
|
||||||
experience. The bells and whistles will make rectification more
|
|
||||||
powerful and facilitate object-oriented programming. Even with
|
|
||||||
the above, however, OOP is doable - just a little "ghetto."
|
|
||||||
|
|
||||||
Let's walk through this data structure very quickly, and we'll
|
|
||||||
keep your introduction to Watt short and easy to chew.
|
|
||||||
|
|
||||||
A mold satisfies the two basic requirements of any type or data
|
|
||||||
description system.
|
|
||||||
|
|
||||||
One: a mold unambiguously defines a set of nouns. For any mold and
|
|
||||||
any noun, that noun is either in the mold or not in the mold.
|
|
||||||
Without the bells and whistles, we cannot describe all such sets,
|
|
||||||
but even without the bells and whistles we can express just about
|
|
||||||
any practical data structure.
|
|
||||||
|
|
||||||
(For instance, a mold is to nouns much as an XML DTD or schema is
|
|
||||||
to valid XML. The difference is that nouns are much simpler than
|
|
||||||
XML; molds are much, much, much simpler than XML Schema; and XML
|
|
||||||
does not ship with a functional programming language. So it's a
|
|
||||||
little like comparing an oxcart to a Ducati. But both are
|
|
||||||
perfectly effective ways of getting from point A to point B.)
|
|
||||||
|
|
||||||
Two: a mold ascribes semantics to nouns in the mold. More
|
|
||||||
precisely, it defines a namespace on the noun. A name in Watt is
|
|
||||||
called a "port"; a path is a "wire." When you see [a.z] above,
|
|
||||||
this is referencing port "a" in port "z" in the subject. (Note
|
|
||||||
that in most Earth programming languages, this would be "z.a".)
|
|
||||||
[$] is simply the name 0 - sometimes a name is superfluous.
|
|
||||||
|
|
||||||
Ports can be fragments of the subject ("parts") or computed
|
|
||||||
functions ("hooks"). You can also hack into a noun with a direct
|
|
||||||
fragment address - eg, [+2] for the head, [+3] for the tail.
|
|
||||||
|
|
||||||
Lisp programmers learning Watt may be slightly puzzled by the
|
|
||||||
lack of an environment. Indeed, the simplest way to describe
|
|
||||||
Watt to someone who knows Lisp is: Lisp without an environment.
|
|
||||||
|
|
||||||
Instead, every Watt gene is evaluated relative to a single noun
|
|
||||||
which contains all data *and code* available to that gene. This
|
|
||||||
is called "the subject." Thus, when we compute
|
|
||||||
|
|
||||||
(mill mol gen)
|
|
||||||
|
|
||||||
and produce a product
|
|
||||||
|
|
||||||
[pro=[mol=mold noc=nock]]
|
|
||||||
|
|
||||||
we are saying: produce the Nock formula, [noc.pro], that computes
|
|
||||||
the function defined by [gen] on a subject in mold [mol]. The
|
|
||||||
result of this function will be in mold [mol.pro].
|
|
||||||
|
|
||||||
Let's list the mold forks quickly, and we'll be done with this
|
|
||||||
lesson. Frankly, my previous essays have been too long. There
|
|
||||||
are two kinds of mold: the obvious kind, and the non-obvious
|
|
||||||
kind. First, the former.
|
|
||||||
|
|
||||||
|
|
||||||
Three atomic molds: %atom, %blot, %blur. %atom means: any
|
|
||||||
atom. %blur means: any noun. %blot means: no nouns (ie,
|
|
||||||
"void"). I trust there is no rocket science here. None of these
|
|
||||||
export any ports.
|
|
||||||
|
|
||||||
[%cube p=*]: a constant noun. Any noun that equals [p] is in the
|
|
||||||
mold; any noun that does not, is not. No ports are exported.
|
|
||||||
|
|
||||||
[%cell p=mold q=mold]: a cell, ie, ordered pair. [p] is the mold
|
|
||||||
of the head; [q] the mold of the tail. Exports all ports of [p]
|
|
||||||
and [q]; any conflict is an error.
|
|
||||||
|
|
||||||
[%fork p=mold q=mold]: set union of the molds [p] and [q].
|
|
||||||
Exports any port for which [p] and [q] define the same formula.
|
|
||||||
Symmetric; [%fork q p] has the same semantics as [%fork p q].
|
|
||||||
|
|
||||||
[%fuse p=mold q=mold]: set intersection of the molds [p] and [q].
|
|
||||||
Asymmetric; [q] has higher priority, ie, [p] cannot change or
|
|
||||||
conceal the semantics of [q].
|
|
||||||
|
|
||||||
[%face p=mark q=mold]: name a part. Exports the name [p]. For
|
|
||||||
ports in [q], read through [p] - eg, in the Nock example above,
|
|
||||||
[a.z] works but [a] will give you "port not found." [p] may not
|
|
||||||
be 0 - port 0 is always a hook, never a part.
|
|
||||||
|
|
||||||
Now, the above are all the *obvious* molds. If you were writing
|
|
||||||
a simple data-description model, the noun equivalent of a DTD,
|
|
||||||
for finite static nouns, they might even be sufficient. But for
|
|
||||||
actual functional programming, we need two more: %cone and %hold.
|
|
||||||
If your brain has been nodding "duh, duh, of course" up to this
|
|
||||||
point, it's time for it to sit up and pay attention!
|
|
||||||
|
|
||||||
|
|
||||||
[%cone p=mold q=(bush mark gene)]: executable code. A bush is
|
|
||||||
simply a data structure which, like Nock formulas, overloads the
|
|
||||||
head atom: it is either
|
|
||||||
|
|
||||||
[mark gene]
|
|
||||||
|
|
||||||
mark being an atom - ie, a symbol - or
|
|
||||||
|
|
||||||
[(bush mark gene) (bush mark gene)]
|
|
||||||
|
|
||||||
with a tree search order. Thus, what we're looking at here is...
|
|
||||||
a symbol table. Very comparable to a vtable in C++. Okay, we
|
|
||||||
may still be in "duh" territory.
|
|
||||||
|
|
||||||
The nouns in the cone are of the form [data code]. [data]
|
|
||||||
is a noun in the mold [p]. [code] matches the geometry of [q];
|
|
||||||
every leaf
|
|
||||||
|
|
||||||
[mark gene]
|
|
||||||
|
|
||||||
becomes the Nock formula
|
|
||||||
|
|
||||||
noc.(mill [%cone p q] gene)
|
|
||||||
|
|
||||||
A cone exports all the marks in its book as hooks. If the port
|
|
||||||
equals the mark, the formula produced calls the hook formula,
|
|
||||||
with a subject that is the entire cone (not just the cone head).
|
|
||||||
|
|
||||||
So, for instance, the Nock equivalent of a lambda is a "gate."
|
|
||||||
This is always a mold of the form
|
|
||||||
|
|
||||||
[%cone
|
|
||||||
[%cell arg=mold env=mold]
|
|
||||||
[0 gene]
|
|
||||||
]
|
|
||||||
|
|
||||||
Ie, the data in a gate is an argument-environment pair (so there
|
|
||||||
*is* an environment - it's just reentrant), and a hook whose name
|
|
||||||
is 0 (ie, [$]). To invoke the lambda, replace the default
|
|
||||||
argument with a compatible value of your choice, and use the
|
|
||||||
hook.
|
|
||||||
|
|
||||||
When you write
|
|
||||||
|
|
||||||
(dec x)
|
|
||||||
|
|
||||||
you are actually invoking *two* hooks. First, [dec] resolves to
|
|
||||||
a hook in the kernel which produces a decrement gate. Second,
|
|
||||||
you replace the argument [+4] in that gate with [arg], and invoke
|
|
||||||
the formula.
|
|
||||||
|
|
||||||
Note, in particular, that Watt never calculates "type signatures"
|
|
||||||
for code. Rather, the gene is simply embedded in the mold! And
|
|
||||||
since different genes generate different formulas, cones with
|
|
||||||
different books are never in any sense compatible. If this makes
|
|
||||||
you wonder how we do higher-order polymorphism, it should!
|
|
||||||
Suffice it to say: there's a trick.
|
|
||||||
|
|
||||||
|
|
||||||
[%hold p=mold q=gene]: delayed evaluation. For all but cosmetic
|
|
||||||
purposes, [%hold p q] is equivalent to
|
|
||||||
|
|
||||||
mol.(mill p q)
|
|
||||||
|
|
||||||
Ie, [%hold p q] is the mold produced by gene [q] in mold [p].
|
|
||||||
|
|
||||||
Some programming languages support infinite data structures.
|
|
||||||
Watt is not one of these; all Watt data structures are Nock
|
|
||||||
nouns, and all Nock nouns are finite.
|
|
||||||
|
|
||||||
However, we often find ourselves needing to describe *indefinite*
|
|
||||||
data structures. For example, a list of pairs of atoms:
|
|
||||||
|
|
||||||
(list :[@ @])
|
|
||||||
|
|
||||||
Nouns in this mold include
|
|
||||||
|
|
||||||
0
|
|
||||||
[[3 7] 0]
|
|
||||||
[[2 3] [37 42] 0]
|
|
||||||
|
|
||||||
And so on. Clearly, a list can be as long as you want. Clearly,
|
|
||||||
since molds are Nock nouns, and Nock nouns are finite, we must
|
|
||||||
define this infinitely-large set, containing indefinitely-large
|
|
||||||
nouns, with a single mold which is a finite noun. Yikes!
|
|
||||||
|
|
||||||
Nor is [list] a built-in feature. If you scroll back up to the
|
|
||||||
mold definition, you will not see any mention of lists - or any
|
|
||||||
other generic data structure. Actually, [list] is just a gate
|
|
||||||
in the kernel, like [dec] or [add] or [mill].
|
|
||||||
|
|
||||||
So when I write
|
|
||||||
|
|
||||||
((list :[@ @]) [[2 3] [37 42] 0])
|
|
||||||
|
|
||||||
ie, rectifying this noun, just as we might write
|
|
||||||
|
|
||||||
(mold [%cell %atom [%fork [%cube 12] [%cube 32]]])
|
|
||||||
|
|
||||||
what is the product mold? We'll see the answer in the next
|
|
||||||
lesson. But it can only involve %hold.
|
|
||||||
|
|
||||||
In Watt, a finite and strict language, %hold is essentially lazy
|
|
||||||
evaluation done manually. A list mold is *logically* infinite -
|
|
||||||
that is, if you keep evaluating the holds, you can evaluate them
|
|
||||||
ad infinitum. If Watt was implemented in Haskell, this would be
|
|
||||||
done automatically rather than manually, and molds would be an
|
|
||||||
infinite/lazy data structure.
|
|
||||||
|
|
||||||
But Watt is defined in Watt, not Haskell. (Well, it should
|
|
||||||
be defined in Watt. Right now, of course, it's in C.) So
|
|
||||||
lazy evaluation happens manually - we expand a %hold when we need
|
|
||||||
to, and not otherwise.
|
|
||||||
|
|
||||||
A crucial property of Watt that makes this work is that all molds
|
|
||||||
are *rational*: that is, we can expand them indefinitely, but at
|
|
||||||
a certain point they always repeat themselves. This allows us to
|
|
||||||
compute the obvious set-theoretic functions needed for any type
|
|
||||||
system, such as mold compatibility and orthogonality, without
|
|
||||||
going into an infinite loop. It is possible to define Watt genes
|
|
||||||
for which Mill is very slow, but it is not possible to define
|
|
||||||
Watt genes for which it goes into an infinite loop. Or, at
|
|
||||||
least, if it is, there's a bug in my code!
|
|
||||||
|
|
||||||
All right. I think this is enough for one lesson.
|
|
||||||
|
|
||||||
Here is a concrete problem to cogitate on: how would you write a
|
|
||||||
function that tests whether molds A and B are orthogonal (no noun
|
|
||||||
is in both A and B)? Or compatible (any noun in A is also in B)?
|
|
||||||
Are these functions NP-complete? If so, how might one work
|
|
||||||
around this in practice?
|
|
@ -1,404 +0,0 @@
|
|||||||
Anatomy of Watt: 2, Genes
|
|
||||||
|
|
||||||
Watt now works - or at least, works well enough to implement a
|
|
||||||
polymorphic quicksort gate with parameterized comparator. It can
|
|
||||||
also define its own implementation data structures, although
|
|
||||||
these are slightly underconstrained. You'll find these in
|
|
||||||
Test/qsort.watt and Test/mill.watt respectively.
|
|
||||||
|
|
||||||
I will prepare something like a tutorial introduction soon. I
|
|
||||||
would hold off on actually playing with Watt until this is
|
|
||||||
available, though of course you can. For now, I want to finish
|
|
||||||
understanding Watt the way it's meant to be understood. There is
|
|
||||||
no part 3 of this essay! After "Anatomy of Watt," the rest is
|
|
||||||
just a bunch of tedious memorization - like learning any
|
|
||||||
language.
|
|
||||||
|
|
||||||
Basically, there are two kinds of languages. There are languages
|
|
||||||
meant to be learned from the syntax down, and languages meant to
|
|
||||||
be learned from the plumbing up.
|
|
||||||
|
|
||||||
Haskell is an example of the former. I have never seen anyone
|
|
||||||
try to teach Haskell by explaining its internals. In fact, last
|
|
||||||
time I looked, the Haskell type-inference algorithm was not
|
|
||||||
even explained in the Haskell documentation. As in: when I tried
|
|
||||||
to look it up, I got redirected to a '70s academic paper behind a
|
|
||||||
paywall. The idea of Haskell is: you do enough tutorials, and
|
|
||||||
eventually you'll just get it. I suppose this works for some.
|
|
||||||
It makes me want to throw a professor through some drywall.
|
|
||||||
|
|
||||||
C is an example of the latter. To learn C, you learn what the C
|
|
||||||
compiler is doing. This is only viable because the C compiler,
|
|
||||||
at least in the abstract, is a terribly simple bit of code. C is
|
|
||||||
really only one step beyond a macro assembler.
|
|
||||||
|
|
||||||
Watt, while not at all like C, has much the same relationship
|
|
||||||
to Nock that C has to assembly language. The semantic core of
|
|
||||||
Watt is quite straightforward. Mapping this core onto a textual
|
|
||||||
language which doesn't look or feel like ass is a good bit more
|
|
||||||
work; and even when this work is done, the machinery is not at
|
|
||||||
all hidden. It can't be hidden. IMHO, it shouldn't be.
|
|
||||||
|
|
||||||
In the first part of "Anatomy," we discussed the mold system,
|
|
||||||
which you'll see at the top of mill.watt. Today, we're going to
|
|
||||||
look at the gene system, which is the rest of the file.
|
|
||||||
Basically, molds are types and genes are code.
|
|
||||||
|
|
||||||
Recall that the basic toolchain in Watt is
|
|
||||||
|
|
||||||
- mill
|
|
||||||
|| [mol=mold noc=nock]
|
|
||||||
sub = mold
|
|
||||||
gen = gene
|
|
||||||
==
|
|
||||||
|
|
||||||
so, when you want to compile something,
|
|
||||||
|
|
||||||
(mill sub gen)
|
|
||||||
|
|
||||||
where [sub] is the subject type and [gen] is the Watt code,
|
|
||||||
produces [mol noc], where [mol] is the product type and [noc] is
|
|
||||||
the Nock formula. Rockit science, this is not.
|
|
||||||
|
|
||||||
A key point to remember: Nock has no global environment. Whether
|
|
||||||
it's code or data, if it's not in the subject, it doesn't exist.
|
|
||||||
|
|
||||||
Now, the gene part of mill.watt is a lot bigger than the mold
|
|
||||||
part. It is also a lot scarier - it includes a number of
|
|
||||||
strange, four-letter symbols which may be Martian curse words.
|
|
||||||
|
|
||||||
However, like the mold system, the gene system is actually pretty
|
|
||||||
simple. Almost all these stems are internal macros - that is,
|
|
||||||
they are implemented simply by expansion. We could in fact
|
|
||||||
expand them in the parser, although this would make for some
|
|
||||||
pretty husky genes.
|
|
||||||
|
|
||||||
While a few more will probably be added, at present the only
|
|
||||||
axiomatic stems are the initial block of 15, from %bail through
|
|
||||||
%spot:
|
|
||||||
|
|
||||||
[%bail ~] :: !!
|
|
||||||
[%cast fes=gene rum=gene] :: ^-
|
|
||||||
[%cage fut=(list gene)] :: +-
|
|
||||||
[%dbug lyq=gene] :: !#
|
|
||||||
[%home cux=home lyq=gene] :: !:
|
|
||||||
[%kick ved=wire suc=bolt] :: :=
|
|
||||||
[%like rid=wire bul=gene] :: ?=
|
|
||||||
[%link del=gene zim=gene] :: ~>
|
|
||||||
[%load pir=book] :: |@
|
|
||||||
[%name tem=mark caf=gene] :: +=
|
|
||||||
[%nock fiz={3 4 5 6} dil=gene] :: :N
|
|
||||||
[%quiz tes=gene bif=gene hob=gene] :: ?:
|
|
||||||
[%rock gos=*] :: %=
|
|
||||||
[%sure fes=gene gav=gene vep=gene] :: ^=
|
|
||||||
[%spot xed=spot lyq=gene] :: !@
|
|
||||||
|
|
||||||
|
|
||||||
Moreover, three of these (%dbug, %home, and %spot) are
|
|
||||||
exclusively for tracing and debugging. This leaves us with just
|
|
||||||
12 stems to explain.
|
|
||||||
|
|
||||||
(A noun of the above form is called a "rose." The head, or tag,
|
|
||||||
is the "stem." The tail, or payload, is the "bulb." An atomic
|
|
||||||
leaf is a "bud." An implicit pair, like the implicit cons in
|
|
||||||
Nock, is a "twig." Your form can have either buds or twigs, but
|
|
||||||
not of course both.)
|
|
||||||
|
|
||||||
Let's go through them in non-alphabetical order, from simplest
|
|
||||||
to trickiest.
|
|
||||||
|
|
||||||
[%bail ~]
|
|
||||||
|
|
||||||
%bail: bail out.
|
|
||||||
|
|
||||||
Formally, in the Nock spec, this is an infinite loop. In
|
|
||||||
practice it just returns you to the command line.
|
|
||||||
|
|
||||||
Produces
|
|
||||||
++
|
|
||||||
mol=%blot
|
|
||||||
noc=[0 0]
|
|
||||||
==
|
|
||||||
|
|
||||||
|
|
||||||
[%rock gos=*]
|
|
||||||
|
|
||||||
%rock: specific constant.
|
|
||||||
|
|
||||||
Produces
|
|
||||||
++
|
|
||||||
mol = [%cube gos]
|
|
||||||
noc = [1 gos]
|
|
||||||
==
|
|
||||||
|
|
||||||
|
|
||||||
[%nock fiz={3 4 5 6} dil=gene]
|
|
||||||
|
|
||||||
%nock: Nock operator.
|
|
||||||
|
|
||||||
Produces
|
|
||||||
++
|
|
||||||
mol =
|
|
||||||
?- fiz
|
|
||||||
- 3
|
|
||||||
%blur
|
|
||||||
- 4
|
|
||||||
[%fork [%cube 0] [%cube 1]]
|
|
||||||
- 5
|
|
||||||
%atom
|
|
||||||
- 6
|
|
||||||
[%fork [%cube 0] [%cube 1]]
|
|
||||||
==
|
|
||||||
noc = [fiz noc.(mill sub dil)]
|
|
||||||
==
|
|
||||||
|
|
||||||
|
|
||||||
[%cage fut=(list gene)]
|
|
||||||
|
|
||||||
%cage: construct tuple.
|
|
||||||
|
|
||||||
This is Lisp cons - n-ary rather than binary, to reduce gene
|
|
||||||
size. Eg, for gen=[%cage foo bar ~], we get
|
|
||||||
|
|
||||||
++
|
|
||||||
mol = [%cell mol.(mill sub foo) mol.(mill sub bar)]
|
|
||||||
noc = [noc.(mill sub foo) noc.(mill sub bar)]
|
|
||||||
==
|
|
||||||
|
|
||||||
|
|
||||||
[%link del=gene zim=gene]
|
|
||||||
|
|
||||||
%link: compose functions.
|
|
||||||
|
|
||||||
Produces
|
|
||||||
++
|
|
||||||
mol = mol.(mill mol.(mill sub del) zim)
|
|
||||||
noc = [3 noc.(mill sub del)
|
|
||||||
1 noc.(mill mol.(mill sub del) zim)
|
|
||||||
]
|
|
||||||
==
|
|
||||||
|
|
||||||
ie, in C syntax, zim(del(sub)).
|
|
||||||
|
|
||||||
|
|
||||||
[%name tem=mark caf=gene]
|
|
||||||
|
|
||||||
%name: name data.
|
|
||||||
|
|
||||||
Produces
|
|
||||||
++
|
|
||||||
mol = [%face tem mol.(mill sub caf)]
|
|
||||||
noc = noc.(mill sub caf)
|
|
||||||
==
|
|
||||||
|
|
||||||
|
|
||||||
[%cast fes=gene rum=gene]
|
|
||||||
|
|
||||||
%cast: mold conversion.
|
|
||||||
|
|
||||||
Use mold of [fes], nock of [rum]:
|
|
||||||
|
|
||||||
++
|
|
||||||
mol = mol.(mill sub fes)
|
|
||||||
noc = noc.(mill sub rum)
|
|
||||||
==
|
|
||||||
|
|
||||||
given the congruence test
|
|
||||||
|
|
||||||
(nest mol.(mill sub rum) mol.(mill sub fes))
|
|
||||||
|
|
||||||
where (nest foo bar) is yes only if every noun in mold [foo]
|
|
||||||
is also in mold [bar]. ("Only if," not "if and only if,"
|
|
||||||
meaning that the test is conservative.)
|
|
||||||
|
|
||||||
|
|
||||||
[%sure fes=gene gav=gene vep=gene]
|
|
||||||
|
|
||||||
%sure: congruence assertion.
|
|
||||||
|
|
||||||
Produces
|
|
||||||
|
|
||||||
(noc mol vep)
|
|
||||||
|
|
||||||
given the congruence test
|
|
||||||
|
|
||||||
(nest mol.(mill sub gav) mol.(mill sub fes))
|
|
||||||
|
|
||||||
See %cast for the semantics of (nest).
|
|
||||||
|
|
||||||
|
|
||||||
[%quiz tes=gene bif=gene hob=gene]
|
|
||||||
|
|
||||||
%quiz: if-then-else
|
|
||||||
|
|
||||||
Produces (oversimplifying somewhat)
|
|
||||||
++
|
|
||||||
mol = [%fork mol.(mill sub bif) mol.(mill sub hob)]
|
|
||||||
noc = [2
|
|
||||||
noc.(mill sub tes)
|
|
||||||
noc.(mill sub bif)
|
|
||||||
noc.(mill sub hob)
|
|
||||||
]
|
|
||||||
--
|
|
||||||
|
|
||||||
%quiz analyzes the boolean structure of [tes] for any
|
|
||||||
%like genes which specialize the subject. If such
|
|
||||||
a specialization mold [pax] is produced, it is fused with
|
|
||||||
[sub] for the [bif] branch - ie,
|
|
||||||
|
|
||||||
(mill [%fuse pax sub] bif)
|
|
||||||
|
|
||||||
|
|
||||||
[%like rid=wire bul=gene]
|
|
||||||
|
|
||||||
%like: test mold membership
|
|
||||||
|
|
||||||
%like tests whether a fragment of the subject, designated by
|
|
||||||
[rid], is in the mold produced by the subject and [bul]. The
|
|
||||||
result is a standard Nock loobean: 0 for yes, 1 for no.
|
|
||||||
|
|
||||||
[rid] must be a "flat" wire (see part 1 for the definition of
|
|
||||||
[wire], or see the description of %kick below), meaning
|
|
||||||
only parts, no hooks - ie, it must reference a fragment of
|
|
||||||
the subject, not a functional pseudo-attribute.
|
|
||||||
|
|
||||||
[bul] must be a simple, flat mold - %like will not generate
|
|
||||||
code for any old [bul]. Recursive molds, cones, etc, are not
|
|
||||||
allowed - as a matter of policy, rather than ability.
|
|
||||||
Rather, it is used only for simple disambiguation between
|
|
||||||
forks, eg, matching stems.
|
|
||||||
|
|
||||||
See %quiz for how %like is used to specialize type.
|
|
||||||
|
|
||||||
|
|
||||||
[%load pir=book]
|
|
||||||
|
|
||||||
%load: load code
|
|
||||||
|
|
||||||
%load creates a cone, which is a [data code] pair. [pir],
|
|
||||||
the book, is a bush: either [mark gene] or [book book].
|
|
||||||
This is translated into Nock code with the same geometry.
|
|
||||||
[mark gene] becomes [nock]; [[mark gene] [mark gene]]
|
|
||||||
becomes [nock nock]; and so on.
|
|
||||||
|
|
||||||
The subject for these formulas is always the cone itself.
|
|
||||||
Mill does not compute type signatures; rather, the cone
|
|
||||||
mold simply preserves the entire book.
|
|
||||||
|
|
||||||
Thus, (mill sub [%load [%myname mycode]]) generates
|
|
||||||
|
|
||||||
++
|
|
||||||
[%cone sub [%myname mycode]]
|
|
||||||
noc.(mill [%cone sub [%myname mycode]] mycode)
|
|
||||||
--
|
|
||||||
|
|
||||||
|
|
||||||
[%kick ved=wire suc=bolt]
|
|
||||||
|
|
||||||
%kick: use with changes
|
|
||||||
|
|
||||||
%kick is our last and most difficult stem. You might even
|
|
||||||
say that all other kinds of gene are the supporting cast for
|
|
||||||
%kick. Basically, Watt *is* %kick.
|
|
||||||
|
|
||||||
Basically, [ved] is a part or hook within the subject, and
|
|
||||||
[suc] is a list of changes to the subject.
|
|
||||||
|
|
||||||
Recall the definitions of (wire) and (bolt):
|
|
||||||
|
|
||||||
- port
|
|
||||||
|~
|
|
||||||
**
|
|
||||||
@:mark
|
|
||||||
[%find tic=step vix=mark]
|
|
||||||
[%frag haz=axis]
|
|
||||||
==
|
|
||||||
|
|
||||||
- wire
|
|
||||||
(list port)
|
|
||||||
|
|
||||||
- bolt
|
|
||||||
(list :[p=wire q=gene])
|
|
||||||
|
|
||||||
So it looks like there are three kinds of ports. But there
|
|
||||||
are only two: %find and %frag. To reduce gene size,
|
|
||||||
|
|
||||||
[%find 0 vix]
|
|
||||||
|
|
||||||
can be expressed as just [vix].
|
|
||||||
|
|
||||||
%frag maps directly to the Nock fragment operator, 0. %find
|
|
||||||
searches for either a %face mold with a matching mark, or a
|
|
||||||
cone with a matching mark in its book. If [tic] is nonzero,
|
|
||||||
it searches into that mark or in the cone data with (dec
|
|
||||||
tic). Otherwise, it either returns the face fragment matching
|
|
||||||
the mark, or invokes the cone formula matching the mark. In
|
|
||||||
the latter case, again, the subject is the cone itself.
|
|
||||||
|
|
||||||
If [suc] is nonzero, it specifies a list of edits to be
|
|
||||||
applied to either the fragment or the cone subject. So, in
|
|
||||||
the latter case, we edit the data and call the code. This
|
|
||||||
can produce very strange and powerful effects.
|
|
||||||
|
|
||||||
A simple example of these strange and powerful effects is
|
|
||||||
the common, or garden, function call. In Watt terms, we are
|
|
||||||
kicking a gate. Suppose, for instance, we call
|
|
||||||
|
|
||||||
(add foo bar)
|
|
||||||
|
|
||||||
This does exactly what it might do in, say, Lisp: it produces
|
|
||||||
the sum of foo and bar. Indeed I have shamelessly borrowed
|
|
||||||
the Lisp syntax. But Watt is not, after all, a lambda
|
|
||||||
language - so what happens behind the scenes is much
|
|
||||||
stranger.
|
|
||||||
|
|
||||||
When the parser parses (add foo bar), it becomes the macro
|
|
||||||
|
|
||||||
[%mang
|
|
||||||
[%kick [%add ~] ~]
|
|
||||||
[%cage
|
|
||||||
[%kick [%foo ~] ~]
|
|
||||||
[%kick [%bar ~] ~]
|
|
||||||
~
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
%mang means "call." This expands to a somewhat hefty macro
|
|
||||||
which I won't burden you with at present. Let's look at what
|
|
||||||
it does, though.
|
|
||||||
|
|
||||||
First we need to resolve %add. This is a hook in the kernel.
|
|
||||||
But the hook does not actually add - rather, it produces a
|
|
||||||
"gate," whose mold will be of the form
|
|
||||||
|
|
||||||
[%cone
|
|
||||||
[%cell arg=mold env=mold]
|
|
||||||
[0 add-gene]
|
|
||||||
]
|
|
||||||
|
|
||||||
Thus, a gate is a cone whose data is a pair [arg env], and
|
|
||||||
whose code is one nameless gene (since there's only one, a
|
|
||||||
name would be superfluous). In the case of this particular
|
|
||||||
gate, [arg] is the default argument for add - [0 0], and
|
|
||||||
[env] is the aforementioned kernel, a cone whose book is all
|
|
||||||
the gates in Cato/k300.watt, and whose data is ~, constant 0.
|
|
||||||
|
|
||||||
In order to call this add function on [foo bar], therefore,
|
|
||||||
we need to (a) edit the gate to replace the default argument
|
|
||||||
with [foo bar], and (b) invoke hook 0. Hence:
|
|
||||||
|
|
||||||
[%kick
|
|
||||||
[0 0] :: kick hook 0
|
|
||||||
[ [+4 :: changing fragment 4 - arg
|
|
||||||
[%cage ;; to [foo bar]
|
|
||||||
[%kick [%foo 0] 0]
|
|
||||||
[%kick [%bar 0] 0]
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
After a while, this will feel completely natural to you - and
|
|
||||||
lambda reduction will seem like bizarre alien technology.
|
|
File diff suppressed because one or more lines are too long
3004
Spec/watt/297.watt
3004
Spec/watt/297.watt
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2982
Spec/watt/298.watt
2982
Spec/watt/298.watt
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
|||||||
slon ~> X [del=gene zim=gene] [zim] in [del]
|
|
||||||
gant := X [ved=rope suc=list+[rope gene]] use with changes
|
|
||||||
blin |* E [bov=gene pir=list+gene] load and apply
|
|
||||||
boce ?& E [das=list+gene] yes iff all of [das] are yes
|
|
||||||
perd ^+ X [fes=gene rum=gene] [rum] perd to [fes], geometric
|
|
||||||
brip :^ E [mef=skel] skeleton lamb
|
|
||||||
halc ^- X [fes=gene rum=gene] [rum] perd to [fes], genetic
|
|
||||||
crot ^@ E [rum=gene] [rum] perd to bead
|
|
||||||
dbug !? X [lyq=gene] activate internal debug
|
|
||||||
delc :$ E [mef=skel] skeleton stub
|
|
||||||
feng ?> E [tes=gene bif=gene] if [tes], then [bif], else fail
|
|
||||||
flec :- E [buz=gene] fire with default
|
|
||||||
flot |- E [cug=gene] hot lamb
|
|
||||||
frez &~ E [dil=gene] pray
|
|
||||||
garc :+ E [buz=gene pum=list+gene] fire with list
|
|
||||||
glax ?+ E [feg=gene bem=list+pike] examine [feg], externally
|
|
||||||
glem -> E [hig=gene muc=gene] [muc] in [hig +]
|
|
||||||
clep X [pir=bush+[mark gene]] load book
|
|
||||||
tarn ^= X [fes=gene gav=gene vep=gene] [vep], since [gav] matches [fes]
|
|
||||||
gult X [fiz=(3 4 5 6 7 8) dil=gene] nock operator
|
|
||||||
stol ?: X [tes=gene bif=gene hob=gene] if [tes], then [bif], else [hob]
|
|
||||||
lisc -< E [hig=gene muc=gene] [hig] in [muc +]
|
|
||||||
lome |= E [cug=gene] cold lamb
|
|
||||||
sard :* X [caw=list+[mark gene]] classic American tuple
|
|
||||||
malk |+ E [pir=list+[mark gene]] book as list
|
|
||||||
neft &^ E [dil=gene] inc
|
|
||||||
peld ?| E [das=list+gene] yes iff any of [das] are yes
|
|
||||||
pont => E [han=mark dur=gene leb=gene] [leb] in [[han=dur] +]
|
|
||||||
prex ?< E [tes=gene hob=gene] if [tes], then fail, else [hob]
|
|
||||||
pung :~ E [buz=gene lep=gene] fire lamb, direct
|
|
||||||
rald ^* E [rum=gene] [rum] perd to blur
|
|
||||||
rulf =< E [han=mark dur=gene leb=gene] [dur] in [[han=leb] +]
|
|
||||||
spot X [xed=[[@ @] [@ @] lyq=gene] source position, [at to] [lin col]
|
|
||||||
serd |: E [tep=gene von=gene] lamb construction
|
|
||||||
poos X [cux=lab lyq=gene] source label
|
|
||||||
spal &~ E [dil=gene] cell
|
|
||||||
stiv ~< E [del=gene zim=gene] [del] in [zim]
|
|
||||||
trup ?! E [gix=gene] not
|
|
||||||
veck &* E [dil=gene] raw eval
|
|
||||||
forb ?= X [rid=rope bul=gene] yes iff [rid] is like [bul]
|
|
||||||
wamp &= E [dil=gene] eq
|
|
||||||
zact ?- E [mox=rope bem=list+pike] examine [mox], internally
|
|
@ -1,31 +0,0 @@
|
|||||||
blin |* E [bov=gene pir=list+[mark gene]] load and apply
|
|
||||||
boce ?& E [das=list+gene] yes iff all of [das] are yes
|
|
||||||
brip :^ E [mef=skel] skeleton lamb
|
|
||||||
crot ^@ E [rum=gene] [rum] perd to bead
|
|
||||||
delc :$ E [mef=skel] skeleton stub
|
|
||||||
drol || E [nix=skel gub=skel von=gene] lamb with cast
|
|
||||||
feng ?> E [tes=gene bif=gene] if [tes], then [bif], else fail
|
|
||||||
flec :- E [buz=gene] call with default
|
|
||||||
flot |- E [cug=gene] hot lamb
|
|
||||||
frez &~ E [dil=gene] pray
|
|
||||||
garc :+ E [buz=gene pum=list+gene] call with list
|
|
||||||
glax ?+ E [feg=gene bem=list+pike] examine [feg], externally
|
|
||||||
glem -> E [hig=gene muc=gene] [muc] in [hig +]
|
|
||||||
lisc -< E [hig=gene muc=gene] [hig] in [muc +]
|
|
||||||
lome |= E [cug=gene] cold lamb
|
|
||||||
malk |+ E [pir=list+[mark gene]] book as list
|
|
||||||
neft &^ E [dil=gene] inc
|
|
||||||
peld ?| E [das=list+gene] yes iff any of [das] are yes
|
|
||||||
plom :. E [fut=list+gene] nameless tuple
|
|
||||||
pont => E [han=mark dur=gene leb=gene] [leb] in [[han=dur] +]
|
|
||||||
prex ?< E [tes=gene hob=gene] if [tes], then fail, else [hob]
|
|
||||||
pung :~ E [buz=gene lep=gene] call lamb, direct
|
|
||||||
rald ^* E [rum=gene] [rum] perd to blur
|
|
||||||
rulf =< E [han=mark dur=gene leb=gene] [dur] in [[han=leb] +]
|
|
||||||
stur |: E [tep=skel von=gene] lamb construction
|
|
||||||
spal &~ E [dil=gene] cell
|
|
||||||
stiv ~< E [del=gene zim=gene] [del] in [zim]
|
|
||||||
trup ?! E [gix=gene] not
|
|
||||||
veck &* E [dil=gene] raw eval
|
|
||||||
wamp &= E [dil=gene] eq
|
|
||||||
zact ?- E [mox=rope bem=list+pike] examine [mox], internally
|
|
@ -1,15 +0,0 @@
|
|||||||
clep X [pir=bush+[mark gene]] load book
|
|
||||||
dbug !? X [lyq=gene] activate internal debug
|
|
||||||
forb ?= X [rid=rope bul=gene] yes iff [rid] is like [bul]
|
|
||||||
gant := X [ved=rope suc=list+[rope gene]] use with changes
|
|
||||||
gult X [fiz=(3 4 5 6 7) dil=gene] nock operator
|
|
||||||
griv && X [dil=gene zep=noun] raw formula
|
|
||||||
halc ^- X [fes=gene rum=gene] [rum] perd to [fes], genetic
|
|
||||||
perd ^+ X [fes=gene rum=gene] [rum] perd to [fes], geometric
|
|
||||||
poos X [cux=lab lyq=gene] source label
|
|
||||||
pret X [daq=clod] constant, cube
|
|
||||||
sard :* X [caw=list+[mark gene]] classic American tuple
|
|
||||||
slon ~> X [del=gene zim=gene] [zim] in [del]
|
|
||||||
spot X [xed=[[@ @] [@ @] lyq=gene] source position, [at to] [lin col]
|
|
||||||
stol ?: X [tes=gene bif=gene hob=gene] if [tes], then [bif], else [hob]
|
|
||||||
tarn ^= X [fes=gene gav=gene vep=gene] [vep], since [gav] matches [fes]
|
|
@ -1,92 +0,0 @@
|
|||||||
Coinage of Watt
|
|
||||||
|
|
||||||
|
|
||||||
Name Definition Example as printed Comment
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
%blob opaque noun ~02Aw9cbfG21 ~0 prefix and uw
|
|
||||||
%many coin list ._foo_bar__ underscore delimited
|
|
||||||
~ %c codepoint ~--urbit.rocks~21 UTF-32 codepoint array
|
|
||||||
~ %da absolute time ~2012.8.3..21.30.12..beef.dead galactic time, AD/BC
|
|
||||||
~323-.6.10 death of Alexander
|
|
||||||
~ %dr relative time ~d30.h20.m15.s12..dead.beef GT, relative
|
|
||||||
~ %f flag .y boolean, 0=yes and 1=no
|
|
||||||
.n
|
|
||||||
~ %n null ~ absence of information
|
|
||||||
~ %if IPv4 address .232.12.9.1
|
|
||||||
~ %is IPv6 address .0.0.0.0.0.0.dead.beef
|
|
||||||
~ %p phonemic ~pic memorable integer
|
|
||||||
~-
|
|
||||||
~nopgen
|
|
||||||
~nopgen-durban
|
|
||||||
~nopgen-durban--picmec-micmac
|
|
||||||
~ %rd real-double .~28.4089
|
|
||||||
~ %rh real-half .~~34
|
|
||||||
~ %rq real-quad .~~~17
|
|
||||||
~ %rs real-single .-576.78
|
|
||||||
|
|
||||||
~ %sb signed 2
|
|
||||||
~ %sd signed 10
|
|
||||||
~ %sh signed 16
|
|
||||||
~ %sv signed 32
|
|
||||||
~ %sw signed 64
|
|
||||||
|
|
||||||
~ %ub unsigned 2
|
|
||||||
~ %ud unsigned 10
|
|
||||||
~ %ux unsigned 16
|
|
||||||
~ %uv unsigned 32
|
|
||||||
~ %uw unsigned 64
|
|
||||||
|
|
||||||
~ %t text ~~urbit.rocks~21 literal UTF-8
|
|
||||||
~ %ta legible span ~.under~-score [a/z 0/9 - . ~ _]
|
|
||||||
~ %tas symbolic span foo-bar-baz [a/z -, start a/z]
|
|
||||||
|
|
||||||
sack prefix tree, with status (& good, | no good, * postponed)
|
|
||||||
|
|
||||||
a/z %tabc symbol &
|
|
||||||
1/9 %ud unsigned decimal &
|
|
||||||
0 %ud unsigned decimal &
|
|
||||||
. %
|
|
||||||
b %ub unsigned binary &
|
|
||||||
h %uh unsigned hex &
|
|
||||||
v %uv unsigned base32 &
|
|
||||||
w %uw unsigned base64 &
|
|
||||||
|
|
||||||
.
|
|
||||||
y,n %f booleans 0 and 1 &
|
|
||||||
0/9 %rs single precision real *
|
|
||||||
%ib ipv4 &
|
|
||||||
%il ipv6 &
|
|
||||||
~ %rh half precision real *
|
|
||||||
. %rd double precision real *
|
|
||||||
.. %rq quad precision real *
|
|
||||||
-
|
|
||||||
0/9 %rs single precision real *
|
|
||||||
~ %rh half precision real *
|
|
||||||
. %rd double precision real *
|
|
||||||
.. %rq quad precision real *
|
|
||||||
_ [%many] encoded list, _ and __
|
|
||||||
|
|
||||||
-
|
|
||||||
1/9 %sd signed decimal
|
|
||||||
0 %sd signed decimal
|
|
||||||
b %sb signed binary
|
|
||||||
h %sh signed hex
|
|
||||||
v %sv signed base32
|
|
||||||
w %sw signed base64
|
|
||||||
-
|
|
||||||
1/9 %sd signed decimal
|
|
||||||
0 %sd signed decimal
|
|
||||||
b %sb signed binary
|
|
||||||
h %sh signed hex
|
|
||||||
v %sv signed base32
|
|
||||||
w %sw signed base64
|
|
||||||
~ $c ucs32 string
|
|
||||||
|
|
||||||
~ %n null
|
|
||||||
~ %t utf8 string (escaped)
|
|
||||||
. %ta unescaped span
|
|
||||||
- %p zero post
|
|
||||||
a/z %p post
|
|
||||||
0 [%blob] jam as base64
|
|
||||||
* %da absolute date
|
|
||||||
* %dr relative date
|
|
@ -1,58 +0,0 @@
|
|||||||
!! "hopven" %zike
|
|
||||||
!# "hopdax" %zush
|
|
||||||
!% "hopmit" %zoot
|
|
||||||
!: "hopdig" %zalt
|
|
||||||
!` "hoptic" %zole
|
|
||||||
%* "mitras" %teck
|
|
||||||
%+ "mitpod" %bung
|
|
||||||
%- "mitnub" %fung
|
|
||||||
%. "mitdot" %gnum
|
|
||||||
%: "mitdig" %mung
|
|
||||||
%= "mitben" %mack
|
|
||||||
%^ "mithat" %tung
|
|
||||||
%~ "mitsig" %hang
|
|
||||||
%| "mitbar" %gath
|
|
||||||
.* "dotras" %sail
|
|
||||||
.= "dotben" %sing
|
|
||||||
.? "dotask" %dust
|
|
||||||
.^ "dothat" %vint
|
|
||||||
:* "digras" %prex
|
|
||||||
:+ "digpod" %trex
|
|
||||||
:- "dignub" %twix
|
|
||||||
:^ "dighat" %quax
|
|
||||||
:~ "digsig" %slax
|
|
||||||
;+ "lompod" %fist
|
|
||||||
;- "lomnub" %mast
|
|
||||||
=+ "benpod" %gant
|
|
||||||
=- "bennub" %tang
|
|
||||||
=< "bender" %claf
|
|
||||||
=> "benred" %flac
|
|
||||||
?! "askhop" %vern
|
|
||||||
?& "askamp" %chan
|
|
||||||
?* "askras" %moze
|
|
||||||
?- "asknub" %grel
|
|
||||||
?. "askdot" %lort
|
|
||||||
?: "askdig" %trol
|
|
||||||
?< "askder" %marg
|
|
||||||
?= "askben" %plin
|
|
||||||
?> "askred" %gram
|
|
||||||
?| "askbar" %dorn
|
|
||||||
?~ "asksig" %fent
|
|
||||||
^$ "hatbuc" %germ
|
|
||||||
^% "hatmit" %velt
|
|
||||||
^* "hatras" %mave
|
|
||||||
^+ "hatdig" %pock
|
|
||||||
^- "hatnub" %cast
|
|
||||||
^: "hatpod" %stil
|
|
||||||
^= "hatben" %bran
|
|
||||||
^? "hatask" %hint
|
|
||||||
^@ "hatpat" %grit
|
|
||||||
^~ "hatsig" %wost
|
|
||||||
|% "barmit" %tash
|
|
||||||
|* "barras" %pank
|
|
||||||
|- "barnub" %vamp
|
|
||||||
|: "bardig" %sunt
|
|
||||||
|= "barben" %lome
|
|
||||||
|? "barask" %rond
|
|
||||||
|~ "barsig" %lonk
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
|||||||
new:
|
|
||||||
bar |
|
|
||||||
bas \
|
|
||||||
buc $
|
|
||||||
cab _ :: XX dun
|
|
||||||
cen %
|
|
||||||
col :
|
|
||||||
com ,
|
|
||||||
doq "
|
|
||||||
dot .
|
|
||||||
fas /
|
|
||||||
gal <
|
|
||||||
gar >
|
|
||||||
hax #
|
|
||||||
hep -
|
|
||||||
kel {
|
|
||||||
ker }
|
|
||||||
ket ^
|
|
||||||
lus +
|
|
||||||
pel (
|
|
||||||
pam &
|
|
||||||
per )
|
|
||||||
pat @ :: XX mat
|
|
||||||
sel [
|
|
||||||
sem ;
|
|
||||||
ser ]
|
|
||||||
sig ~
|
|
||||||
soq '
|
|
||||||
tar *
|
|
||||||
tic `
|
|
||||||
tis =
|
|
||||||
wut ?
|
|
||||||
zap !
|
|
||||||
|
|
||||||
old:
|
|
||||||
del < gal
|
|
||||||
pel ) par
|
|
||||||
bar | bar
|
|
||||||
hes $ hes
|
|
||||||
sed } ker
|
|
||||||
bot ' soc
|
|
||||||
ket ^ ket
|
|
||||||
sep - mus
|
|
||||||
cab _ cab
|
|
||||||
lep ( pal
|
|
||||||
sig ~ sig
|
|
||||||
com , com
|
|
||||||
led > gar
|
|
||||||
tam ; sem
|
|
||||||
cas ? wut
|
|
||||||
lyc = tis
|
|
||||||
tar * tar
|
|
||||||
dax # hax
|
|
||||||
mit % cen
|
|
||||||
tec ` tic
|
|
||||||
deg : col
|
|
||||||
ned ] ser
|
|
||||||
tob " doc
|
|
||||||
den [ sel
|
|
||||||
nov \ bas
|
|
||||||
von / fas
|
|
||||||
des { kel
|
|
||||||
pam & pam
|
|
||||||
wat @ pat
|
|
||||||
dot . dot
|
|
||||||
pes + lus
|
|
||||||
zap ! zap
|
|
||||||
|
|
||||||
Watt GTO: Gran Tabula Operazioni
|
|
||||||
|
|
||||||
|= %brts AM "bartis" "funk" declare a function
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(Copied from watt.watt - recopy if in doubt.)
|
|
||||||
::::
|
|
||||||
::
|
|
||||||
++ gene
|
|
||||||
$& [p=gene q=gene]
|
|
||||||
$%
|
|
||||||
[%% p=axis]
|
|
||||||
::
|
|
||||||
[%brts p=gene q=gene]
|
|
||||||
[%brls p=gene q=gene]
|
|
||||||
[%brdt p=gene]
|
|
||||||
[%brhp p=gene]
|
|
||||||
[%brtr p=gene q=gene]
|
|
||||||
::
|
|
||||||
[%clkt p=gene q=gene r=gene s=gene]
|
|
||||||
[%clhp p=gene q=gene]
|
|
||||||
[%clls p=gene q=gene r=gene]
|
|
||||||
[%clsg p=(list gene)]
|
|
||||||
[%cltr p=(list gene)]
|
|
||||||
::
|
|
||||||
[%cntr p=twig q=gene r=(list >[p=gene q=gene])]
|
|
||||||
[%cncl p=gene q=gene]
|
|
||||||
[%cndt p=gene q=gene]
|
|
||||||
[%cnkt p=gene q=gene r=gene s=gene]
|
|
||||||
[%cnbc p=term]
|
|
||||||
[%cnls p=gene q=gene r=gene]
|
|
||||||
[%cnhp p=gene q=(list gene)]
|
|
||||||
[%cnhx p=twig]
|
|
||||||
[%cnsg p=twig q=gene r=gene]
|
|
||||||
[%cnts p=twig q=(list >[p=gene q=gene])]
|
|
||||||
::
|
|
||||||
[%dtkt p=gene]
|
|
||||||
[%dtls p=gene]
|
|
||||||
[%dtpt p=term q=@]
|
|
||||||
[%dtsg p=term q=*]
|
|
||||||
[%dttr p=gene q=gene]
|
|
||||||
[%dtts p=gene q=gene]
|
|
||||||
[%dtwt p=gene]
|
|
||||||
::
|
|
||||||
[%bcbr p=gene q=gene]
|
|
||||||
[%bccb p=gene]
|
|
||||||
[%bccl p=(list gene)]
|
|
||||||
[%bccn p=gene q=(list gene)]
|
|
||||||
[%bcgr p=gene]
|
|
||||||
[%bckt p=gene]
|
|
||||||
[%bcpm p=gene q=gene]
|
|
||||||
[%bctr p=gene]
|
|
||||||
[%bcts p=?([%atom odor] %noun %cell %flag %null)]
|
|
||||||
[%bcwt p=gene q=(list gene)]
|
|
||||||
::
|
|
||||||
[%ktbr p=gene]
|
|
||||||
[%ktls p=gene q=gene]
|
|
||||||
[%ktdt p=gene q=gene]
|
|
||||||
[%ktgl p=gene q=gene]
|
|
||||||
[%ktgr p=gene q=gene]
|
|
||||||
[%kthp p=gene q=gene]
|
|
||||||
[%ktpm p=gene]
|
|
||||||
[%ktsg p=gene]
|
|
||||||
[%ktts p=term q=gene]
|
|
||||||
[%ktwt p=gene]
|
|
||||||
::
|
|
||||||
[%brcb p=gene q=(map term foot)]
|
|
||||||
[%brcn p=(map term foot)]
|
|
||||||
[%brkt p=gene q=(map term foot)]
|
|
||||||
::
|
|
||||||
[%sgbr p=gene q=gene]
|
|
||||||
[%sgcl p=[p=@ q=@] q=gene]
|
|
||||||
[%sgcn p=chop q=gene r=(list >[p=term q=gene]) s=gene]
|
|
||||||
[%sgdt p=chop q=gene]
|
|
||||||
[%sggl p=$|(term [p=term q=gene]) q=gene]
|
|
||||||
[%sggr p=$|(term [p=term q=gene]) q=gene]
|
|
||||||
[%sgbc p=term q=gene]
|
|
||||||
[%sghx p=term q=gene]
|
|
||||||
[%sgkt p=gene]
|
|
||||||
[%sgls p=@ q=gene]
|
|
||||||
[%sghp p=@ q=gene]
|
|
||||||
[%sgpm p=gene q=gene]
|
|
||||||
[%sgsg p=gene q=gene]
|
|
||||||
[%sgts p=gene q=gene]
|
|
||||||
::
|
|
||||||
[%smcl p=gene q=(list gene)]
|
|
||||||
[%smdq p=(list goop)]
|
|
||||||
[%smsg p=gene q=(list gene)]
|
|
||||||
::
|
|
||||||
[%tsgl p=gene q=gene]
|
|
||||||
[%tsgr p=gene q=gene]
|
|
||||||
[%tsls p=gene q=gene]
|
|
||||||
[%tshp p=gene q=gene]
|
|
||||||
::
|
|
||||||
[%wtbr p=(list gene)]
|
|
||||||
[%wthp p=gene q=(list >[p=gene q=gene])]
|
|
||||||
[%wtcl p=gene q=gene r=gene]
|
|
||||||
[%wtcn p=gene q=gene]
|
|
||||||
[%wtdt p=gene q=gene r=gene]
|
|
||||||
[%wtts p=gene q=gene]
|
|
||||||
[%wtgl p=gene q=gene]
|
|
||||||
[%wtgr p=gene q=gene]
|
|
||||||
[%wtls p=gene q=gene r=(list >[p=gene q=gene])]
|
|
||||||
[%wtpm p=(list gene)]
|
|
||||||
[%wtsg p=gene q=gene r=gene]
|
|
||||||
[%wtzp p=gene]
|
|
||||||
::
|
|
||||||
[%zpcb p=spot q=gene]
|
|
||||||
[%zpcm p=gene q=gene]
|
|
||||||
[%zpcn ~]
|
|
||||||
[%zpfs p=gene]
|
|
||||||
[%zpsm p=gene q=gene]
|
|
||||||
[%zpts p=gene]
|
|
||||||
[%zpzp ~]
|
|
||||||
==
|
|
@ -1,5 +0,0 @@
|
|||||||
s/csdt/wtdt/g
|
|
||||||
s/cslc/wtts/g
|
|
||||||
s/csps/wtls/g
|
|
||||||
s/cssp/wtms/g
|
|
||||||
s/mtdt/cndt/g
|
|
@ -1,117 +0,0 @@
|
|||||||
Dear Curtis,
|
|
||||||
|
|
||||||
We are sorry to inform you that your paper has not been selected for
|
|
||||||
presentation at the Workshop on Self-sustaining Systems (S3) 2010 at The
|
|
||||||
University of Tokyo, Japan.
|
|
||||||
|
|
||||||
Attached you will find the reviewers' comments. We hope you find them
|
|
||||||
useful for improving your paper and for resubmitting to another event.
|
|
||||||
|
|
||||||
Best regards,
|
|
||||||
Hidehiko and Robert
|
|
||||||
|
|
||||||
|
|
||||||
=========================== REVIEWS ==========================
|
|
||||||
|
|
||||||
---------------------------- REVIEW 1 --------------------------
|
|
||||||
PAPER: 3
|
|
||||||
TITLE: Watt: A Self-Sustaining Functional Language
|
|
||||||
|
|
||||||
|
|
||||||
SUMMARY
|
|
||||||
|
|
||||||
The author presents Watt and Nock. Watt is said to be a "functional
|
|
||||||
programming for stupid people like you". Nock is said to be the "model".
|
|
||||||
|
|
||||||
EVALUATION
|
|
||||||
|
|
||||||
A workshop contribution does not have to be perfect, but it needs to
|
|
||||||
be presented in a way that allows for a technical evaluation. This paper
|
|
||||||
is written in a different style. The paper is unclear and incomprehensible.
|
|
||||||
In many places it is vague, for example, "The relationship between Urth and
|
|
||||||
Earth is unclear". At some places it is humurous, for example, "Urth code
|
|
||||||
is nothing like Earth code". In other places it is simply insulting, for
|
|
||||||
example, "Watt is functional programming for stupid people like you".
|
|
||||||
|
|
||||||
Comments:
|
|
||||||
|
|
||||||
"Watt is C, A is the machine instruction set, B is the C source for CC,
|
|
||||||
and C is the executable for CC". C???
|
|
||||||
"spec is is generally"=> "spec is generally"
|
|
||||||
"Watt is dishwater; Urbit is vaporware". What?
|
|
||||||
"eg" => "e.g.," Same for ie.
|
|
||||||
"It has has all the flaws" => "It has all the flaws"
|
|
||||||
"slowness, ..., and slowness". Once is slow enough.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------------- REVIEW 2 --------------------------
|
|
||||||
PAPER: 3
|
|
||||||
TITLE: Watt: A Self-Sustaining Functional Language
|
|
||||||
|
|
||||||
|
|
||||||
This paper presents a self-sustaining functional language: Watt.
|
|
||||||
Watt is defined by a very small kernel which compiles itself to
|
|
||||||
Nock, which is a simple non-lambda automaton. Nock is defined
|
|
||||||
with very concise spec in English and pseudocode.
|
|
||||||
|
|
||||||
My major concern is that there is only an alpha release of the
|
|
||||||
language with a lot of flaws and I can not tell from the paper
|
|
||||||
if there are efficient and practical way to improve the flaws.
|
|
||||||
For example, to have Watt program with reasonable performance.
|
|
||||||
|
|
||||||
Although it seems the author has very good reason to use an
|
|
||||||
alien language to create Watt, it is still hard to understand
|
|
||||||
the language and design intuition. It would be better if the
|
|
||||||
paper can explain more about the design intuition, for example,
|
|
||||||
why it only defines noun and does not introduce type, variables and
|
|
||||||
functions etc. Also it is not clear about the programmability
|
|
||||||
of Watt. What kinds of programs can easily be expressed? And what kinds
|
|
||||||
of programs can it not easily express? And it still looks not clear
|
|
||||||
to me in which domain Watt can beat other existing language and
|
|
||||||
how.
|
|
||||||
|
|
||||||
Also it would be good if there is an example to illustrate Watt.
|
|
||||||
A small but complete Watt program would be good.
|
|
||||||
|
|
||||||
There are some redundant words in the paper, for example: in
|
|
||||||
Section 1.2 Practical, the 5th paragraph, the second sentence,
|
|
||||||
there is an extra 'is' and in the 9th paragraph, the second
|
|
||||||
sentence, there is an extra 'since'.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------------- REVIEW 3 --------------------------
|
|
||||||
PAPER: 3
|
|
||||||
TITLE: Watt: A Self-Sustaining Functional Language
|
|
||||||
|
|
||||||
|
|
||||||
The paper describes a minimal computation kernel (called Nock) and a statically typed functional language (Watt) built on top of it.
|
|
||||||
|
|
||||||
I think the approach of a minimal kernel with obvious execution semantics and a
|
|
||||||
real language that maps on top of it is interesting and well worth pursuing. It
|
|
||||||
is also clearly relevant to S3. The problem I have with this paper is that it
|
|
||||||
does not really help me to understand what is going on with Nock and Watt.
|
|
||||||
While Nock is probably fully specified by the formulas given, it is very hard
|
|
||||||
to see what they are doing. It would have been helpful to see some detailed
|
|
||||||
examples to understand Nock a bit more directly.
|
|
||||||
|
|
||||||
The same problem is even more pronounced for the section about Watt. We learn
|
|
||||||
some things about Watt (compiles to Nock, statically typed, type inference,
|
|
||||||
...), however, the actual working of the language remains unclear. The example
|
|
||||||
code in Figure 2 and 3 is essentially inscrutable. I think that again some very
|
|
||||||
simple and fully explained examples could have helped to understand the
|
|
||||||
language and the intuition behind it better. On the other hand, I regard some
|
|
||||||
parts of the Watt sections as not really necessary for the paper. It is not
|
|
||||||
that helpful to get a list of semi-arbitrary names for glyphs and digraphs.
|
|
||||||
|
|
||||||
I understand the reasoning behind not using standard names for constructs in
|
|
||||||
the language. However, the paper doesn't do a good enough job at telling me
|
|
||||||
what the non-standard names actually mean, which leads to the fact that I don't
|
|
||||||
understand the standard, nor the non-standard names.
|
|
||||||
|
|
||||||
Another issue of the paper is the idiosyncratic and partially arcane way to
|
|
||||||
describe things and the unconventional language. I was left with the impression
|
|
||||||
that this approach to writing was more an end in itself and not a mean to make
|
|
||||||
the reader understand better. While reading the paper I sometimes felt vaguely
|
|
||||||
insulted, which is not something you want your readers to be.
|
|
1029
Spec/watt/sss10.tex
1029
Spec/watt/sss10.tex
File diff suppressed because it is too large
Load Diff
@ -1,166 +0,0 @@
|
|||||||
@inproceedings{hindleymilner,
|
|
||||||
author = {Damas, Luis and Milner, Robin},
|
|
||||||
title = {Principal type-schemes for functional programs},
|
|
||||||
booktitle = {POPL '82: Proceedings of the 9th ACM SIGPLAN-SIGACT symposium on Principles of programming languages},
|
|
||||||
year = {1982},
|
|
||||||
isbn = {0-89791-065-6},
|
|
||||||
pages = {207--212},
|
|
||||||
location = {Albuquerque, New Mexico},
|
|
||||||
doi = {http://doi.acm.org/10.1145/582153.582176},
|
|
||||||
publisher = {ACM},
|
|
||||||
address = {New York, NY, USA},
|
|
||||||
}
|
|
||||||
|
|
||||||
@misc{graham100yr,
|
|
||||||
author = {Graham, Paul},
|
|
||||||
year = {2003},
|
|
||||||
title = {The hundred-year language},
|
|
||||||
howpublished = "\url{http://www.paulgraham.com/hundred.html}"
|
|
||||||
}
|
|
||||||
|
|
||||||
@article{skcombinator,
|
|
||||||
author = {Sch{\"{o}}nfinkel, M.},
|
|
||||||
citeulike-article-id = {2546429},
|
|
||||||
journal = {Math Annalen},
|
|
||||||
keywords = {lcbbbib},
|
|
||||||
pages = {305--316},
|
|
||||||
posted-at = {2008-03-17 14:30:48},
|
|
||||||
priority = {3},
|
|
||||||
title = {{\"{U}}ber die Bausteine der mathematischen Logik},
|
|
||||||
volume = {92},
|
|
||||||
year = {1924}
|
|
||||||
}
|
|
||||||
|
|
||||||
@inproceedings{tromp,
|
|
||||||
author = {John Tromp},
|
|
||||||
title = {Binary Lambda Calculus and Combinatory Logic},
|
|
||||||
booktitle = {Proceedings of the 5th Conference on Real Numbers and Computers, RNC5},
|
|
||||||
year = {2003},
|
|
||||||
pages = {214}
|
|
||||||
}
|
|
||||||
|
|
||||||
@misc{grahamarc,
|
|
||||||
author = {Graham, Paul},
|
|
||||||
year = {2001},
|
|
||||||
title = {Arc at 3 weeks},
|
|
||||||
howpublished = "\url{http://www.paulgraham.com/arcll1.html}"
|
|
||||||
}
|
|
||||||
|
|
||||||
@misc{grahamcore,
|
|
||||||
author = {Graham, Paul},
|
|
||||||
year = {2008},
|
|
||||||
title = {First priority: core language},
|
|
||||||
howpublished = "\url{http://www.paulgraham.com/core.html}"
|
|
||||||
}
|
|
||||||
|
|
||||||
@misc{piumartacola,
|
|
||||||
author = {Piumarta, Ian},
|
|
||||||
year = {2005},
|
|
||||||
title = {Making COLAs with Pepsi and Coke},
|
|
||||||
howpublished = "\url{http://piumarta.com/papers/colas-whitepaper.pdf}"
|
|
||||||
}
|
|
||||||
|
|
||||||
@misc{tarverqi,
|
|
||||||
author = {Tarver, Mark},
|
|
||||||
year = {2009},
|
|
||||||
title = {Functional programming with Qi},
|
|
||||||
howpublished = "\url{http://www.lambdassociates.org/Book/page000.htm}"
|
|
||||||
}
|
|
||||||
|
|
||||||
@article{kaymaxwell,
|
|
||||||
author = {Feldman, Stuart},
|
|
||||||
title = {A Conversation with Alan Kay},
|
|
||||||
journal = {Queue},
|
|
||||||
volume = {2},
|
|
||||||
number = {9},
|
|
||||||
year = {2005},
|
|
||||||
issn = {1542-7730},
|
|
||||||
pages = {20--30},
|
|
||||||
doi = {http://doi.acm.org/10.1145/1039511.1039523},
|
|
||||||
publisher = {ACM},
|
|
||||||
address = {New York, NY, USA},
|
|
||||||
}
|
|
||||||
|
|
||||||
@article{mccarthylisp,
|
|
||||||
author = {McCarthy, John},
|
|
||||||
title = {History of LISP},
|
|
||||||
journal = {SIGPLAN Not.},
|
|
||||||
volume = {13},
|
|
||||||
number = {8},
|
|
||||||
year = {1978},
|
|
||||||
issn = {0362-1340},
|
|
||||||
pages = {217--223},
|
|
||||||
doi = {http://doi.acm.org/10.1145/960118.808387},
|
|
||||||
publisher = {ACM},
|
|
||||||
address = {New York, NY, USA},
|
|
||||||
}
|
|
||||||
|
|
||||||
@article{haskell98,
|
|
||||||
author = {Simon {Peyton Jones} and others},
|
|
||||||
title = {The {Haskell} 98 Language and Libraries: The
|
|
||||||
Revised Report},
|
|
||||||
journal = {Journal of Functional Programming},
|
|
||||||
volume = 13,
|
|
||||||
number = 1,
|
|
||||||
pages = {0--255},
|
|
||||||
month = {Jan},
|
|
||||||
year = 2003,
|
|
||||||
note =
|
|
||||||
{\url{http://www.haskell.org/definition/}}
|
|
||||||
}
|
|
||||||
|
|
||||||
@inproceedings{crashonly,
|
|
||||||
author = {Candea, George and Fox, Armando},
|
|
||||||
title = {Crash-only software},
|
|
||||||
booktitle = {HOTOS'03: Proceedings of the 9th conference on
|
|
||||||
Hot Topics in Operating Systems},
|
|
||||||
year = {2003},
|
|
||||||
pages = {12--12},
|
|
||||||
location = {Lihue, Hawaii},
|
|
||||||
publisher = {USENIX Association},
|
|
||||||
address = {Berkeley, CA, USA},
|
|
||||||
}
|
|
||||||
|
|
||||||
@inproceedings{unixtrouble,
|
|
||||||
author = {Leon, Lorenzo De and Harris, William G. and Evens,
|
|
||||||
Martha},
|
|
||||||
title = {Is there really trouble with UNIX?},
|
|
||||||
booktitle = {CHI '83: Proceedings of the SIGCHI conference
|
|
||||||
on Human Factors in Computing Systems},
|
|
||||||
year = {1983},
|
|
||||||
isbn = {0-89791-121-0},
|
|
||||||
pages = {125--129},
|
|
||||||
location = {Boston, Massachusetts, United States},
|
|
||||||
doi = {http://doi.acm.org/10.1145/800045.801595},
|
|
||||||
publisher = {ACM},
|
|
||||||
address = {New York, NY, USA},
|
|
||||||
}
|
|
||||||
|
|
||||||
@article{kaysmalltalk,
|
|
||||||
author = {Kay, Alan C.},
|
|
||||||
title = {The early history of Smalltalk},
|
|
||||||
journal = {SIGPLAN Not.},
|
|
||||||
volume = {28},
|
|
||||||
number = {3},
|
|
||||||
year = {1993},
|
|
||||||
issn = {0362-1340},
|
|
||||||
pages = {69--95},
|
|
||||||
doi = {http://doi.acm.org/10.1145/155360.155364},
|
|
||||||
publisher = {ACM},
|
|
||||||
address = {New York, NY, USA},
|
|
||||||
}
|
|
||||||
|
|
||||||
@inproceedings{namedcontent,
|
|
||||||
author = {Jacobson, Van and Smetters, Diana K. and Thornton,
|
|
||||||
James D. and Plass, Michael F. and Briggs, Nicholas H. and
|
|
||||||
Braynard, Rebecca L.},
|
|
||||||
title = {Networking named content},
|
|
||||||
booktitle = {CoNEXT '09},
|
|
||||||
year = {2009},
|
|
||||||
isbn = {978-1-60558-636-6},
|
|
||||||
pages = {1--12},
|
|
||||||
location = {Rome, Italy},
|
|
||||||
doi = {http://doi.acm.org/10.1145/1658939.1658941},
|
|
||||||
publisher = {ACM},
|
|
||||||
address = {New York, NY, USA},
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
1. zeno: a 32-bit Nock interpreter
|
|
||||||
|
|
||||||
The core of the Watt reference distribution is zeno, a 32-bit
|
|
||||||
Nock interpreter.
|
|
||||||
|
|
||||||
zeno is by no means as efficient as possible. For instance, a
|
|
||||||
mature version would generate bytecode or machine code for
|
|
||||||
bottleneck Nock, not rely entirely on hardcoded jets. However,
|
|
||||||
zeno is designed to extend well in this sort of direction. It is
|
|
||||||
also easily generalizable to a 64-bit model.
|
|
||||||
|
|
||||||
In many ways, zeno is a typical language runtime system.
|
|
||||||
However, it has some unusual large-scale features:
|
|
||||||
|
|
||||||
- zeno is a direct interpreter.
|
|
||||||
|
|
||||||
zeno does not generate byte or machine code. It runs Nock
|
|
||||||
formulas straight out of the noun tree. Nock itself can be
|
|
||||||
seen as a kind of "treecode" - it is designed for direct
|
|
||||||
execution.
|
|
||||||
|
|
||||||
- zeno cannot call foreign functions.
|
|
||||||
|
|
||||||
There is no way to call system-level C functions from Nock.
|
|
||||||
How absurd would it be to make a system call from SQL? However
|
|
||||||
absurd it would be, it would be much more absurd in Nock.
|
|
||||||
|
|
||||||
- zeno is jet-propelled.
|
|
||||||
|
|
||||||
Naive Nock is inherently inefficient; eg, decrement is an O(n)
|
|
||||||
operation. To rectify this, zeno loads external accelerators
|
|
||||||
called "jets," written in C, which match well-known Nock
|
|
||||||
formulas (such as decrement). Every jet must match Nock
|
|
||||||
semantics exactly; zeno can test this automatically.
|
|
||||||
|
|
||||||
Can a jet call a foreign function? Yes, but only if it is
|
|
||||||
doing so to precisely match Nock semantics. For instance, if
|
|
||||||
you have a tuned C implementation of a pure function such as
|
|
||||||
crypto, Unicode collation, etc, etc, you can call it from a jet
|
|
||||||
- so long as the jet formula matches perfectly.
|
|
||||||
|
|
||||||
It is impossible to perform input from a matching jet. If you
|
|
||||||
perform output from jets, armed men will come to your house at
|
|
||||||
night and kill you. You probably think I'm joking about this.
|
|
||||||
|
|
||||||
- zeno is pausable (and should be stable).
|
|
||||||
|
|
||||||
In other words, zeno can be halted and restarted without OS
|
|
||||||
tools - it can participate in cooperative multitasking. More
|
|
||||||
or less a requirement for any serious interpreter, but still
|
|
||||||
worth mentioning. At present halting is a hack, but it should be
|
|
||||||
upgraded to a genuine transaction model.
|
|
||||||
|
|
||||||
- zeno does not use the C stack for nested computations.
|
|
||||||
|
|
||||||
More or less implied in the previous.
|
|
||||||
|
|
||||||
- zeno does not use heuristic memory management.
|
|
||||||
|
|
||||||
Ie, garbage collection. May I speak frankly? Managing memory
|
|
||||||
is the work of the programmer. GC is the work of Satan.
|
|
||||||
When you assign the programmer's work to the interpreter,
|
|
||||||
your fingers are working for Satan.
|
|
||||||
|
|
||||||
Nor is it a question of malloc() and "riding the bus." Since
|
|
||||||
nouns are acyclic, the only question in noun memory management
|
|
||||||
is how to improve on classic reference counting. Not a
|
|
||||||
heuristic algorithm - and not one that requires pausing the UI.
|
|
||||||
(If you're of a certain age, every time you see Safari spin its
|
|
||||||
wheel, you think: "Garbage collecting... done.")
|
|
||||||
|
|
||||||
The default allocation scheme in zeno is not reference
|
|
||||||
counting, but a three-pointer seniority scheme. This "Seuss
|
|
||||||
system" works well for some algorithms, not so well for others.
|
|
||||||
For the latter, there is reference counting.
|
|
363
Spec/zeno/2.txt
363
Spec/zeno/2.txt
@ -1,363 +0,0 @@
|
|||||||
Memory structure of Zeno (the loom):
|
|
||||||
|
|
||||||
1. Layout
|
|
||||||
|
|
||||||
You may know the typical Unix process layout, in which heap
|
|
||||||
grows upward from low memory, and stack grows downward
|
|
||||||
from high memory. The general advantage of this layout
|
|
||||||
is zero fragmentation: heap and stack can grow as far
|
|
||||||
toward each other as possible, until they meet.
|
|
||||||
|
|
||||||
This design is independent of the actual structures that grow
|
|
||||||
from both ends of a linear address space. We might just as
|
|
||||||
well have two heaps growing toward each other, or two stacks.
|
|
||||||
|
|
||||||
The loom allocator is the latter - two stacks, growing toward
|
|
||||||
each other. A stack is actually a more powerful structure
|
|
||||||
than a heap; if you have a stack and you want a heap, you can
|
|
||||||
always push one on top of the stack. If you have a heap
|
|
||||||
and you want a (contiguous) stack, you are SOL.
|
|
||||||
|
|
||||||
We call these opposed stacks "beams." A loom is two symmetric
|
|
||||||
beams, one growing up from the bottom of memory, the other
|
|
||||||
growing down from the top. The obvious constraint is that the
|
|
||||||
beams must never cross. The unused area between them may be of
|
|
||||||
any length and at any position.
|
|
||||||
|
|
||||||
(The loom design is not Nock-specific. Any functional language
|
|
||||||
with acyclic data structures can use it. For instance, the same
|
|
||||||
approach could be used for functional processing of XML.)
|
|
||||||
|
|
||||||
All loom addresses are word (32-bit) addresses. The loom
|
|
||||||
pointer format can address 2^28 words, or 1GB, which is
|
|
||||||
adequate for most light-duty programming tasks.
|
|
||||||
|
|
||||||
A "ray" is a 29-bit word offset onto a beam. If bit 29
|
|
||||||
is 0, the ray grows forward from 0 and is "west." If bit
|
|
||||||
29 is 1, the ray grows backward from the end of the loom,
|
|
||||||
and is "east." East and west address spaces overlap; any
|
|
||||||
word in the loom has an east address and a west address.
|
|
||||||
|
|
||||||
(The loom mapping cannot be implemented in hardware by any
|
|
||||||
existing MMU I know of, but it is of course much simpler than a
|
|
||||||
page table, and relatively alacritous in software. Twisted
|
|
||||||
pointers of various kinds are typical ingredients in any dynamic
|
|
||||||
language implementation.)
|
|
||||||
|
|
||||||
All u3 nouns are represented by the 32-bit word type "rat."
|
|
||||||
If bit 31 of a rat is 0, it is a "cat" - a direct atom.
|
|
||||||
Bits 0-30 of a cat are an unsigned 31-bit integer.
|
|
||||||
|
|
||||||
A rat can be a special value, u3_none - not a noun. A
|
|
||||||
rat which is not u3_none is a "fox."
|
|
||||||
|
|
||||||
If bit 31 of a rat is 1, bits 0-28 are a ray which points
|
|
||||||
to an indirect noun - a "dog." If bit 30 is 0, the dog is
|
|
||||||
a "pom" - an indirect cell. If bit 30 is 1, the noun is a
|
|
||||||
"pug" - an indirect atom.
|
|
||||||
|
|
||||||
Bit 29 on a dog is set iff the dog has special information
|
|
||||||
behind the pointer, including but not limited to allocation
|
|
||||||
header (block and reference count).
|
|
||||||
|
|
||||||
Although poms and pugs cannot be referenced through C pointers,
|
|
||||||
because a ray is not a C pointer and requires specialized address
|
|
||||||
arithmetic, their shape is described by these simple structures:
|
|
||||||
|
|
||||||
struct u3_cell {
|
|
||||||
c3_w mug_w;
|
|
||||||
u3_ray hed_ray;
|
|
||||||
u3_ray tel_ray;
|
|
||||||
};
|
|
||||||
struct u3_atom {
|
|
||||||
c3_w mug_w;
|
|
||||||
c3_w len_w;
|
|
||||||
c3_w buf_w[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
"c3_w" and "u3_ray" are both uint32_t. "buf_w" is really an
|
|
||||||
array of length "len_w," of course.
|
|
||||||
|
|
||||||
"mug_w" is a 31-bit insecure hash which matches the Watt (mug)
|
|
||||||
gate. The mug is computed lazily, as we need it - 0 is an
|
|
||||||
out-of-band value. It is useful not just in associative and
|
|
||||||
balanced trees, but also in simple functions like equality. If
|
|
||||||
two dogs have the same mug, they may be different nouns; if they
|
|
||||||
have different mugs, they must be different nouns.
|
|
||||||
|
|
||||||
We also borrow the mug field to maintain dag structure when
|
|
||||||
copying nouns. Why copy a noun? See below.
|
|
||||||
|
|
||||||
2. Allocation (simple)
|
|
||||||
|
|
||||||
Now we consider the loom's allocation algorithm, the "Seuss
|
|
||||||
system." The Seuss system manages memory with three pointers:
|
|
||||||
"hat", "cap", and "mat".
|
|
||||||
|
|
||||||
"hat" and "cap", rays, are the tops of two opposing stack
|
|
||||||
allocators, rays on opposite beams. Either the hat is east
|
|
||||||
and the cap is west, or vice versa.
|
|
||||||
|
|
||||||
"mat" is a ray on the same beam as the cap, at or below it.
|
|
||||||
|
|
||||||
The loom is hence divided into three parts:
|
|
||||||
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->.....box.......---pad----**can**........box.........<-|
|
|
||||||
| |
|
|
||||||
west east
|
|
||||||
|
|
||||||
The "pad" is free memory. The "box" is stable memory. The
|
|
||||||
"can" is temporary memory. Again, the loom is symmetric. The
|
|
||||||
can is always on one side of the pad, but that side can be east
|
|
||||||
(as shown above), or west:
|
|
||||||
|
|
||||||
0 mat cap hat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->.....box.......***can****--pad--........box.........<-|
|
|
||||||
| |
|
|
||||||
west east
|
|
||||||
|
|
||||||
The loom imposes a seniority restriction on all pointers. Box is
|
|
||||||
senior to can. A reference stored in the can may point into the
|
|
||||||
box, but a reference stored in the box cannot point into the can.
|
|
||||||
|
|
||||||
Since the box cannot point to the can, the can may be entirely
|
|
||||||
destroyed without any corruption of the box. In general, the can
|
|
||||||
is used for temporary allocation - ie, future garbage.
|
|
||||||
|
|
||||||
So the programmer in this context has two allocation choices.
|
|
||||||
She can allocate garbage on the cap, or product on the hat.
|
|
||||||
Rather than the normal model of stack and heap, she has two stack
|
|
||||||
pointers - cap and hat - which grow toward each other.
|
|
||||||
|
|
||||||
In general, any function F is expected to produce its product as a
|
|
||||||
fragmentation-free block of nouns, allocated in the box (on the
|
|
||||||
hat), with pointers to older data in the box, but not of course
|
|
||||||
the can. The can is expected to be reset after the call. Thus
|
|
||||||
F is expected, as part of the calling convention, to pick up its
|
|
||||||
own garbage.
|
|
||||||
|
|
||||||
Two stacks seems like a lot of choices for the programmer, every
|
|
||||||
time she allocates a cell. Actually, though, this choice can in
|
|
||||||
normal circumstances be made by the interpreter - because the
|
|
||||||
only memory management problem in acyclic functional programming
|
|
||||||
is composition.
|
|
||||||
|
|
||||||
When computing F(x), Zeno allocates product on the hat. When
|
|
||||||
computing F(G(x)), Zeno allocates product of G(x) on the cap, and
|
|
||||||
product of F(x) on the hat. If F(x) uses unmodified product of
|
|
||||||
G(x), these must be copied from cap to hat. For the product of
|
|
||||||
G(x) is, by definition, garbage.
|
|
||||||
|
|
||||||
And when computing F(G(H(x)))? Sadly, the calculations that
|
|
||||||
produce garbage have a nasty tendency to allocate temporary nouns
|
|
||||||
of their own. If we had an infinite number of beams, we'd be
|
|
||||||
set. But we only have two.
|
|
||||||
|
|
||||||
Zeno handles the H(x) problem by reversing the beams. Let's work
|
|
||||||
through a simple example:
|
|
||||||
|
|
||||||
As we start:
|
|
||||||
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............----------*******....................<-|
|
|
||||||
| |
|
|
||||||
west east
|
|
||||||
|
|
||||||
"." is permanent memory, the box; "*" is temporary memory, the
|
|
||||||
can; "-" is free memory, the pad.
|
|
||||||
|
|
||||||
Our goal is to allocate the product of F as ".", by pushing
|
|
||||||
the hat a couple of columns east.
|
|
||||||
|
|
||||||
First, we depart to compute G. The product of G is "!", and is
|
|
||||||
allocated on the cap. Not that G is hardcoded for this role, of
|
|
||||||
course - it allocates its product on the hat, like any function.
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............--------!!*******....................<-|
|
|
||||||
| | | |
|
|
||||||
west mat hat east
|
|
||||||
cap
|
|
||||||
now now
|
|
||||||
|
|
||||||
Thus, so long as the VM exhibits east/west independence,
|
|
||||||
we can compute G with the same code that computed F, on the
|
|
||||||
opposite beam. And if G depends on another function, H?
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............????----!!*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
west mat cap hat east
|
|
||||||
now now now
|
|
||||||
|
|
||||||
The garbage of G - which is the product of H - is "?". How do we
|
|
||||||
get H to allocate on the cap? By reversing the beams again.
|
|
||||||
This solution can be iterated indefinitely - until, of course,
|
|
||||||
the cap and hat collide. At this point you are out of memory.
|
|
||||||
|
|
||||||
We complete the calculation of G by retreating:
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............--------!!*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
| hat cap mat east
|
|
||||||
west now now now
|
|
||||||
|
|
||||||
We have now computed G and placed its product on the cap. So we
|
|
||||||
are ready to compute F, placing its product on the hat. The
|
|
||||||
product of F is "~":
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............~~------!!*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
west hat cap mat east
|
|
||||||
now now now
|
|
||||||
|
|
||||||
We then retract the cap, freeing the product of G:
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............~~--------*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
west hat cap mat east
|
|
||||||
now now now
|
|
||||||
|
|
||||||
Thus we have pushed the product of F, without any garbage,
|
|
||||||
on the hat. The algorithm is optimal, except for the cost of
|
|
||||||
copying temporary results - which varies from 0 to prohibitive.
|
|
||||||
|
|
||||||
One case in which the algorithm above needs improvement is
|
|
||||||
tail-call compaction. In a tail-call loop, F(x) resolves to
|
|
||||||
F(G(x)) - which resolves to F(G(G(x))), etc, etc.
|
|
||||||
|
|
||||||
The loom knows the final destination at all times and never needs
|
|
||||||
to copy the result - but it also accumulates a pile of
|
|
||||||
intermediate results, G(x), G(G(x)), etc, on the cap. Where
|
|
||||||
"!" is G(x), "?" is G(G(x)), '#" is G(G(G(x))):
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............----##??!!*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
| hat cap mat east
|
|
||||||
west now now now
|
|
||||||
|
|
||||||
This is obviously craptastic. Can anything be done about it?
|
|
||||||
That depends on one thing - whether G(G(x)) references G(x). If
|
|
||||||
it does (for example, when inverting a list), no compaction is
|
|
||||||
possible - there is nothing to free.
|
|
||||||
|
|
||||||
But if G(G(x)) does not reference G(x), we can simply slide
|
|
||||||
G(G(x)) down the cap over its parent. In general, tail calls can
|
|
||||||
be computed in fixed space if the new tail context does not
|
|
||||||
reference the old tail context.
|
|
||||||
|
|
||||||
How can we determine this? Well, we could analyze the formula.
|
|
||||||
In a compilation context, this is probably what we'd do. In an
|
|
||||||
interpreter, however, there is no room for static analysis.
|
|
||||||
|
|
||||||
Therefore, the only way to determine whether G(G(x)) references
|
|
||||||
G(x) is a brute-force nano-garbage collection. We simply chase
|
|
||||||
any pointers in G(G(x)) and see if they fall into G(x). If not,
|
|
||||||
we slide "?" down over "!". This is called "tamping."
|
|
||||||
|
|
||||||
Tamping is a effective optimization because its cost is
|
|
||||||
proportional to the size of G(G(x)). This may be an arbitrarily
|
|
||||||
large and complex noun, but we do not have to chase pointers
|
|
||||||
outside G(G(x)). "?" cannot address "!" through either "." or
|
|
||||||
"*", because "." and "*" cannot point to "!" due to seniority.
|
|
||||||
|
|
||||||
A tamped loom makes an excellent top-level default. For many
|
|
||||||
algorithms, it is entirely impractical. But, if the implementor
|
|
||||||
of the algorithm knows this, it is easy to request special
|
|
||||||
treatment. The pad is an unfragmented block of memory - the
|
|
||||||
appropriate input for any memory-management algorithm.
|
|
||||||
|
|
||||||
The Watt programmer, optimizing for Zeno, does so by using hints
|
|
||||||
(Nock 11) to select one of three memory disciplines: %flee,
|
|
||||||
%stay, and %keep. The above is the %flee discipline, which
|
|
||||||
is the default. In general, the memory discipline defines what
|
|
||||||
Zeno does where, under %flee, it would depart.
|
|
||||||
|
|
||||||
The %stay discipline never departs. Instead, it uses the cap as
|
|
||||||
a conventional stack, and the hat as a conventional heap. Only
|
|
||||||
control information is allocated on the cap. All product nouns -
|
|
||||||
F(x), G(x) and H(x) - are allocated on the hat:
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............??!!~~----*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
west hat cap mat east
|
|
||||||
now now now
|
|
||||||
|
|
||||||
What do we do with G(x) and H(x)? We leak the motherfsckers.
|
|
||||||
But at least there is no copying.
|
|
||||||
|
|
||||||
This is remedied by the %keep discipline. Remember that
|
|
||||||
disciplines affect one thing: departure policy. In the %flee
|
|
||||||
discipline, we depart; in the %stay discipline, we remain.
|
|
||||||
|
|
||||||
In %keep, we depart - but install a refcount allocator. In the
|
|
||||||
departing frame, this upgrades the hat to a reference-counted
|
|
||||||
heap. It also sets the memory discipline to %stay. Thus, in the
|
|
||||||
departing frame, we do not leak but rather free.
|
|
||||||
|
|
||||||
Since we are refcounting in the departed frame, not the current
|
|
||||||
frame, the result still needs to be copied back if used. It is
|
|
||||||
good to copy out of an allocator, because the allocator doubles
|
|
||||||
the size of a cell.
|
|
||||||
|
|
||||||
Therefore, if we compute F under %keep, we observe, in the
|
|
||||||
departed context,
|
|
||||||
|
|
||||||
old old old
|
|
||||||
0 hat cap mat 1GB
|
|
||||||
| | | | |
|
|
||||||
|->...............-----?=!?&*******....................<-|
|
|
||||||
| | | | |
|
|
||||||
west mat hat rut east
|
|
||||||
cap
|
|
||||||
now now now
|
|
||||||
|
|
||||||
"&" is the free list; "=" is a free block.
|
|
||||||
|
|
||||||
Using the allocator adds a fourth memory pointer, "rut", to the
|
|
||||||
Seuss system. The rut sets (a) the location of the free list
|
|
||||||
and (b) the boundaries of reference control. If the rut is set,
|
|
||||||
before advancing the hat, zeno will try to allocate out of the
|
|
||||||
free list.
|
|
||||||
|
|
||||||
Since nouns are acyclic, reference counting is always effective.
|
|
||||||
Why bother with this loom business, then? Why not just a simple
|
|
||||||
reference-counting allocator for all of memory?
|
|
||||||
|
|
||||||
One of the big performance problems with refcounting is its
|
|
||||||
tendency to absolutely screw the hell out of your cache. Address
|
|
||||||
space can be backed by anything - from on-chip cache to a tape
|
|
||||||
drive. Many programs pass around references to memory they use
|
|
||||||
much more rarely, and when they use this memory it is only to
|
|
||||||
read them. If the entire space is in one reference pool, this
|
|
||||||
will generate a shower of *writes* to deep data structures that
|
|
||||||
are essentially static.
|
|
||||||
|
|
||||||
Reference counting in the loom is *local*. References only need
|
|
||||||
to be tracked for nouns between rut and hat. Even if these nouns
|
|
||||||
reference reference-counted nouns elsewhere in the loom, counts
|
|
||||||
do not need to be updated for any noun between 0 and hat, or rut
|
|
||||||
and 1GB.
|
|
@ -1,252 +0,0 @@
|
|||||||
1: Introduction and philosophy
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Urbit is a new programming and execution environment
|
|
||||||
designed from scratch. Any resemblance to existing languages
|
|
||||||
or operating systems is coincidental, cosmetic, or inevitable.
|
|
||||||
|
|
||||||
## Urbit is a stack ##
|
|
||||||
|
|
||||||
Nock is a stateless virtual machine defined in 200 words. The
|
|
||||||
Nock machine is sealed - all execution is "pure." Nock's goal is
|
|
||||||
extreme commoditization of computing semantics.
|
|
||||||
|
|
||||||
Hoon is a high-level language which defines itself in Nock. Its
|
|
||||||
self-compiling kernel, 7000 lines of code, specifies Hoon
|
|
||||||
unambiguously; there is no Hoon spec. Hoon can be classified as
|
|
||||||
a pure, strict higher-order static type-inferred functional
|
|
||||||
language, with co/contra/bivariance and genericity. However,
|
|
||||||
Hoon does not use lambda calculus, unification, or other
|
|
||||||
constructs from "PL theory." Hoon also excels at handling and
|
|
||||||
validating untyped data, a common task on teh Internets. Its
|
|
||||||
syntax is entirely novel and initially quite frightening.
|
|
||||||
|
|
||||||
Arvo is a deterministic functional operating system defined in
|
|
||||||
Hoon. While still basically a toy, it can serve web apps and
|
|
||||||
network securely with other Arvo instances. An Arvo instance
|
|
||||||
is designed to be a simple independent computer in the cloud.
|
|
||||||
|
|
||||||
It's possible to use Nock without Hoon, but there is no obvious
|
|
||||||
reason to do so. It's not necessary to learn Nock to learn Hoon,
|
|
||||||
but it helps a lot. The same for Hoon and Arvo. Therefore,
|
|
||||||
we'll work through all three in order. in order. If you're
|
|
||||||
convinced that you want to learn Urbit, feel free to skip the
|
|
||||||
justifications below.
|
|
||||||
|
|
||||||
## Nock ##
|
|
||||||
|
|
||||||
Nock is a virtual machine, like the Java VM or Microsoft CLR.
|
|
||||||
Just as you execute Java by compiling it to the JVM, you execute
|
|
||||||
Hoon by compiling it to Nock.
|
|
||||||
|
|
||||||
Do you need to learn Nock to learn Hoon? In theory, no, just as
|
|
||||||
you don't need to know anything about the JVM to learn Java.
|
|
||||||
Indeed, in theory we could compile Hoon to the JVM (or Java to
|
|
||||||
Nock). What's the difference between Nock and the JVM, anyway?
|
|
||||||
Why do we need a new virtual machine? And since we'll only be
|
|
||||||
programming in Hoon, why do we care how Hoon is executed?
|
|
||||||
|
|
||||||
There is no formal difference between Nock and the JVM. In
|
|
||||||
theory, both are Turing-complete computers. In practice, they
|
|
||||||
have just about nothing in common.
|
|
||||||
|
|
||||||
The main practical difference is that Nock is two orders of
|
|
||||||
magnitude simpler. The JVM spec is a book. The Nock spec is 200
|
|
||||||
words; it fits on a T-shirt; it gzips to 371 bytes. There are
|
|
||||||
lots of other differences, but all follow from this.
|
|
||||||
|
|
||||||
Why does size matter? The goal of any programming language is to
|
|
||||||
become a standard. Universal standardization equals world
|
|
||||||
domination. The simpler a specification is, the easier to
|
|
||||||
standardize. Nock is a practical interpreter as simple as the
|
|
||||||
most frozen and fundamental of protocols or formats: IPv4, XML,
|
|
||||||
JSON. A 200-word spec needs a standards process like a fish
|
|
||||||
needs swim lessons. When a T-shirt goes to war with a book, it's
|
|
||||||
like tanks against cavalry - or should be, anyway.
|
|
||||||
|
|
||||||
Since every program in Hoon (including the Hoon compiler) reduces
|
|
||||||
to Nock, every program in Hoon inherits Nock's precision. If two
|
|
||||||
Nock interpreters produce different results, one is wrong, and it
|
|
||||||
is always easy to tell which - without a "standards lawyer." Can
|
|
||||||
we imagine the end of incompatiblity?
|
|
||||||
|
|
||||||
Essential to actually realizing this promise is a second
|
|
||||||
difference between Nock and the JVM, which is that the JVM can
|
|
||||||
call Unix system calls and libraries, and Nock can't. Nock has
|
|
||||||
no "native methods" or foreign-function interface. Nor is it
|
|
||||||
ever extended, embellished, forked, improved or advanced.
|
|
||||||
|
|
||||||
Java began as a portable language; so did C. Most Java today is
|
|
||||||
server-side Java, dependent no less than C on library and OS
|
|
||||||
configuration. It is possible to write portable Java; it is
|
|
||||||
possible to write portable C. It is not possible to write
|
|
||||||
unportable Nock or Hoon. It is also not possible to write
|
|
||||||
insecure Nock or Hoon, unless your interpreter is so broken
|
|
||||||
there's an actual hole in its skull.
|
|
||||||
|
|
||||||
How does Nock obtain native CPU performance, if it can't call
|
|
||||||
native code? This is actually a much more pressing problem in
|
|
||||||
Nock than in conventional virtual machines, like the JVM, because
|
|
||||||
naive Nock is hopelessly inefficient. Nock is defined in a page
|
|
||||||
of pseudocode, and a naive interpreter can be written in a page
|
|
||||||
of any language. But since Nock's only arithmetic operator is
|
|
||||||
increment, decrement in your one-page interpreter is an `O(n)`
|
|
||||||
operation. Addition is `O(n^2)`. And so on.
|
|
||||||
|
|
||||||
The programmer cannot solve this problem by calling a C function,
|
|
||||||
because Nock can't do that. In theory, an optimizing Nock
|
|
||||||
interpreter might be able to analyze the code and reduce it to a
|
|
||||||
simpler equivalent. But this would be a true research problem.
|
|
||||||
|
|
||||||
Instead, a practical Nock engine simply recognizes code it knows
|
|
||||||
and substitutes equivalent C functions, or "jets." For instance,
|
|
||||||
in theory there are many different ways to express decrement in
|
|
||||||
Nock, but in practice the Nock interpreter will execute only one:
|
|
||||||
the decrement function in the Hoon kernel. Therefore, the only
|
|
||||||
Nock decrement that must be optimized is the code that Hoon
|
|
||||||
generates when it compiles its own decrement. All others will
|
|
||||||
suck, so don't roll your own. Code recognition, as opposed to
|
|
||||||
code analysis, is not a research problem.
|
|
||||||
|
|
||||||
Jet propulsion separates mechanism and policy, transferring the
|
|
||||||
task of achieving native performance from the programmer to the
|
|
||||||
sysadmin. The Hoon programmer must still use hints to mark
|
|
||||||
functions for jet recognition, but cannot control or discern how
|
|
||||||
these functions are actually executed. Of course, a correct jet
|
|
||||||
is indistiguishable in every way, except timing, from naive Nock.
|
|
||||||
The 200-word spec defines the result, not the algorithm.
|
|
||||||
|
|
||||||
We can see a jet as a sort of "functional device driver." For
|
|
||||||
instance, an OpenGL programmer today has no idea whether her GL
|
|
||||||
operations are implemented in software or by a GPU. This
|
|
||||||
abstraction is essential to modern graphics programming.
|
|
||||||
|
|
||||||
When we compare jets to native calls, what are the pros and cons?
|
|
||||||
Jets have only one disadvantage: high-performance code must be
|
|
||||||
written twice, once in Hoon and once in C. Indeed, from the C
|
|
||||||
programmer's perspective, Hoon is a specification language for
|
|
||||||
your C functions. Hoon specifications are executable, of course,
|
|
||||||
so you can test the two implementations against each other -
|
|
||||||
again, transparently to the programmer. Moreover, the jet can
|
|
||||||
even fail in special cases and drop back to the interpreter.
|
|
||||||
|
|
||||||
Hoon ships with a Nock that jet-propels most of the Hoon kernel,
|
|
||||||
including most of the Hoon compiler. If you're wondering how we
|
|
||||||
wrote Hoon in Hoon when we didn't have Hoon, the answer is that
|
|
||||||
we wrote Hoon in C and evolved this C code into a mere jet. This
|
|
||||||
process is not recommended unless absolutely necessary - by far
|
|
||||||
the best way to write the jet pair is to write the Hoon first.
|
|
||||||
|
|
||||||
## Hoon ##
|
|
||||||
|
|
||||||
If I can summarize Hoon's goal, it's to be the C of functional
|
|
||||||
programming. If you're not so arthritic that you learned to code
|
|
||||||
in Turbo Pascal, you may never fully appreciate the metaphor.
|
|
||||||
|
|
||||||
All languages in the Algol procedural family, including both C
|
|
||||||
and Pascal, map straightforwardly onto a conventional CPU. But
|
|
||||||
Pascal and C handle this mapping very differently. Pascal and C
|
|
||||||
both have pointers and arrays, but Pascal works hard to treat
|
|
||||||
both pointers and arrays as mathematical abstractions. C drops
|
|
||||||
the abstraction; it makes no bones about the fact that a pointer
|
|
||||||
is a memory address.
|
|
||||||
|
|
||||||
To a Pascal purist, to anyone who thinks mathematically, this
|
|
||||||
seemed hideous. C isn't really a high-level language at all -
|
|
||||||
it's a glorified macro assembler. Mankind retreats to the cave.
|
|
||||||
But to programmers who are not natural mathematicians, whose
|
|
||||||
minds are mechanical rather than abstract, C is a lifesaver.
|
|
||||||
Since most mathematicians are also good mechanical thinkers,
|
|
||||||
whereas very few people are naturally adept at abstraction,
|
|
||||||
C slew and pillaged the once promising empire of Pascal.
|
|
||||||
|
|
||||||
There are two broad families of functional language available
|
|
||||||
today: Haskell/ML, and Lisp. The Haskell family is relatively
|
|
||||||
abstract; the Lisp family, relatively concrete. Perhaps Lisp is
|
|
||||||
about as abstract as Pascal; Haskell is far more abstract. Both
|
|
||||||
rest on the fundamental abstraction of functional programming,
|
|
||||||
the lambda calculus, and more generally the foundational
|
|
||||||
metamathematics of the late 19th and early 20th centuries.
|
|
||||||
|
|
||||||
Hoon has nothing to do with any of this stuff. It has functions
|
|
||||||
and types, or what appear to be functions and types. On closer
|
|
||||||
inspection, they are not abstractions at all, just glorified Nock
|
|
||||||
macros.
|
|
||||||
|
|
||||||
If we compare these concrete patterns to the genuine abstractions
|
|
||||||
of Haskell, we see that - as with Pascal and C - Hoon is roughly
|
|
||||||
as expressive as Haskell. Haskell has higher-order type
|
|
||||||
inference; Hoon has "higher-order" "type" "inference." Some
|
|
||||||
Haskell extensions have dependent types - Hoon has "refined"
|
|
||||||
"types." Hoon, like Lisp, unlike Haskell, is also very
|
|
||||||
comfortable with typeless data; it should be, because it has no
|
|
||||||
types, only "types." The Hoon features and the Haskell
|
|
||||||
abstractions have nothing in common - except that they solve the
|
|
||||||
same problems for you, the programmer. In short, Hoon next to
|
|
||||||
Haskell is a white shark next to a killer whale. The apparent
|
|
||||||
resemblance is strictly superficial.
|
|
||||||
|
|
||||||
So we could describe Hoon as a pure, strict, higher-order typed
|
|
||||||
functional language. But don't do this in front of a Haskell
|
|
||||||
purist, unless you put quotes around "typed," "functional," and
|
|
||||||
possibly even "language." We could also say "object-oriented,"
|
|
||||||
with the same scare quotes for the cult of Eiffel.
|
|
||||||
|
|
||||||
Knowing Pascal made it harder, not easier, to learn C. Knowing
|
|
||||||
Haskell or Lisp makes it harder to learn Hoon. Indeed, knowing
|
|
||||||
either would have made it impossible for me to write Hoon. I do
|
|
||||||
know C, of course, and the spirit of K&R is all over Hoon. Or so
|
|
||||||
I'd like to think. Just as C is little more than a macro
|
|
||||||
assembler for machine code, Hoon is little more than a macro
|
|
||||||
assembler for Nock.
|
|
||||||
|
|
||||||
The most basic difference between Hoon and other languages is
|
|
||||||
that Hoon is defined in Hoon. There is no formal Hoon spec -
|
|
||||||
just a self-compiling compiler written in Hoon. The target of
|
|
||||||
this compiler is, of course, Nock. Thus Hoon is as precisely
|
|
||||||
defined as Nock, which is quite precisely indeed.
|
|
||||||
|
|
||||||
This would be true regardless of the size of Hoon in Hoon, but
|
|
||||||
Hoon in Hoon is in fact quite small. The Hoon kernel is 7000
|
|
||||||
lines; it gzips to 25K. But this includes not only the
|
|
||||||
self-compiling compiler, but also all the standard libraries it
|
|
||||||
needs. The compiler alone is 2500 lines, including a very
|
|
||||||
intricate "monadic" parser, a non-Hindley-Milner "type inference"
|
|
||||||
engine, and a Nock code generator. This reflects both the
|
|
||||||
internal simplicity of Hoon and its expressiveness. If you know
|
|
||||||
these 2500 lines, and an expert should, you _know_ Hoon.
|
|
||||||
|
|
||||||
On the other hand, the _apparent_ complexity of Hoon is very
|
|
||||||
high. When you open a Hoon file, you are confronted with an
|
|
||||||
enormous avalanche of barely structured line noise. Again this
|
|
||||||
reminds us of C, which makes no attempt at the kind of abstract
|
|
||||||
prettiness we expect from a Pascal or a Haskell. Learning Hoon
|
|
||||||
involves learning nearly 100 ASCII digraph "runes."
|
|
||||||
|
|
||||||
Is this a harsh learning curve? Of course it is. On the other
|
|
||||||
hand, it is not a mathematical task, but a mechanical one. It is
|
|
||||||
trivial compared to the task of learning the Chinese alphabet,
|
|
||||||
memorizing the Qu'ran, etc, all rote mental tasks routinely
|
|
||||||
performed by normal human 11-year-olds. If you have an
|
|
||||||
11-year-old who understands the Hindley-Milner algorithm, you
|
|
||||||
have a remarkable young mathematician.
|
|
||||||
|
|
||||||
A practical programming language is first and foremost a UI for
|
|
||||||
programmers - meaning human programmers. Concrete languages beat
|
|
||||||
abstract ones because they play to the strengths of the human
|
|
||||||
brain, and avoid its weaknesses. Functional programming is
|
|
||||||
traditionally reserved for the topmost echelon of natural talent.
|
|
||||||
I'd like to think that anyone who can learn to fix a Chevy can
|
|
||||||
learn to write a Hoon function. We'll see if that's true.
|
|
||||||
|
|
||||||
A programming language is called a language for a reason - it
|
|
||||||
should activate the human linguistic lobes. Learning Hoon
|
|
||||||
is like learning a language very alien to your first, such as
|
|
||||||
Chinese from English. Before you know Hoon, it looks like
|
|
||||||
squiggles. Once you know Hoon, and the rote task of syntax
|
|
||||||
processing is hardwired, you look at your screen and _see_
|
|
||||||
the function. Or, at least, I do - I hope you can too.
|
|
||||||
|
|
||||||
## Arvo ##
|
|
||||||
|
|
||||||
Lorem ipsum.
|
|
File diff suppressed because it is too large
Load Diff
@ -1,81 +0,0 @@
|
|||||||
2: Hoon - Philosophy
|
|
||||||
=========
|
|
||||||
|
|
||||||
Yo, let's write some Hoon!
|
|
||||||
|
|
||||||
In this tutorial we're going to try to learn Hoon, without first
|
|
||||||
learning either Nock or Arvo. You probably should at least skim
|
|
||||||
the Nock doc though - learning Hoon without understanding Nock is
|
|
||||||
a little like learning C without understanding how a CPU works.
|
|
||||||
It is not easier than knowing both, it's harder.
|
|
||||||
|
|
||||||
As for Arvo, all we'll use of it here is the power to edit a
|
|
||||||
library and type expressions on the command line. These are
|
|
||||||
not hard things and we'll explain them as we go along.
|
|
||||||
|
|
||||||
We'll cover Hoon in three sections - philosophy, syntax, and
|
|
||||||
semantics. You should probably just skip to syntax.
|
|
||||||
|
|
||||||
The good news about Hoon is that it compiles itself to Nock in
|
|
||||||
3400 lines of Hoon. If this number is accurate (it is), Hoon
|
|
||||||
is very expressive, or very simple, or both. (It's both.) The
|
|
||||||
bad news is that it really has nothing at all in common, either
|
|
||||||
syntactically or semantically, with anything you've used before.
|
|
||||||
|
|
||||||
In particular, please remember that _you are not too stupid_ to
|
|
||||||
program in Hoon. Hoon is FP made stupid - for the stupid, by the
|
|
||||||
stupid. (This is the real reason it's only 3400 lines.)
|
|
||||||
|
|
||||||
Maybe you've had some bad experiences with a higher-order typed
|
|
||||||
functional language - like Haskell or OCaml. Dare we suspect
|
|
||||||
that these experiences came on one of our nation's fine college
|
|
||||||
campuses? Well, we have to admit, Hoon is a higher-order typed
|
|
||||||
functional language. But really, don't let this discourage you.
|
|
||||||
|
|
||||||
First, Hoon hasn't been to college. Second, at least it's
|
|
||||||
strict. And third, we don't use any of that PL theory shit.
|
|
||||||
We're OS guys. Our mission is not to praise PL, but to drive it
|
|
||||||
out of the CS department with a flaming sword, back to the
|
|
||||||
fundless ghetto of math where it belongs. Hoon is 'street FP.'
|
|
||||||
The only functional language it gives any real props to is JS.
|
|
||||||
JS sucks, of course, but at least it's a player. (Scala is a
|
|
||||||
player too, but it's also the size of a blue whale's ass.)
|
|
||||||
|
|
||||||
We hope we're not being too harsh on PL theory. The fact is that
|
|
||||||
this branch of mathematics (constructive logic or whatever) is a
|
|
||||||
perfectly adequate and remarkably elegant formal description of
|
|
||||||
computing. But, of course, it is not the only such description.
|
|
||||||
And it was originally created for mathematicians, not coders.
|
|
||||||
|
|
||||||
Nor is the math department's standard of elegance the only such
|
|
||||||
standard. The programmer has his own design sense, which
|
|
||||||
corresponds more closely to usability in a UI sense. To read a
|
|
||||||
program is to simulate it in your head, and the skill needed to
|
|
||||||
do this revolves around tracing long complex sequences of very
|
|
||||||
simple transformations.
|
|
||||||
|
|
||||||
For these simple transformations, I think, we use a completely
|
|
||||||
different processing path - less a mathematical intution, than a
|
|
||||||
mechanical one. This is why many good C programmers are bad at
|
|
||||||
symbolic math. Of course, we all should be good at all things -
|
|
||||||
but are we? And this is why Hoon is not Haskell.
|
|
||||||
|
|
||||||
For example, it's very true and cool that you can represent all
|
|
||||||
data as functions. It's also pretty neat that you can take a
|
|
||||||
leak while standing on your head, and you always hit the bowl.
|
|
||||||
To an OS guy it seems more interesting to represent functions as
|
|
||||||
data. Like, you know how to send data over the network. Do you
|
|
||||||
know how to send functions over the network?
|
|
||||||
|
|
||||||
(On the other hand, if only because Hoon is very immature, Haskell
|
|
||||||
is good at many things that Hoon isn't good at. For instance,
|
|
||||||
good list/container comprehensions are the pons asinorum of a
|
|
||||||
functional language. Hoon's actually kind of suck. This is not
|
|
||||||
(well, mostly not) because Hoon sucks, but because (a) they are
|
|
||||||
some of the oldest library code in the system, and (b) they have
|
|
||||||
never seen either end of a top-notch functional programmer.
|
|
||||||
Despite the fact that category theory can go bite itself, people
|
|
||||||
with more FP experience on more mature languages probably have a
|
|
||||||
lot to contribute here.)
|
|
||||||
|
|
||||||
Anyway. That's enough philosophy. Come on, who needs it?
|
|
@ -1,326 +0,0 @@
|
|||||||
3: Hoon - Syntax
|
|
||||||
=========
|
|
||||||
|
|
||||||
Now, let's actually look at Hoon. Really, try not to recoil
|
|
||||||
in horror. It's actually not anything like line noise.
|
|
||||||
|
|
||||||
Open the Hoon kernel - `urb/les/arvo/hoon.hoon`. Let's look at
|
|
||||||
the full, official decrement function (line 549):
|
|
||||||
|
|
||||||
++ dec
|
|
||||||
~/ %dec
|
|
||||||
|= a=@
|
|
||||||
^- @
|
|
||||||
?< =(0 a)
|
|
||||||
=+ b=@
|
|
||||||
|-
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
Whaa?
|
|
||||||
|
|
||||||
Any attempt to understand this in terms of any language you
|
|
||||||
already know would clearly be mistaken. In both syntax and
|
|
||||||
semantics, learning to program in Hoon is learning to program all
|
|
||||||
over again. When we say from scratch, we mean from scratch!
|
|
||||||
|
|
||||||
It's actually worse than that - learning Hoon is learning to
|
|
||||||
_read_ all over again. Hoon is a keyword-free language - any
|
|
||||||
alphanumeric text in the program is part of the program. Where
|
|
||||||
other languages have reserved words, Hoon has squiggles.
|
|
||||||
|
|
||||||
We use so many of these ASCII glyphs that we like to be able
|
|
||||||
to read them out loud. A language is meant to be _said_. The
|
|
||||||
squiggles have conventional names, sort of, some of them, some of
|
|
||||||
them easy to say, others not so much. So we've renamed them:
|
|
||||||
|
|
||||||
ace space gal < per )
|
|
||||||
bar | gar > sel [
|
|
||||||
bas \ hax # sem ;
|
|
||||||
buc $ hep - ser ]
|
|
||||||
cab _ kel { sig ~
|
|
||||||
cen % ker } soq '
|
|
||||||
col : ket ^ tar *
|
|
||||||
com , lus + tec `
|
|
||||||
doq " pam & tis =
|
|
||||||
dot . pat @ wut ?
|
|
||||||
fas / pel ( zap !
|
|
||||||
|
|
||||||
You just have to memorize these names. Sorry.
|
|
||||||
|
|
||||||
But is this at least enough symbols? Alas, nowhere near.
|
|
||||||
ASCII's glyph supply is not the greatest, but we can make all the
|
|
||||||
squiggles we need by forming digraphs, or _runes_.
|
|
||||||
|
|
||||||
To pronounce a rune, concatenate the glyph names, stressing the
|
|
||||||
first syllable and softening the second vowel into a "schwa."
|
|
||||||
Hence, to say `~.`, say "sigdot." To say `|=`, say "bartis."
|
|
||||||
Which has an inevitable tendency to turn into "barts" - a sin
|
|
||||||
to be encouraged. In any language actually spoken by actual
|
|
||||||
humans, laziness soon rounds off any rough edges.
|
|
||||||
|
|
||||||
So if we had to read the above decrement, omitting the spaces
|
|
||||||
(which only a real purist would pronounce), we'd say: "luslus dec
|
|
||||||
sigfas cen dec bartis a tis pat sigbar soq dec soq ketcab pat wutgal
|
|
||||||
tis pel zero a per tislus b tis pat barhep wutcol tis pel a lus pel
|
|
||||||
b per per b buc pel b lus pel b per per." The authorities would
|
|
||||||
then arrive, and drag us out in a big net. Definitely don't do
|
|
||||||
this at the airport.
|
|
||||||
|
|
||||||
Geeks being solitary by nature, opportunities for reading code
|
|
||||||
aloud are limited. But studies by actual scientists have shown
|
|
||||||
that even when we read silently, we activate the motor cortex
|
|
||||||
that controls our vocal cords. Even if we never speak these
|
|
||||||
squiggles, they're easier to _think_ if bound to simple sounds.
|
|
||||||
|
|
||||||
(And don't worry if you can't get yourself to say "lus" instead
|
|
||||||
of "plus" for `+`, or "tar" instead of "star" for `*` - I have
|
|
||||||
this problem myself. It's much easier to replace "underscore"
|
|
||||||
with "cab" or "ampersand" with "pam.")
|
|
||||||
|
|
||||||
Hoon has almost 90 digraphic runes. They are easier to organize
|
|
||||||
in your head, though, because the choice of glyph is not random.
|
|
||||||
The second glyph in a rune means little or nothing, but the first
|
|
||||||
defines a rough semantic category. These categories are:
|
|
||||||
|
|
||||||
| bar gates (ie, functions) (ie, one-method cores)
|
|
||||||
? wut conditionals, booleans, tests
|
|
||||||
: col tuples
|
|
||||||
. dot nock operators
|
|
||||||
$ buc factory macros (ie, type definitions)
|
|
||||||
^ ket type conversions
|
|
||||||
= tis compositions
|
|
||||||
% cen invocations
|
|
||||||
& pam gears (ie, objects) (ie, multi-method cores)
|
|
||||||
~ sig hints
|
|
||||||
; sem miscellaneous macros
|
|
||||||
! zap special operations
|
|
||||||
|
|
||||||
Each rune has _[not]_ its own doc file in the `rune/190` directory. The
|
|
||||||
name of the file is the name of the rune, minus the vowels.
|
|
||||||
Thus, `|=` or "bartis" is _[not]_ defined in `rune/190/brts.txt`.
|
|
||||||
|
|
||||||
Opening this file, we see:
|
|
||||||
|
|
||||||
%brts |= "bartis"
|
|
||||||
|
|
||||||
define:
|
|
||||||
[%brts p=gene q=gene]
|
|
||||||
|
|
||||||
expand:
|
|
||||||
[%brts *] [%brcb p.gen (~(put by *(map term foot)) %% [%ash q.gen])]
|
|
||||||
|
|
||||||
There should be some actual discussion, but there isn't. Still,
|
|
||||||
`brts.txt` is quite complete as a definition of `|=`. How? We
|
|
||||||
need to step back a little.
|
|
||||||
|
|
||||||
When the Hoon parser parses a source file, it generates a noun
|
|
||||||
called a `gene`. If you know what an AST is, a gene is an AST node.
|
|
||||||
If you don't, don't worry about it.
|
|
||||||
|
|
||||||
Search the current kernel for `++ gene` - note double space.
|
|
||||||
This code is both the type declaration for type `gene`, and
|
|
||||||
a function that maps an untyped noun to a typed gene. In it
|
|
||||||
you'll see the above definition,
|
|
||||||
|
|
||||||
[%brts p=gene q=gene]
|
|
||||||
|
|
||||||
Ie, one kind of gene is a triple whose head is the constant
|
|
||||||
`%brts`, and whose tail is a pair of genes, `p` and `q`.
|
|
||||||
|
|
||||||
We also see the semantics of this rune: it expands to
|
|
||||||
|
|
||||||
[%brcb p.gen (~(put by *(map term foot)) %% [%ash q.gen])]
|
|
||||||
|
|
||||||
ie, `|=` is a built-in macro. But back to syntax.
|
|
||||||
|
|
||||||
What is `%brts`? The atom also known as `1937011298` or
|
|
||||||
`0x73747262`. Simply a string mapped to an unsigned integer, LSB
|
|
||||||
first. It's easy to see why the vowels got lost - `%bartis` is
|
|
||||||
`126896762413410` or `0x736974726162`. On a 32-bit CPU with
|
|
||||||
31-bit direct atoms, `%brts` is direct and `%bartis` indirect
|
|
||||||
(not that the programmer can tell the difference). But you still
|
|
||||||
say "bartis."
|
|
||||||
|
|
||||||
For instance, in the decrement above, we have
|
|
||||||
|
|
||||||
|= a=@
|
|
||||||
~| 'dec'
|
|
||||||
^- @
|
|
||||||
?< =(0 a)
|
|
||||||
=+ b=@
|
|
||||||
|-
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
In this `%brts`, `p` is
|
|
||||||
|
|
||||||
a=@
|
|
||||||
|
|
||||||
and `q` is
|
|
||||||
|
|
||||||
~| 'dec'
|
|
||||||
^- @
|
|
||||||
?< =(0 a)
|
|
||||||
=+ b=@
|
|
||||||
|-
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
We are starting to see the principles of Hoon syntax. Let's make
|
|
||||||
them clear.
|
|
||||||
|
|
||||||
First, for any rune, the Hoon parser has two kinds of syntax:
|
|
||||||
normal and custom. Most runes, such as `|=`, have only normal
|
|
||||||
syntax without custom syntax. Almost all runes with custom
|
|
||||||
syntax also have normal. Custom can mean anything; normal
|
|
||||||
is rigid and uniform.
|
|
||||||
|
|
||||||
All programming languages, but especially functional ones, face
|
|
||||||
two difficult syntactic problems. One is controlling the large
|
|
||||||
numbers of terminators that appear in any deeply nested tree
|
|
||||||
structure - Lisp is infamous for its piles of right parens.
|
|
||||||
These are not a serious usability problem, except inasmuch as you
|
|
||||||
consider ugly a usability problem (which I do). Two, a more
|
|
||||||
serious concern, is keeping complex routines from flowing off the
|
|
||||||
right margin as tab depth increases.
|
|
||||||
|
|
||||||
A glance at the more complex organs of the Hoon kernel reveals
|
|
||||||
that Hoon is relatively untroubled by either of these woes. But
|
|
||||||
why? One dubious panacea for the terminator problem is the use
|
|
||||||
of significant whitespace. Whitespace in Hoon is not
|
|
||||||
significant. (To be exact, the presence or absence of whitespace
|
|
||||||
matters, but the quantity never does.)
|
|
||||||
|
|
||||||
The answer is that the normal syntax for every rune has two
|
|
||||||
forms: "wide" and "tall." As a functional language, Hoon does
|
|
||||||
not distinguish between statements and expressions, but normal
|
|
||||||
wide syntax is expression-like and tall is statement-like.
|
|
||||||
|
|
||||||
For instance, in our example above,
|
|
||||||
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
is a tall normal form. The equivalent wide form is
|
|
||||||
|
|
||||||
?:(=(a +(b)) b $(b +(b)))
|
|
||||||
|
|
||||||
It's usually best to use the wide form if your gene fits on the
|
|
||||||
line, but this is obviously an aesthetic choice. If your gene
|
|
||||||
does not fit your margin (which should always be 80 columns),
|
|
||||||
you have no choice but to go tall. For reasons that should be
|
|
||||||
obvious, a tall gene can contain wide subgenes, but a wide gene
|
|
||||||
cannot contain tall subgenes - just as, in procedural languages,
|
|
||||||
a statement can contain expressions but not vice versa.
|
|
||||||
|
|
||||||
In the wide normal form, the rune is followed immediately (no
|
|
||||||
whitespace) by a left paren ("pel"), then the subgenes with a
|
|
||||||
single space between them, then a right paren ("per") as
|
|
||||||
terminator. If the rune was inside the parens rather than a
|
|
||||||
prefix, this would be the Lisp syntax.
|
|
||||||
|
|
||||||
In the tall normal form, any quantity of whitespace follows the
|
|
||||||
rune, and separates the subgenes from each other. Where is the
|
|
||||||
terminator? There is no terminator - in most cases.
|
|
||||||
|
|
||||||
Consider the `?:` rune, "wutcol," `%wtcl`. This is
|
|
||||||
|
|
||||||
[%wtcl p=gene q=gene r=gene]
|
|
||||||
|
|
||||||
Why should we need a terminator? We know `%wtcl`, whose
|
|
||||||
semantics are if-then-else, has three subgenes. When the parser
|
|
||||||
sees `?:` followed by space, it simply parses the next three
|
|
||||||
genes and fills the rune with them.
|
|
||||||
|
|
||||||
This only works in runes with fixed tuple structure, which
|
|
||||||
fortunately is most of them. A counterexample is `:*`, ie,
|
|
||||||
|
|
||||||
[%cltr p=(list gene)]
|
|
||||||
|
|
||||||
which is of variable length and needs a terminator. But we
|
|
||||||
have no dangling parens, but an attractive tall closure:
|
|
||||||
|
|
||||||
:* %foo
|
|
||||||
%bar
|
|
||||||
%baz
|
|
||||||
%moo
|
|
||||||
==
|
|
||||||
|
|
||||||
whose equivalent wide normal is
|
|
||||||
|
|
||||||
:*(%foo %bar %baz %moo)
|
|
||||||
|
|
||||||
which no one would ever write, preferring the custom
|
|
||||||
|
|
||||||
[%foo %bar %baz %moo]
|
|
||||||
|
|
||||||
This leaves only one question: indentation. Since space is not
|
|
||||||
significant (even linebreaks are irrelevant - the newline is just
|
|
||||||
another space), the use of whitespace in tall forms is purely a
|
|
||||||
matter of style. Style is very important, however!
|
|
||||||
|
|
||||||
The first law of Hoon indentation style is that all tall
|
|
||||||
indentation is in two-space increments. (Tabs are illegal. If
|
|
||||||
you pollute a Hoon file with ASCII 9, not only will it not parse,
|
|
||||||
but thugs in ski masks will kick down your door and shoot you.
|
|
||||||
You laugh! Try it!) Single spaces are for wide only.
|
|
||||||
|
|
||||||
The second law of Hoon indentation is that everything in the
|
|
||||||
kernel is good indentation style. Or at least if it's not, it
|
|
||||||
needs changed. The kernel shall be lapidary, noble, ideal and
|
|
||||||
above all suspicion - a Doric column, a Tlingit totem pole,
|
|
||||||
an Egyptian obelisk.
|
|
||||||
|
|
||||||
Tallness matters. The third law of Hoon indentation is that
|
|
||||||
large genes should flow _down_ and not _across_ - like the
|
|
||||||
decrement example above. The right margin is a precious resource
|
|
||||||
not to be wasted. It's this law, when properly applied, that
|
|
||||||
makes casual readers wonder if Hoon is a functional language at
|
|
||||||
all. It doesn't have a program counter, but it looks like it
|
|
||||||
does - at least when written right.
|
|
||||||
|
|
||||||
In list-structured runes, like the `:*` above, there is no choice
|
|
||||||
but to lose right margin. Fortunately, most runes are tuples,
|
|
||||||
and most have limited "fanout" - 1, 2, 3 or at most 4.
|
|
||||||
|
|
||||||
Both of our above examples - `|=` and `?:` - use "backstep"
|
|
||||||
indentation which takes advantage of this tuple structure. For
|
|
||||||
instance, `|=` has two subgenes, `p` and `q.` We put `p` on the
|
|
||||||
same line as `|=`, set off by two spaces, losing 4 characters of
|
|
||||||
margin. We put `q` _directly below_, losing no margin at all.
|
|
||||||
|
|
||||||
It so happens that in almost every actual case of `|=`, `p` (the
|
|
||||||
function's argument) is relatively light, whereas `q` (the
|
|
||||||
function's body) will be much heavier. Thus, with this pattern of
|
|
||||||
indentation, we lose no margin and our code flows _down_.
|
|
||||||
|
|
||||||
We see this even more in `?:`, where the conditional test (which
|
|
||||||
is much less likely to be heavy) is first and farthest right,
|
|
||||||
followed by the "then" case indented two spaces, followed by the
|
|
||||||
"else" case at the same indent as the rune.
|
|
||||||
|
|
||||||
Suppose your "else" is relatively light, and your "then" is
|
|
||||||
heavy? You may prefer the `?.` rune, Hoon's "unless," which puts
|
|
||||||
the else before the then. Or not. And in both `?:` and `?.`,
|
|
||||||
the test (which of course can be arbitrarily heavy) is first.
|
|
||||||
It is not necessary for your code to _always_ flow down and not
|
|
||||||
across - just _mostly_.
|
|
||||||
|
|
||||||
The conventional principle which backstep indentation sacrifices,
|
|
||||||
of course, is the idea that absolute indentation depth should
|
|
||||||
correspond to tree depth, loop depth, or some other metric. Hoon
|
|
||||||
is so deeply nested that if tab depth matched tree depth, your
|
|
||||||
margins on anything interesting would be in the next cube to your
|
|
||||||
right. There is perhaps a case for indenting loops, but we don't
|
|
||||||
find we miss this cue at all.
|
|
||||||
|
|
||||||
The paucity of terminators also eliminates a lot of redundancy in
|
|
||||||
the parser, which can result in relatively exciting syntax
|
|
||||||
errors. Our experience is that this is seldom a big problem,
|
|
||||||
because there are terminated tall forms and the cascade stops
|
|
||||||
with them. It is often a small problem, however.
|
|
@ -1,382 +0,0 @@
|
|||||||
4: Hoon - Semantics
|
|
||||||
=========
|
|
||||||
|
|
||||||
Now let's actually learn to program in Hoon, which really isn't
|
|
||||||
that hard. Unfortunately, we have to start by learning enough
|
|
||||||
Arvo to not be utterly defenseless.
|
|
||||||
|
|
||||||
Start vere, the Arvo VM, which you created in chapter 2:
|
|
||||||
|
|
||||||
vere $mypier
|
|
||||||
|
|
||||||
You'll see a prompt of the form
|
|
||||||
|
|
||||||
~tasfyn-partyv/try=>
|
|
||||||
|
|
||||||
What does this prompt mean?
|
|
||||||
|
|
||||||
`~tasfyn-partyv` (or whatever) is your ship. `try` is your desk;
|
|
||||||
that is, the project you're working on. There is no spur, or
|
|
||||||
further current directory. The current case (revision) is the
|
|
||||||
null case, `=`, ie, you are working in the present tense.
|
|
||||||
|
|
||||||
This is the `behn` prompt. Arvo is talking to you through the
|
|
||||||
console vane `dill` from the processing vane `behn`. You can
|
|
||||||
think of `behn` as a shell, a REPL or both. As either it is very
|
|
||||||
barebones and can perform only a few basic tasks. You can see
|
|
||||||
its source in Unix in `$URBIT_HOME/tasfyn-partyv/arvo/behn.hoon`,
|
|
||||||
or print it in Urbit with `:cat /===/arvo/behn/hoon`.
|
|
||||||
|
|
||||||
As this correspondence implies, basically the way you get
|
|
||||||
any kind of bulk information from Unix, into Urbit, is to write
|
|
||||||
it into a Unix file whose path Urbit knows how to sync.
|
|
||||||
|
|
||||||
While learning Hoon we are not going to run any commands in
|
|
||||||
`behn` or use its OS-level capabilities at all. We are just
|
|
||||||
going to use it as a REPL. But we are still using it and should
|
|
||||||
have no reason to fear it.
|
|
||||||
|
|
||||||
`behn` uses emacs control keys for history and editing.
|
|
||||||
^ABEFKNPY all basically kind of work. (Since `dill` works at the
|
|
||||||
character level, not the line level, this is not `readline`.)
|
|
||||||
There are also special Arvo controls: ^CDWX.
|
|
||||||
|
|
||||||
^C will stop any working task (and cancel the event that caused
|
|
||||||
it). If Urbit is waiting rather than working, ^C will kill the
|
|
||||||
waiting task. ^X will cycle between waiting tasks and the
|
|
||||||
command line (which can always create a new task). Think of it
|
|
||||||
as switching windows, in a command-line sense. ^W switches
|
|
||||||
ships, on a multi-ship pier. ^D exits back to Unix.
|
|
||||||
|
|
||||||
If there is an error you may see a stack trace. The bottom of
|
|
||||||
the trace is the thing that caused the problem.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now let's see how to use Hoon
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now, let's actually look at Hoon. Really, try not to recoil
|
|
||||||
in horror. It's actually not anything like line noise.
|
|
||||||
|
|
||||||
Open the Hoon kernel - `urb/les/arvo/hoon.hoon`. Let's look at
|
|
||||||
the full, official decrement function (line 549):
|
|
||||||
|
|
||||||
++ dec
|
|
||||||
~/ %dec
|
|
||||||
|= a=@
|
|
||||||
^- @
|
|
||||||
?< =(0 a)
|
|
||||||
=+ b=@
|
|
||||||
|-
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
Whaa?
|
|
||||||
|
|
||||||
Any attempt to understand this in terms of any language you
|
|
||||||
already know would clearly be mistaken. In both syntax and
|
|
||||||
semantics, learning to program in Hoon is learning to program all
|
|
||||||
over again. When we say from scratch, we mean from scratch!
|
|
||||||
|
|
||||||
It's actually worse than that - learning Hoon is learning to
|
|
||||||
_read_ all over again. Hoon is a keyword-free language - any
|
|
||||||
alphanumeric text in the program is part of the program. Where
|
|
||||||
other languages have reserved words, Hoon has squiggles.
|
|
||||||
|
|
||||||
We use so many of these ASCII glyphs that we like to be able
|
|
||||||
to read them out loud. A language is meant to be _said_. The
|
|
||||||
squiggles have conventional names, sort of, some of them, some of
|
|
||||||
them easy to say, others not so much. So we've renamed them:
|
|
||||||
|
|
||||||
ace space gal < per )
|
|
||||||
bar | gar > sel [
|
|
||||||
bas \ hax # sem ;
|
|
||||||
buc $ hep - ser ]
|
|
||||||
cab _ kel { sig ~
|
|
||||||
cen % ker } soq '
|
|
||||||
col : ket ^ tar *
|
|
||||||
com , lus + tec `
|
|
||||||
doq " pam & tis =
|
|
||||||
dot . pat @ wut ?
|
|
||||||
fas / pel ( zap !
|
|
||||||
|
|
||||||
You just have to memorize these names. Sorry.
|
|
||||||
|
|
||||||
But is this at least enough symbols? Alas, nowhere near.
|
|
||||||
ASCII's glyph supply is not the greatest, but we can make all the
|
|
||||||
squiggles we need by forming digraphs, or _runes_.
|
|
||||||
|
|
||||||
To pronounce a rune, concatenate the glyph names, stressing the
|
|
||||||
first syllable and softening the second vowel into a "schwa."
|
|
||||||
Hence, to say `~.`, say "sigdot." To say `|=`, say "bartis."
|
|
||||||
Which has an inevitable tendency to turn into "barts" - a sin
|
|
||||||
to be encouraged. In any language actually spoken by actual
|
|
||||||
humans, laziness soon rounds off any rough edges.
|
|
||||||
|
|
||||||
So if we had to read the above decrement, omitting the spaces
|
|
||||||
(which only a real purist would pronounce), we'd say: "luslus dec
|
|
||||||
sigfas cen dec bartis a tis pat sigbar soq dec soq ketcab pat wutgal
|
|
||||||
tis pel zero a per tislus b tis pat barhep wutcol tis pel a lus pel
|
|
||||||
b per per b buc pel b lus pel b per per." The authorities would
|
|
||||||
then arrive, and drag us out in a big net. Definitely don't do
|
|
||||||
this at the airport.
|
|
||||||
|
|
||||||
Geeks being solitary by nature, opportunities for reading code
|
|
||||||
aloud are limited. But studies by actual scientists have shown
|
|
||||||
that even when we read silently, we activate the motor cortex
|
|
||||||
that controls our vocal cords. Even if we never speak these
|
|
||||||
squiggles, they're easier to _think_ if bound to simple sounds.
|
|
||||||
|
|
||||||
(And don't worry if you can't get yourself to say "lus" instead
|
|
||||||
of "plus" for `+`, or "tar" instead of "star" for `*` - I have
|
|
||||||
this problem myself. It's much easier to replace "underscore"
|
|
||||||
with "cab" or "ampersand" with "pam.")
|
|
||||||
|
|
||||||
Hoon has almost 90 digraphic runes. They are easier to organize
|
|
||||||
in your head, though, because the choice of glyph is not random.
|
|
||||||
The second glyph in a rune means little or nothing, but the first
|
|
||||||
defines a rough semantic category. These categories are:
|
|
||||||
|
|
||||||
| bar gates (ie, functions) (ie, one-method cores)
|
|
||||||
? wut conditionals, booleans, tests
|
|
||||||
: col tuples
|
|
||||||
. dot nock operators
|
|
||||||
$ buc factory macros (ie, type definitions)
|
|
||||||
^ ket type conversions
|
|
||||||
= tis compositions
|
|
||||||
% cen invocations
|
|
||||||
& pam gears (ie, objects) (ie, multi-method cores)
|
|
||||||
~ sig hints
|
|
||||||
; sem miscellaneous macros
|
|
||||||
! zap special operations
|
|
||||||
|
|
||||||
Each rune has _[not]_ its own doc file in the `rune/190` directory. The
|
|
||||||
name of the file is the name of the rune, minus the vowels.
|
|
||||||
Thus, `|=` or "bartis" is _[not]_ defined in `rune/190/brts.txt`.
|
|
||||||
|
|
||||||
Opening this file, we see:
|
|
||||||
|
|
||||||
%brts |= "bartis"
|
|
||||||
|
|
||||||
define:
|
|
||||||
[%brts p=gene q=gene]
|
|
||||||
|
|
||||||
expand:
|
|
||||||
[%brts *] [%brcb p.gen (~(put by *(map term foot)) %% [%ash q.gen])]
|
|
||||||
|
|
||||||
There should be some actual discussion, but there isn't. Still,
|
|
||||||
`brts.txt` is quite complete as a definition of `|=`. How? We
|
|
||||||
need to step back a little.
|
|
||||||
|
|
||||||
When the Hoon parser parses a source file, it generates a noun
|
|
||||||
called a `gene`. If you know what an AST is, a gene is an AST node.
|
|
||||||
If you don't, don't worry about it.
|
|
||||||
|
|
||||||
Search the current kernel for `++ gene` - note double space.
|
|
||||||
This code is both the type declaration for type `gene`, and
|
|
||||||
a function that maps an untyped noun to a typed gene. In it
|
|
||||||
you'll see the above definition,
|
|
||||||
|
|
||||||
[%brts p=gene q=gene]
|
|
||||||
|
|
||||||
Ie, one kind of gene is a triple whose head is the constant
|
|
||||||
`%brts`, and whose tail is a pair of genes, `p` and `q`.
|
|
||||||
|
|
||||||
We also see the semantics of this rune: it expands to
|
|
||||||
|
|
||||||
[%brcb p.gen (~(put by *(map term foot)) %% [%ash q.gen])]
|
|
||||||
|
|
||||||
ie, `|=` is a built-in macro. But back to syntax.
|
|
||||||
|
|
||||||
What is `%brts`? The atom also known as `1937011298` or
|
|
||||||
`0x73747262`. Simply a string mapped to an unsigned integer, LSB
|
|
||||||
first. It's easy to see why the vowels got lost - `%bartis` is
|
|
||||||
`126896762413410` or `0x736974726162`. On a 32-bit CPU with
|
|
||||||
31-bit direct atoms, `%brts` is direct and `%bartis` indirect
|
|
||||||
(not that the programmer can tell the difference). But you still
|
|
||||||
say "bartis."
|
|
||||||
|
|
||||||
For instance, in the decrement above, we have
|
|
||||||
|
|
||||||
|= a=@
|
|
||||||
~| 'dec'
|
|
||||||
^- @
|
|
||||||
?< =(0 a)
|
|
||||||
=+ b=@
|
|
||||||
|-
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
In this `%brts`, `p` is
|
|
||||||
|
|
||||||
a=@
|
|
||||||
|
|
||||||
and `q` is
|
|
||||||
|
|
||||||
~| 'dec'
|
|
||||||
^- @
|
|
||||||
?< =(0 a)
|
|
||||||
=+ b=@
|
|
||||||
|-
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
We are starting to see the principles of Hoon syntax. Let's make
|
|
||||||
them clear.
|
|
||||||
|
|
||||||
First, for any rune, the Hoon parser has two kinds of syntax:
|
|
||||||
normal and custom. Most runes, such as `|=`, have only normal
|
|
||||||
syntax without custom syntax. Almost all runes with custom
|
|
||||||
syntax also have normal. Custom can mean anything; normal
|
|
||||||
is rigid and uniform.
|
|
||||||
|
|
||||||
All programming languages, but especially functional ones, face
|
|
||||||
two difficult syntactic problems. One is controlling the large
|
|
||||||
numbers of terminators that appear in any deeply nested tree
|
|
||||||
structure - Lisp is infamous for its piles of right parens.
|
|
||||||
These are not a serious usability problem, except inasmuch as you
|
|
||||||
consider ugly a usability problem (which I do). Two, a more
|
|
||||||
serious concern, is keeping complex routines from flowing off the
|
|
||||||
right margin as tab depth increases.
|
|
||||||
|
|
||||||
A glance at the more complex organs of the Hoon kernel reveals
|
|
||||||
that Hoon is relatively untroubled by either of these woes. But
|
|
||||||
why? One dubious panacea for the terminator problem is the use
|
|
||||||
of significant whitespace. Whitespace in Hoon is not
|
|
||||||
significant. (To be exact, the presence or absence of whitespace
|
|
||||||
matters, but the quantity never does.)
|
|
||||||
|
|
||||||
The answer is that the normal syntax for every rune has two
|
|
||||||
forms: "wide" and "tall." As a functional language, Hoon does
|
|
||||||
not distinguish between statements and expressions, but normal
|
|
||||||
wide syntax is expression-like and tall is statement-like.
|
|
||||||
|
|
||||||
For instance, in our example above,
|
|
||||||
|
|
||||||
?: =(a +(b))
|
|
||||||
b
|
|
||||||
$(b +(b))
|
|
||||||
|
|
||||||
is a tall normal form. The equivalent wide form is
|
|
||||||
|
|
||||||
?:(=(a +(b)) b $(b +(b)))
|
|
||||||
|
|
||||||
It's usually best to use the wide form if your gene fits on the
|
|
||||||
line, but this is obviously an aesthetic choice. If your gene
|
|
||||||
does not fit your margin (which should always be 80 columns),
|
|
||||||
you have no choice but to go tall. For reasons that should be
|
|
||||||
obvious, a tall gene can contain wide subgenes, but a wide gene
|
|
||||||
cannot contain tall subgenes - just as, in procedural languages,
|
|
||||||
a statement can contain expressions but not vice versa.
|
|
||||||
|
|
||||||
In the wide normal form, the rune is followed immediately (no
|
|
||||||
whitespace) by a left paren ("pel"), then the subgenes with a
|
|
||||||
single space between them, then a right paren ("per") as
|
|
||||||
terminator. If the rune was inside the parens rather than a
|
|
||||||
prefix, this would be the Lisp syntax.
|
|
||||||
|
|
||||||
In the tall normal form, any quantity of whitespace follows the
|
|
||||||
rune, and separates the subgenes from each other. Where is the
|
|
||||||
terminator? There is no terminator - in most cases.
|
|
||||||
|
|
||||||
Consider the `?:` rune, "wutcol," `%wtcl`. This is
|
|
||||||
|
|
||||||
[%wtcl p=gene q=gene r=gene]
|
|
||||||
|
|
||||||
Why should we need a terminator? We know `%wtcl`, whose
|
|
||||||
semantics are if-then-else, has three subgenes. When the parser
|
|
||||||
sees `?:` followed by space, it simply parses the next three
|
|
||||||
genes and fills the rune with them.
|
|
||||||
|
|
||||||
This only works in runes with fixed tuple structure, which
|
|
||||||
fortunately is most of them. A counterexample is `:*`, ie,
|
|
||||||
|
|
||||||
[%cltr p=(list gene)]
|
|
||||||
|
|
||||||
which is of variable length and needs a terminator. But we
|
|
||||||
have no dangling parens, but an attractive tall closure:
|
|
||||||
|
|
||||||
:* %foo
|
|
||||||
%bar
|
|
||||||
%baz
|
|
||||||
%moo
|
|
||||||
==
|
|
||||||
|
|
||||||
whose equivalent wide normal is
|
|
||||||
|
|
||||||
:*(%foo %bar %baz %moo)
|
|
||||||
|
|
||||||
which no one would ever write, preferring the custom
|
|
||||||
|
|
||||||
[%foo %bar %baz %moo]
|
|
||||||
|
|
||||||
This leaves only one question: indentation. Since space is not
|
|
||||||
significant (even linebreaks are irrelevant - the newline is just
|
|
||||||
another space), the use of whitespace in tall forms is purely a
|
|
||||||
matter of style. Style is very important, however!
|
|
||||||
|
|
||||||
The first law of Hoon indentation style is that all tall
|
|
||||||
indentation is in two-space increments. (Tabs are illegal. If
|
|
||||||
you pollute a Hoon file with ASCII 9, not only will it not parse,
|
|
||||||
but thugs in ski masks will kick down your door and shoot you.
|
|
||||||
You laugh! Try it!) Single spaces are for wide only.
|
|
||||||
|
|
||||||
The second law of Hoon indentation is that everything in the
|
|
||||||
kernel is good indentation style. Or at least if it's not, it
|
|
||||||
needs changed. The kernel shall be lapidary, noble, ideal and
|
|
||||||
above all suspicion - a Doric column, a Tlingit totem pole,
|
|
||||||
an Egyptian obelisk.
|
|
||||||
|
|
||||||
Tallness matters. The third law of Hoon indentation is that
|
|
||||||
large genes should flow _down_ and not _across_ - like the
|
|
||||||
decrement example above. The right margin is a precious resource
|
|
||||||
not to be wasted. It's this law, when properly applied, that
|
|
||||||
makes casual readers wonder if Hoon is a functional language at
|
|
||||||
all. It doesn't have a program counter, but it looks like it
|
|
||||||
does - at least when written right.
|
|
||||||
|
|
||||||
In list-structured runes, like the `:*` above, there is no choice
|
|
||||||
but to lose right margin. Fortunately, most runes are tuples,
|
|
||||||
and most have limited "fanout" - 1, 2, 3 or at most 4.
|
|
||||||
|
|
||||||
Both of our above examples - `|=` and `?:` - use "backstep"
|
|
||||||
indentation which takes advantage of this tuple structure. For
|
|
||||||
instance, `|=` has two subgenes, `p` and `q.` We put `p` on the
|
|
||||||
same line as `|=`, set off by two spaces, losing 4 characters of
|
|
||||||
margin. We put `q` _directly below_, losing no margin at all.
|
|
||||||
|
|
||||||
It so happens that in almost every actual case of `|=`, `p` (the
|
|
||||||
function's argument) is relatively light, whereas `q` (the
|
|
||||||
function's body) will be much heavier. Thus, with this pattern of
|
|
||||||
indentation, we lose no margin and our code flows _down_.
|
|
||||||
|
|
||||||
We see this even more in `?:`, where the conditional test (which
|
|
||||||
is much less likely to be heavy) is first and farthest right,
|
|
||||||
followed by the "then" case indented two spaces, followed by the
|
|
||||||
"else" case at the same indent as the rune.
|
|
||||||
|
|
||||||
Suppose your "else" is relatively light, and your "then" is
|
|
||||||
heavy? You may prefer the `?.` rune, Hoon's "unless," which puts
|
|
||||||
the else before the then. Or not. And in both `?:` and `?.`,
|
|
||||||
the test (which of course can be arbitrarily heavy) is first.
|
|
||||||
It is not necessary for your code to _always_ flow down and not
|
|
||||||
across - just _mostly_.
|
|
||||||
|
|
||||||
The conventional principle which backstep indentation sacrifices,
|
|
||||||
of course, is the idea that absolute indentation depth should
|
|
||||||
correspond to tree depth, loop depth, or some other metric. Hoon
|
|
||||||
is so deeply nested that if tab depth matched tree depth, your
|
|
||||||
margins on anything interesting would be in the next cube to your
|
|
||||||
right. There is perhaps a case for indenting loops, but we don't
|
|
||||||
find we miss this cue at all.
|
|
||||||
|
|
||||||
The paucity of terminators also eliminates a lot of redundancy in
|
|
||||||
the parser, which can result in relatively exciting syntax
|
|
||||||
errors. Our experience is that this is seldom a big problem,
|
|
||||||
because there are terminated tall forms and the cascade stops
|
|
||||||
with them. It is often a small problem, however.
|
|
@ -1,8 +0,0 @@
|
|||||||
|= %brbn "barbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%brbn p=*gene q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%pmbn p.gen [[%% [& q.gen]] ~ ~]]
|
|
||||||
--
|
|
@ -1,14 +0,0 @@
|
|||||||
|? %brcs "barcas"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%brcs p=*(list gene)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
?~ p.gen
|
|
||||||
[%zpzp ~]
|
|
||||||
[%brbn [%ktdp [%tmbn %noun] i.p.gen] [%cstr [~ 4] p.gen]]
|
|
||||||
|
|
||||||
meta:
|
|
||||||
?~ p.gen
|
|
||||||
!$(!!)
|
|
||||||
!$(|=(^-(* {i.p.gen}) ?*(.4 {p.gen})))
|
|
@ -1,10 +0,0 @@
|
|||||||
|- %brdp "bardap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%brdp p=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%bnld [%brdg p.gen] %%]
|
|
||||||
|
|
||||||
meta:
|
|
||||||
=>(|:({p.gen}) $)
|
|
@ -1,7 +0,0 @@
|
|||||||
|: %brdg "bardig"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%brdg p=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%pmdg [[%% [& p.gen]] ~ ~]]
|
|
@ -1,7 +0,0 @@
|
|||||||
|. %brdt "bardot"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%brdt p=*gene q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%pmdt p.gen [[%% [& q.gen]] ~ ~]]
|
|
@ -1,8 +0,0 @@
|
|||||||
|* %brtr "bartar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%brtr p=*gene q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%pmtr p.gen [[%% [& q.gen]] ~ ~]]
|
|
||||||
--
|
|
@ -1,10 +0,0 @@
|
|||||||
=- %bndp "bondap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%bndp p=*gene q=*gene]
|
|
||||||
|
|
||||||
open:
|
|
||||||
[%bnpd q.gen p.gen]
|
|
||||||
|
|
||||||
soft:
|
|
||||||
=+({q.gen} {p.gen})
|
|
@ -1,10 +0,0 @@
|
|||||||
=< %bndl "bondel"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%bndl p=*gene q=*gene]
|
|
||||||
|
|
||||||
open:
|
|
||||||
[%bnld q.gen p.gen]
|
|
||||||
|
|
||||||
soft:
|
|
||||||
=>({q.gen} {p.gen})
|
|
@ -1,4 +0,0 @@
|
|||||||
=> %bnld "bonled"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%bnld p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
=+ %bnpd "bonpad"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%bnpd p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?| %csbr "casbar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csbr p=*(list gene)]
|
|
@ -1,4 +0,0 @@
|
|||||||
?= %csbn "casbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csbn p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?- %csdp "casdap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csdp p=*gene q=*(list &[p=*gene q=*gene])] :: ?- casdap
|
|
@ -1,4 +0,0 @@
|
|||||||
?: %csdg "casdeg"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csdg p=*gene q=*gene r=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?< %csdl "casdel"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csdl p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?. %csdt "casdot"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csdt p=*gene q=*gene r=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?> %csld "casled"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%csld p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?& %cspm "caspam"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%cspm p=*(list gene)]
|
|
@ -1,4 +0,0 @@
|
|||||||
?~ %cssg "cassig"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%cssg p=*gene q=*gene r=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
?* %cstr "castar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%cstr p=*gene q=*(list gene)]
|
|
@ -1,4 +0,0 @@
|
|||||||
?! %cszp "caszap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%cszp p=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
:- %dgdp "degdap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dgdp p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
:^ %dgkt "degket"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dgkt p=*gene q=*gene r=*gene s=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
:+ %dgpd "degpad"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dgpd p=*gene q=*gene r=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
:` %dgsg "degsig"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dgsg p=*(list gene)]
|
|
@ -1,4 +0,0 @@
|
|||||||
:* %dgtr "degtar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dgtr p=*(list gene)]
|
|
@ -1,4 +0,0 @@
|
|||||||
.= %dtbn "dotbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dtbn p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
.^ %dtkt "dotket"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dtkt p=*gene]
|
|
@ -1 +0,0 @@
|
|||||||
.~ %dtsg "dotsig"
|
|
@ -1,4 +0,0 @@
|
|||||||
.* %dttr "dottar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%dttr p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
^= %ktbn "ketbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%ktbn p=*term q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
^- %ktdp "ketdap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%ktdp p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
^+ %ktpd "ketpad"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%ktpd p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
^~ %ktsg "ketsig"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%ktsg p=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
%| %mtbr "mitbar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtbr p=*twig q=*gene r=*(list &[p=*gene q=*gene])]
|
|
@ -1,4 +0,0 @@
|
|||||||
%= %mtbn "mitbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtbn p=*twig q=*(list &[p=*gene q=*gene])]
|
|
@ -1,4 +0,0 @@
|
|||||||
%- "mitdap" %mtdp
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtdp p=*gene q=*(list gene)]
|
|
@ -1,4 +0,0 @@
|
|||||||
%: %mtdg "mitdeg"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtdt p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
%. %mtdt "mitdot"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtdt p=*gene q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
%^ %mtkt "mitket"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtkt p=*gene q=*gene r=*gene s=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
%+ %mtpd "mitpad"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtpd p=*gene q=*gene r=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
%~ %mtsg "mitsig"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mtsg p=*twig q=*gene r=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
%* %mttr "mittar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%mttr p=*gene]
|
|
@ -1,7 +0,0 @@
|
|||||||
&= %pmbn "pambon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%pmtr p=*gene q=*(map term foot)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%bnpd p.gen [%pmzp %gold q.gen]]
|
|
@ -1,7 +0,0 @@
|
|||||||
&- %pmdp "pamdap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%pmdp p=*gene q=*(map term foot)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%bnld [%pmdg q.gen] p.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
&: %pmdg "pamdeg"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%pmdg p=*(map term foot)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%pmzp %lead p.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
&. %pmdt "pamdot"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%pmdt p=*gene q=*(map term foot)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%bnpd p.gen [%pmzp %zinc q.gen]]
|
|
@ -1,7 +0,0 @@
|
|||||||
&* %pmtr "pamtar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%pmtr p=*gene q=*(map term foot)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%bnpd p.gen [%pmzp %wood q.gen]]
|
|
@ -1,4 +0,0 @@
|
|||||||
&= %pmzp "pamzap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%pmbn p=*gene q=*(map term foot)]
|
|
@ -1,7 +0,0 @@
|
|||||||
~| %sgbr "sigbar"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgbr p=*gene q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld [%bean p.gen] q.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~= %sgbn "sigbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgbn p=*gene q=*gene] :: ~= sigbon
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld [%germ p.gen] q.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~- %sgdp "sigdap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgdp p=@ q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld [%sole %dtsg p.gen] q.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~^ %sgdx "sigdax"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgdx p=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld %ping p.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~: %sgdg "sigdeg"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgdg p=[p=@ q=@] q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld [%bank %dtsg p.gen] q.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~< %sgdl "sigdel"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgdl p=*term q=*gene r=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%bndl [%sgld p.gen [~ 1]] q.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~^ %sgkt "sigket"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgkt p=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld %keep p.gen]
|
|
@ -1,4 +0,0 @@
|
|||||||
~> %sgld "sigled"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgld p=*term q=*gene r=*gene] :: ~> sigled
|
|
@ -1,17 +0,0 @@
|
|||||||
~% %sgmt "sigmit"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgmt p=*seal q=*gene r=*(list &[p=*term q=*gene]) s=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
:+ %sgdl
|
|
||||||
:- %mine
|
|
||||||
:+ [%dtsg p.gen]
|
|
||||||
[%zpbn q.gen]
|
|
||||||
:- %dgsg
|
|
||||||
=+ nob=`*(list gene)`~
|
|
||||||
|- ^- nob
|
|
||||||
?~ r.gen
|
|
||||||
nob
|
|
||||||
[[[%dtsg p.i.r.gen] [%zpbn q.i.r.gen]] $(r.gen t.r.gen)]
|
|
||||||
q.gen
|
|
@ -1,7 +0,0 @@
|
|||||||
~+ %sgpd "sigpad"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgpd p=@ q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld [%memo %dtsg p.gen] q.gen]
|
|
@ -1,7 +0,0 @@
|
|||||||
~& %sgpm "sigpam"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%sgpm p=*gene q=*gene]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
[%sgld [%loaf p.gen] q.gen]
|
|
@ -1,4 +0,0 @@
|
|||||||
;= %tmbn "tambon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%tmbn p=*<%atom %noun %cell %flag %null>]
|
|
@ -1,4 +0,0 @@
|
|||||||
;- %tmdp "tamdap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%tmdp p=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
;+ %tmpd "tampad"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%tmpd p=*gene q=*gene]
|
|
@ -1,110 +0,0 @@
|
|||||||
|~ %tmsg "tamsig"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%tmsg p=*gene q=*(list gene)]
|
|
||||||
|
|
||||||
ap open:
|
|
||||||
|-
|
|
||||||
?- q.gen
|
|
||||||
~ !!
|
|
||||||
[* ~] i.q.gen
|
|
||||||
^
|
|
||||||
:+ %bnpd
|
|
||||||
$(q.gen t.q.gen)
|
|
||||||
:+ %bnpd
|
|
||||||
[%bnld [~ 3] i.q.gen]
|
|
||||||
:+ %brbn
|
|
||||||
[~ 8]
|
|
||||||
:^ %mtpd
|
|
||||||
[%bnld [~ 23] p.gen]
|
|
||||||
[%mtdp [~ 10] [~ 4] ~]
|
|
||||||
[%mtbn [[~ 22] ~] [[[~ 4] [~ 4]] ~]]
|
|
||||||
--
|
|
||||||
|
|
||||||
ap open (meta):
|
|
||||||
|-
|
|
||||||
?- q.gen
|
|
||||||
~ !!
|
|
||||||
[* ~] i.q.gen
|
|
||||||
^
|
|
||||||
!$
|
|
||||||
:: 1 == original subject
|
|
||||||
::
|
|
||||||
=+ {$(q.gen t.q.gen)}
|
|
||||||
::
|
|
||||||
:: 2 == {$(q.gen t.q.gen)}
|
|
||||||
:: 3 == original subject
|
|
||||||
::
|
|
||||||
=+ =>(.3 {i.q.gen})
|
|
||||||
::
|
|
||||||
:: 2 == =>(.2 {i.q.gen})
|
|
||||||
:: 3 == previous subject
|
|
||||||
:: 6 == {$(q.gen t.q.gen)}
|
|
||||||
:: 7 == original subject
|
|
||||||
::
|
|
||||||
|= .8
|
|
||||||
::
|
|
||||||
:: 4 == sample
|
|
||||||
:: 5 == context
|
|
||||||
:: 10 == =>(.2 {i.q.gen})
|
|
||||||
:: 22 == {$(q.gen t.q.gen)}
|
|
||||||
:: 23 == original subject
|
|
||||||
:: 3 == formula
|
|
||||||
::
|
|
||||||
%+ =>(.23 {p.gen})
|
|
||||||
%-(.10 .4)
|
|
||||||
%=(.22 .4 .4)
|
|
||||||
--
|
|
||||||
|
|
||||||
ap open (new):
|
|
||||||
|-
|
|
||||||
?- q.gen
|
|
||||||
~ !!
|
|
||||||
[* ~] i.q.gen
|
|
||||||
^
|
|
||||||
:+ %bnpd
|
|
||||||
$(q.gen t.q.gen)
|
|
||||||
:+ %bnpd
|
|
||||||
[%bnld [~ 2] i.q.gen]
|
|
||||||
:+ %brbn
|
|
||||||
[~ 13]
|
|
||||||
:^ %mtpd
|
|
||||||
[%bnld [~ 16] p.gen]
|
|
||||||
[%mtdp [~ 9] [~ 5] ~]
|
|
||||||
[%mtbn [[~ 17] ~] [[[~ 5] [~ 5]] ~]]
|
|
||||||
--
|
|
||||||
|
|
||||||
ap open (meta, new):
|
|
||||||
|-
|
|
||||||
?- q.gen
|
|
||||||
~ !!
|
|
||||||
[* ~] i.q.gen
|
|
||||||
^
|
|
||||||
!$
|
|
||||||
:: 1 == original subject
|
|
||||||
::
|
|
||||||
=+ {$(q.gen t.q.gen)}
|
|
||||||
::
|
|
||||||
:: 2 == original subject
|
|
||||||
:: 3 == {$(q.gen t.q.gen)}
|
|
||||||
::
|
|
||||||
=+ =>(.2 {i.q.gen})
|
|
||||||
::
|
|
||||||
:: 2 == previous subject
|
|
||||||
:: 4 == original subject
|
|
||||||
:: 5 == {$(q.gen t.q.gen)}
|
|
||||||
:: 3 == =>(.2 {i.q.gen})
|
|
||||||
::
|
|
||||||
|= .13
|
|
||||||
::
|
|
||||||
:: 4 == context
|
|
||||||
:: 16 == original subject
|
|
||||||
:: 17 == {$(q.gen t.q.gen)}
|
|
||||||
:: 9 == =>(.2 {i.q.gen})
|
|
||||||
:: 5 == sample (.13)
|
|
||||||
:: 3 == formula
|
|
||||||
::
|
|
||||||
%+ =>(.16 {p.gen})
|
|
||||||
%-(.9 .5)
|
|
||||||
%=(.17 .5 .5)
|
|
||||||
--
|
|
@ -1,4 +0,0 @@
|
|||||||
!= %zpbn "zapbon"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%zpbn p=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
!_ %zpcb "zapcab"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%zpcb p=*spot q=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
!# %zpdx "zapdax"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%zpdx p=*gene]
|
|
@ -1,4 +0,0 @@
|
|||||||
!! %zpzp "zapzap"
|
|
||||||
|
|
||||||
gene:
|
|
||||||
[%zpzp ~]
|
|
41
doc/sbox.txt
41
doc/sbox.txt
@ -1,41 +0,0 @@
|
|||||||
:: an invertible sbox for any conceivable use.
|
|
||||||
|
|
||||||
|%
|
|
||||||
++ ice :: =(a (fry (ice a)))
|
|
||||||
|= a=@
|
|
||||||
=+ ^= b
|
|
||||||
0x5aa2.c1dd.32d2.e2e4.5689.c39d.9584.dca4.3bda.9b47.a985.
|
|
||||||
4f57.d313.77fb.1b6a.bafe.040b.43d1.8262.4db7.9926.90f7.
|
|
||||||
bff4.7c09.c6df.ab87.3138.be29.eba5.1f1d.7a01.5e07.6769.
|
|
||||||
003d.535d.518a.f97d.dbfd.1002.72b0.aff0.28f2.36b9.f122.
|
|
||||||
b5e6.6ba0.3ac8.59f5.c0f3.65cb.976d.88d7.5ba7.3593.768c.
|
|
||||||
7fa8.604a.98c5.de52.705f.0a55.4550.edc4.ae4e.647e.ce4b.
|
|
||||||
cdb3.b1a1.e080.333c.96f8.c227.b2d5.e158.9cfa.4686.c7a6.
|
|
||||||
e78f.1a74.4873.f616.0f66.63e8.6e54.1c49.062b.9271.9f78.
|
|
||||||
05aa.ff12.e9b6.cfec.6fbd.1511.2523.41bb.14cc.c994.42d8.
|
|
||||||
4c39.6c30.3e2a.acb8.242c.2f8d.83b4.19bc.ee81.3721.449a.
|
|
||||||
757b.3fef.03d0.8bd4.a39e.0d8e.17d6.0efc.d91e.0861.ad18.
|
|
||||||
ca91.e568.0c79.2e40.5cea.2034.2de3
|
|
||||||
=+ c=(met 3 a)
|
|
||||||
?> (lte c 1)
|
|
||||||
(cut 3 [a 1] b)
|
|
||||||
::
|
|
||||||
++ fry
|
|
||||||
|= a=@
|
|
||||||
=+ ^= b
|
|
||||||
0x4de0.b414.e46a.b772.d45f.a0d2.9eac.a9ae.2029.8348.c704.
|
|
||||||
4b5a.65a6.0bf8.00f9.6d77.ce8b.fcf1.b5ee.133a.9816.6e1c.
|
|
||||||
e7fa.dc1e.497d.7b3e.9c0d.3da2.67cf.8c82.f571.fd9f.d3c9.
|
|
||||||
462a.40e1.aa32.d84a.a72c.7a6f.79b0.af81.0f33.cd4e.eb90.
|
|
||||||
9666.c6f0.1bfe.78a4.511a.f46b.ed24.d78d.9b73.f33c.9453.
|
|
||||||
0cd5.6418.2e92.1db8.f699.cc68.eaf2.2ddb.2876.917e.b6d1.
|
|
||||||
22c3.0850.e593.2362.60b1.5289.4759.9a37.a5e2.be0a.bf5c.
|
|
||||||
9d7f.5bda.108f.88c1.ba05.97ff.a16c.e8f7.8658.bb8a.b984.
|
|
||||||
e980.d939.7c8e.5661.ec69.8525.dd3b.4106.2135.bc74.efa3.
|
|
||||||
38ca.27ab.9502.75fb.cb36.2f07.0130.5434.c8ad.70d6.4331.
|
|
||||||
42a8.2603.c512.c457.e363.2b0e.175e.453f.e64c.44b3.5d15.
|
|
||||||
1909.de87.d011.c055.4fdf.1fb2.c2bd
|
|
||||||
=+ c=(met 3 a)
|
|
||||||
?> (lte c 1)
|
|
||||||
(cut 3 [a 1] b)
|
|
||||||
--
|
|
Loading…
Reference in New Issue
Block a user