Merge pull request #37 from ab9/remove_old_doc

Remove old documentation
This commit is contained in:
johncburnham 2013-09-29 11:01:12 -07:00
commit 80a8bda300
92 changed files with 0 additions and 11668 deletions

View File

@ -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?

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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 ~]
==

View File

@ -1,5 +0,0 @@
s/csdt/wtdt/g
s/cslc/wtts/g
s/csps/wtls/g
s/cssp/wtms/g
s/mtdt/cndt/g

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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},
}

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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?

View File

@ -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.

View File

@ -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.

View File

@ -1,8 +0,0 @@
|= %brbn "barbon"
gene:
[%brbn p=*gene q=*gene]
ap open:
[%pmbn p.gen [[%% [& q.gen]] ~ ~]]
--

View File

@ -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})))

View File

@ -1,10 +0,0 @@
|- %brdp "bardap"
gene:
[%brdp p=*gene]
ap open:
[%bnld [%brdg p.gen] %%]
meta:
=>(|:({p.gen}) $)

View File

@ -1,7 +0,0 @@
|: %brdg "bardig"
gene:
[%brdg p=*gene]
ap open:
[%pmdg [[%% [& p.gen]] ~ ~]]

View File

@ -1,7 +0,0 @@
|. %brdt "bardot"
gene:
[%brdt p=*gene q=*gene]
ap open:
[%pmdt p.gen [[%% [& q.gen]] ~ ~]]

View File

@ -1,8 +0,0 @@
|* %brtr "bartar"
gene:
[%brtr p=*gene q=*gene]
ap open:
[%pmtr p.gen [[%% [& q.gen]] ~ ~]]
--

View File

@ -1,10 +0,0 @@
=- %bndp "bondap"
gene:
[%bndp p=*gene q=*gene]
open:
[%bnpd q.gen p.gen]
soft:
=+({q.gen} {p.gen})

View File

@ -1,10 +0,0 @@
=< %bndl "bondel"
gene:
[%bndl p=*gene q=*gene]
open:
[%bnld q.gen p.gen]
soft:
=>({q.gen} {p.gen})

View File

@ -1,4 +0,0 @@
=> %bnld "bonled"
gene:
[%bnld p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
=+ %bnpd "bonpad"
gene:
[%bnpd p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
?| %csbr "casbar"
gene:
[%csbr p=*(list gene)]

View File

@ -1,4 +0,0 @@
?= %csbn "casbon"
gene:
[%csbn p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
?- %csdp "casdap"
gene:
[%csdp p=*gene q=*(list &[p=*gene q=*gene])] :: ?- casdap

View File

@ -1,4 +0,0 @@
?: %csdg "casdeg"
gene:
[%csdg p=*gene q=*gene r=*gene]

View File

@ -1,4 +0,0 @@
?< %csdl "casdel"
gene:
[%csdl p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
?. %csdt "casdot"
gene:
[%csdt p=*gene q=*gene r=*gene]

View File

@ -1,4 +0,0 @@
?> %csld "casled"
gene:
[%csld p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
?& %cspm "caspam"
gene:
[%cspm p=*(list gene)]

View File

@ -1,4 +0,0 @@
?~ %cssg "cassig"
gene:
[%cssg p=*gene q=*gene r=*gene]

View File

@ -1,4 +0,0 @@
?* %cstr "castar"
gene:
[%cstr p=*gene q=*(list gene)]

View File

@ -1,4 +0,0 @@
?! %cszp "caszap"
gene:
[%cszp p=*gene]

View File

@ -1,4 +0,0 @@
:- %dgdp "degdap"
gene:
[%dgdp p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
:^ %dgkt "degket"
gene:
[%dgkt p=*gene q=*gene r=*gene s=*gene]

View File

@ -1,4 +0,0 @@
:+ %dgpd "degpad"
gene:
[%dgpd p=*gene q=*gene r=*gene]

View File

@ -1,4 +0,0 @@
:` %dgsg "degsig"
gene:
[%dgsg p=*(list gene)]

View File

@ -1,4 +0,0 @@
:* %dgtr "degtar"
gene:
[%dgtr p=*(list gene)]

View File

@ -1,4 +0,0 @@
.= %dtbn "dotbon"
gene:
[%dtbn p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
.^ %dtkt "dotket"
gene:
[%dtkt p=*gene]

View File

@ -1 +0,0 @@
.~ %dtsg "dotsig"

View File

@ -1,4 +0,0 @@
.* %dttr "dottar"
gene:
[%dttr p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
^= %ktbn "ketbon"
gene:
[%ktbn p=*term q=*gene]

View File

@ -1,4 +0,0 @@
^- %ktdp "ketdap"
gene:
[%ktdp p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
^+ %ktpd "ketpad"
gene:
[%ktpd p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
^~ %ktsg "ketsig"
gene:
[%ktsg p=*gene]

View File

@ -1,4 +0,0 @@
%| %mtbr "mitbar"
gene:
[%mtbr p=*twig q=*gene r=*(list &[p=*gene q=*gene])]

View File

@ -1,4 +0,0 @@
%= %mtbn "mitbon"
gene:
[%mtbn p=*twig q=*(list &[p=*gene q=*gene])]

View File

@ -1,4 +0,0 @@
%- "mitdap" %mtdp
gene:
[%mtdp p=*gene q=*(list gene)]

View File

@ -1,4 +0,0 @@
%: %mtdg "mitdeg"
gene:
[%mtdt p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
%. %mtdt "mitdot"
gene:
[%mtdt p=*gene q=*gene]

View File

@ -1,4 +0,0 @@
%^ %mtkt "mitket"
gene:
[%mtkt p=*gene q=*gene r=*gene s=*gene]

View File

@ -1,4 +0,0 @@
%+ %mtpd "mitpad"
gene:
[%mtpd p=*gene q=*gene r=*gene]

View File

@ -1,4 +0,0 @@
%~ %mtsg "mitsig"
gene:
[%mtsg p=*twig q=*gene r=*gene]

View File

@ -1,4 +0,0 @@
%* %mttr "mittar"
gene:
[%mttr p=*gene]

View File

@ -1,7 +0,0 @@
&= %pmbn "pambon"
gene:
[%pmtr p=*gene q=*(map term foot)]
ap open:
[%bnpd p.gen [%pmzp %gold q.gen]]

View File

@ -1,7 +0,0 @@
&- %pmdp "pamdap"
gene:
[%pmdp p=*gene q=*(map term foot)]
ap open:
[%bnld [%pmdg q.gen] p.gen]

View File

@ -1,7 +0,0 @@
&: %pmdg "pamdeg"
gene:
[%pmdg p=*(map term foot)]
ap open:
[%pmzp %lead p.gen]

View File

@ -1,7 +0,0 @@
&. %pmdt "pamdot"
gene:
[%pmdt p=*gene q=*(map term foot)]
ap open:
[%bnpd p.gen [%pmzp %zinc q.gen]]

View File

@ -1,7 +0,0 @@
&* %pmtr "pamtar"
gene:
[%pmtr p=*gene q=*(map term foot)]
ap open:
[%bnpd p.gen [%pmzp %wood q.gen]]

View File

@ -1,4 +0,0 @@
&= %pmzp "pamzap"
gene:
[%pmbn p=*gene q=*(map term foot)]

View File

@ -1,7 +0,0 @@
~| %sgbr "sigbar"
gene:
[%sgbr p=*gene q=*gene]
ap open:
[%sgld [%bean p.gen] q.gen]

View File

@ -1,7 +0,0 @@
~= %sgbn "sigbon"
gene:
[%sgbn p=*gene q=*gene] :: ~= sigbon
ap open:
[%sgld [%germ p.gen] q.gen]

View File

@ -1,7 +0,0 @@
~- %sgdp "sigdap"
gene:
[%sgdp p=@ q=*gene]
ap open:
[%sgld [%sole %dtsg p.gen] q.gen]

View File

@ -1,7 +0,0 @@
~^ %sgdx "sigdax"
gene:
[%sgdx p=*gene]
ap open:
[%sgld %ping p.gen]

View File

@ -1,7 +0,0 @@
~: %sgdg "sigdeg"
gene:
[%sgdg p=[p=@ q=@] q=*gene]
ap open:
[%sgld [%bank %dtsg p.gen] q.gen]

View File

@ -1,7 +0,0 @@
~< %sgdl "sigdel"
gene:
[%sgdl p=*term q=*gene r=*gene]
ap open:
[%bndl [%sgld p.gen [~ 1]] q.gen]

View File

@ -1,7 +0,0 @@
~^ %sgkt "sigket"
gene:
[%sgkt p=*gene]
ap open:
[%sgld %keep p.gen]

View File

@ -1,4 +0,0 @@
~> %sgld "sigled"
gene:
[%sgld p=*term q=*gene r=*gene] :: ~> sigled

View File

@ -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

View File

@ -1,7 +0,0 @@
~+ %sgpd "sigpad"
gene:
[%sgpd p=@ q=*gene]
ap open:
[%sgld [%memo %dtsg p.gen] q.gen]

View File

@ -1,7 +0,0 @@
~& %sgpm "sigpam"
gene:
[%sgpm p=*gene q=*gene]
ap open:
[%sgld [%loaf p.gen] q.gen]

View File

@ -1,4 +0,0 @@
;= %tmbn "tambon"
gene:
[%tmbn p=*<%atom %noun %cell %flag %null>]

View File

@ -1,4 +0,0 @@
;- %tmdp "tamdap"
gene:
[%tmdp p=*gene]

View File

@ -1,4 +0,0 @@
;+ %tmpd "tampad"
gene:
[%tmpd p=*gene q=*gene]

View File

@ -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)
--

View File

@ -1,4 +0,0 @@
!= %zpbn "zapbon"
gene:
[%zpbn p=*gene]

View File

@ -1,4 +0,0 @@
!_ %zpcb "zapcab"
gene:
[%zpcb p=*spot q=*gene]

View File

@ -1,4 +0,0 @@
!# %zpdx "zapdax"
gene:
[%zpdx p=*gene]

View File

@ -1,4 +0,0 @@
!! %zpzp "zapzap"
gene:
[%zpzp ~]

View File

@ -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)
--