OSDN Git Service

Remove old docs.
[joypy/Thun.git] / docs / sphinx_docs / _build / html / _sources / notebooks / Types.rst.txt
diff --git a/docs/sphinx_docs/_build/html/_sources/notebooks/Types.rst.txt b/docs/sphinx_docs/_build/html/_sources/notebooks/Types.rst.txt
deleted file mode 100644 (file)
index 4c91600..0000000
+++ /dev/null
@@ -1,2968 +0,0 @@
-The Blissful Elegance of Typing Joy
-===================================
-
-This notebook presents a simple type inferencer for Joy code. It can
-infer the stack effect of most Joy expressions. It’s built largely by
-means of existing ideas and research. (A great overview of the existing
-knowledge is a talk `“Type Inference in Stack-Based Programming
-Languages” <http://prl.ccs.neu.edu/blog/2017/03/10/type-inference-in-stack-based-programming-languages/>`__
-given by Rob Kleffner on or about 2017-03-10 as part of a course on the
-history of programming languages.)
-
-The notebook starts with a simple inferencer based on the work of Jaanus
-Pöial which we then progressively elaborate to cover more Joy semantics.
-Along the way we write a simple “compiler” that emits Python code for
-what I like to call Yin functions. (Yin functions are those that only
-rearrange values in stacks, as opposed to Yang functions that actually
-work on the values themselves.)
-
-Part I: Pöial’s Rules
----------------------
-
-`“Typing Tools for Typeless Stack Languages” by Jaanus
-Pöial <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.212.6026>`__
-
-::
-
-   @INPROCEEDINGS{Pöial06typingtools,
-       author = {Jaanus Pöial},
-       title = {Typing tools for typeless stack languages},
-       booktitle = {In 23rd Euro-Forth Conference},
-       year = {2006},
-       pages = {40--46}
-   }
-
-First Rule
-~~~~~~~~~~
-
-This rule deals with functions (and literals) that put items on the
-stack ``(-- d)``:
-
-::
-
-      (a -- b)∘(-- d)
-   ---------------------
-        (a -- b d)
-
-Second Rule
-~~~~~~~~~~~
-
-This rule deals with functions that consume items from the stack
-``(a --)``:
-
-::
-
-      (a --)∘(c -- d)
-   ---------------------
-        (c a -- d)
-
-Third Rule
-~~~~~~~~~~
-
-The third rule is actually two rules. These two rules deal with
-composing functions when the second one will consume one of items the
-first one produces. The two types must be
-`unified <https://en.wikipedia.org/wiki/Robinson's_unification_algorithm>`__
-or a type conflict declared.
-
-::
-
-      (a -- b t[i])∘(c u[j] -- d)   t <= u (t is subtype of u)
-   -------------------------------
-      (a -- b     )∘(c      -- d)   t[i] == t[k] == u[j]
-                                            ^
-
-      (a -- b t[i])∘(c u[j] -- d)   u <= t (u is subtype of t)
-   -------------------------------
-      (a -- b     )∘(c      -- d)   t[i] == u[k] == u[j]
-
-Let’s work through some examples by hand to develop an intuition for the
-algorithm.
-
-There’s a function in one of the other notebooks.
-
-::
-
-   F == pop swap roll< rest rest cons cons
-
-It’s all “stack chatter” and list manipulation so we should be able to
-deduce its type.
-
-Stack Effect Comments
-~~~~~~~~~~~~~~~~~~~~~
-
-Joy function types will be represented by Forth-style stack effect
-comments. I’m going to use numbers instead of names to keep track of the
-stack arguments. (A little bit like `De Bruijn
-index <https://en.wikipedia.org/wiki/De_Bruijn_index>`__, at least it
-reminds me of them):
-
-::
-
-   pop (1 --)
-
-   swap (1 2 -- 2 1)
-
-   roll< (1 2 3 -- 2 3 1)
-
-These commands alter the stack but don’t “look at” the values so these
-numbers represent an “Any type”.
-
-``pop swap``
-~~~~~~~~~~~~
-
-::
-
-   (1 --) (1 2 -- 2 1)
-
-Here we encounter a complication. The argument numbers need to be made
-unique among both sides. For this let’s change ``pop`` to use 0:
-
-::
-
-   (0 --) (1 2 -- 2 1)
-
-Following the second rule:
-
-::
-
-   (1 2 0 -- 2 1)
-
-``pop∘swap roll<``
-~~~~~~~~~~~~~~~~~~
-
-::
-
-   (1 2 0 -- 2 1) (1 2 3 -- 2 3 1)
-
-Let’s re-label them:
-
-::
-
-   (1a 2a 0a -- 2a 1a) (1b 2b 3b -- 2b 3b 1b)
-
-Now we follow the rules.
-
-We must unify ``1a`` and ``3b``, and ``2a`` and ``2b``, replacing the
-terms in the forms:
-
-::
-
-   (1a 2a 0a -- 2a 1a) (1b 2b 3b -- 2b 3b 1b)
-                                               w/  {1a: 3b}
-   (3b 2a 0a -- 2a   ) (1b 2b    -- 2b 3b 1b)
-                                               w/  {2a: 2b}
-   (3b 2b 0a --      ) (1b       -- 2b 3b 1b)
-
-Here we must apply the second rule:
-
-::
-
-      (3b 2b 0a --) (1b -- 2b 3b 1b)
-   -----------------------------------
-        (1b 3b 2b 0a -- 2b 3b 1b)
-
-Now we de-label the type, uh, labels:
-
-::
-
-   (1b 3b 2b 0a -- 2b 3b 1b)
-
-   w/ {
-       1b: 1,
-       3b: 2,
-       2b: 3,
-       0a: 0,
-       }
-
-   (1 2 3 0 -- 3 2 1)
-
-And now we have the stack effect comment for ``pop∘swap∘roll<``.
-
-Compiling ``pop∘swap∘roll<``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The simplest way to “compile” this function would be something like:
-
-.. code:: ipython2
-
-    def poswrd(s, e, d):
-        return rolldown(*swap(*pop(s, e, d)))
-
-However, internally this function would still be allocating tuples
-(stack cells) and doing other unnecesssary work.
-
-Looking ahead for a moment, from the stack effect comment:
-
-::
-
-   (1 2 3 0 -- 3 2 1)
-
-We should be able to directly write out a Python function like:
-
-.. code:: ipython2
-
-    def poswrd(stack):
-        (_, (a, (b, (c, stack)))) = stack
-        return (c, (b, (a, stack)))
-
-This eliminates the internal work of the first version. Because this
-function only rearranges the stack and doesn’t do any actual processing
-on the stack items themselves all the information needed to implement it
-is in the stack effect comment.
-
-Functions on Stacks
-~~~~~~~~~~~~~~~~~~~
-
-These are slightly tricky.
-
-::
-
-   rest ( [1 ...] -- [...] )
-
-   cons ( 1 [...] -- [1 ...] )
-
-``pop∘swap∘roll< rest``
-~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
-   (1 2 3 0 -- 3 2 1) ([1 ...] -- [...])
-
-Re-label (instead of adding left and right tags I’m just taking the next
-available index number for the right-side stack effect comment):
-
-::
-
-   (1 2 3 0 -- 3 2 1) ([4 ...] -- [...])
-
-Unify and update:
-
-::
-
-   (1       2 3 0 -- 3 2 1) ([4 ...] -- [...])
-                                                w/ {1: [4 ...]}
-   ([4 ...] 2 3 0 -- 3 2  ) (        -- [...])
-
-Apply the first rule:
-
-::
-
-      ([4 ...] 2 3 0 -- 3 2) (-- [...])
-   ---------------------------------------
-        ([4 ...] 2 3 0 -- 3 2 [...])
-
-And there we are.
-
-``pop∘swap∘roll<∘rest rest``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Let’s do it again.
-
-::
-
-   ([4 ...] 2 3 0 -- 3 2 [...]) ([1 ...] -- [...])
-
-Re-label (the tails of the lists on each side each get their own label):
-
-::
-
-   ([4 .0.] 2 3 0 -- 3 2 [.0.]) ([5 .1.] -- [.1.])
-
-Unify and update (note the opening square brackets have been omited in
-the substitution dict, this is deliberate and I’ll explain below):
-
-::
-
-   ([4 .0.]   2 3 0 -- 3 2 [.0.]  ) ([5 .1.] -- [.1.])
-                                                       w/ { .0.] : 5 .1.] }
-   ([4 5 .1.] 2 3 0 -- 3 2 [5 .1.]) ([5 .1.] -- [.1.])
-
-How do we find ``.0.]`` in ``[4 .0.]`` and replace it with ``5 .1.]``
-getting the result ``[4 5 .1.]``? This might seem hard, but because the
-underlying structure of the Joy list is a cons-list in Python it’s
-actually pretty easy. I’ll explain below.
-
-Next we unify and find our two terms are the same already: ``[5 .1.]``:
-
-::
-
-   ([4 5 .1.] 2 3 0 -- 3 2 [5 .1.]) ([5 .1.] -- [.1.])
-
-Giving us:
-
-::
-
-   ([4 5 .1.] 2 3 0 -- 3 2) (-- [.1.])
-
-From here we apply the first rule and get:
-
-::
-
-   ([4 5 .1.] 2 3 0 -- 3 2 [.1.])
-
-Cleaning up the labels:
-
-::
-
-   ([4 5 ...] 2 3 1 -- 3 2 [...])
-
-This is the stack effect of ``pop∘swap∘roll<∘rest∘rest``.
-
-``pop∘swap∘roll<∘rest∘rest cons``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
-   ([4 5 ...] 2 3 1 -- 3 2 [...]) (1 [...] -- [1 ...])
-
-Re-label:
-
-::
-
-   ([4 5 .1.] 2 3 1 -- 3 2 [.1.]) (6 [.2.] -- [6 .2.])
-
-Unify:
-
-::
-
-   ([4 5 .1.] 2 3 1 -- 3 2 [.1.]) (6 [.2.] -- [6 .2.])
-                                                        w/ { .1.] : .2.] }
-   ([4 5 .2.] 2 3 1 -- 3 2      ) (6       -- [6 .2.])
-                                                        w/ {2: 6}
-   ([4 5 .2.] 6 3 1 -- 3        ) (        -- [6 .2.])
-
-First rule:
-
-::
-
-   ([4 5 .2.] 6 3 1 -- 3 [6 .2.])
-
-Re-label:
-
-::
-
-   ([4 5 ...] 2 3 1 -- 3 [2 ...])
-
-Done.
-
-``pop∘swap∘roll<∘rest∘rest∘cons cons``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-One more time.
-
-::
-
-   ([4 5 ...] 2 3 1 -- 3 [2 ...]) (1 [...] -- [1 ...])
-
-Re-label:
-
-::
-
-   ([4 5 .1.] 2 3 1 -- 3 [2 .1.]) (6 [.2.] -- [6 .2.])
-
-Unify:
-
-::
-
-   ([4 5 .1.] 2 3 1 -- 3 [2 .1.]) (6 [.2.] -- [6 .2.]  )
-                                                          w/ { .2.] : 2 .1.] }
-   ([4 5 .1.] 2 3 1 -- 3        ) (6       -- [6 2 .1.])
-                                                          w/ {3: 6}
-   ([4 5 .1.] 2 6 1 --          ) (        -- [6 2 .1.])
-
-First or second rule:
-
-::
-
-   ([4 5 .1.] 2 6 1 -- [6 2 .1.])
-
-Clean up the labels:
-
-::
-
-   ([4 5 ...] 2 3 1 -- [3 2 ...])
-
-And there you have it, the stack effect for
-``pop∘swap∘roll<∘rest∘rest∘cons∘cons``.
-
-::
-
-   ([4 5 ...] 2 3 1 -- [3 2 ...])
-
-From this stack effect comment it should be possible to construct the
-following Python code:
-
-.. code:: ipython2
-
-    def F(stack):
-        (_, (d, (c, ((a, (b, S0)), stack)))) = stack
-        return (d, (c, S0)), stack
-
-Part II: Implementation
------------------------
-
-Representing Stack Effect Comments in Python
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-I’m going to use pairs of tuples of type descriptors, which will be
-integers or tuples of type descriptors:
-
-.. code:: ipython2
-
-    roll_dn = (1, 2, 3), (2, 3, 1)
-    
-    pop = (1,), ()
-    
-    swap = (1, 2), (2, 1)
-
-``compose()``
-~~~~~~~~~~~~~
-
-.. code:: ipython2
-
-    def compose(f, g):
-    
-        (f_in, f_out), (g_in, g_out) = f, g
-    
-        # First rule.
-        #
-        #       (a -- b) (-- d)
-        #    ---------------------
-        #         (a -- b d)
-    
-        if not g_in:
-    
-            fg_in, fg_out = f_in, f_out + g_out
-    
-        # Second rule.
-        #
-        #       (a --) (c -- d)
-        #    ---------------------
-        #         (c a -- d)
-    
-        elif not f_out:
-    
-            fg_in, fg_out = g_in + f_in, g_out
-    
-        else: # Unify, update, recur.
-    
-            fo, gi = f_out[-1], g_in[-1]
-    
-            s = unify(gi, fo)
-    
-            if s == False:  # s can also be the empty dict, which is ok.
-                raise TypeError('Cannot unify %r and %r.' % (fo, gi))
-    
-            f_g = (f_in, f_out[:-1]), (g_in[:-1], g_out)
-    
-            if s: f_g = update(s, f_g)
-    
-            fg_in, fg_out = compose(*f_g)
-    
-        return fg_in, fg_out
-
-``unify()``
-~~~~~~~~~~~
-
-.. code:: ipython2
-
-    def unify(u, v, s=None):
-        if s is None:
-            s = {}
-    
-        if isinstance(u, int):
-            s[u] = v
-        elif isinstance(v, int):
-            s[v] = u
-        else:
-            s = False
-    
-        return s
-
-``update()``
-~~~~~~~~~~~~
-
-.. code:: ipython2
-
-    def update(s, term):
-        if not isinstance(term, tuple):
-            return s.get(term, term)
-        return tuple(update(s, inner) for inner in term)
-
-``relabel()``
-~~~~~~~~~~~~~
-
-.. code:: ipython2
-
-    def relabel(left, right):
-        return left, _1000(right)
-    
-    def _1000(right):
-        if not isinstance(right, tuple):
-            return 1000 + right
-        return tuple(_1000(n) for n in right)
-    
-    relabel(pop, swap)
-
-
-
-
-.. parsed-literal::
-
-    (((1,), ()), ((1001, 1002), (1002, 1001)))
-
-
-
-``delabel()``
-~~~~~~~~~~~~~
-
-.. code:: ipython2
-
-    def delabel(f):
-        s = {u: i for i, u in enumerate(sorted(_unique(f)))}
-        return update(s, f)
-    
-    def _unique(f, seen=None):
-        if seen is None:
-            seen = set()
-        if not isinstance(f, tuple):
-            seen.add(f)
-        else:
-            for inner in f:
-                _unique(inner, seen)
-        return seen
-    
-    delabel(relabel(pop, swap))
-
-
-
-
-.. parsed-literal::
-
-    (((0,), ()), ((1, 2), (2, 1)))
-
-
-
-``C()``
-~~~~~~~
-
-At last we put it all together in a function ``C()`` that accepts two
-stack effect comments and returns their composition (or raises and
-exception if they can’t be composed due to type conflicts.)
-
-.. code:: ipython2
-
-    def C(f, g):
-        f, g = relabel(f, g)
-        fg = compose(f, g)
-        return delabel(fg)
-
-Let’s try it out.
-
-.. code:: ipython2
-
-    C(pop, swap)
-
-
-
-
-.. parsed-literal::
-
-    ((1, 2, 0), (2, 1))
-
-
-
-.. code:: ipython2
-
-    C(C(pop, swap), roll_dn)
-
-
-
-
-.. parsed-literal::
-
-    ((3, 1, 2, 0), (2, 1, 3))
-
-
-
-.. code:: ipython2
-
-    C(swap, roll_dn)
-
-
-
-
-.. parsed-literal::
-
-    ((2, 0, 1), (1, 0, 2))
-
-
-
-.. code:: ipython2
-
-    C(pop, C(swap, roll_dn))
-
-
-
-
-.. parsed-literal::
-
-    ((3, 1, 2, 0), (2, 1, 3))
-
-
-
-.. code:: ipython2
-
-    poswrd = reduce(C, (pop, swap, roll_dn))
-    poswrd
-
-
-
-
-.. parsed-literal::
-
-    ((3, 1, 2, 0), (2, 1, 3))
-
-
-
-Stack Functions
-~~~~~~~~~~~~~~~
-
-Here’s that trick to represent functions like ``rest`` and ``cons`` that
-manipulate stacks. We use a cons-list of tuples and give the tails their
-own numbers. Then everything above already works.
-
-.. code:: ipython2
-
-    rest = ((1, 2),), (2,)
-    
-    cons = (1, 2), ((1, 2),)
-
-.. code:: ipython2
-
-    C(poswrd, rest)
-
-
-
-
-.. parsed-literal::
-
-    (((3, 4), 1, 2, 0), (2, 1, 4))
-
-
-
-Compare this to the stack effect comment we wrote above:
-
-::
-
-   ((  (3, 4), 1, 2, 0 ), ( 2, 1,   4  ))
-   (   [4 ...] 2  3  0  --  3  2  [...])
-
-The translation table, if you will, would be:
-
-::
-
-   {
-   3: 4,
-   4: ...],
-   1: 2,
-   2: 3,
-   0: 0,
-   }
-
-.. code:: ipython2
-
-    F = reduce(C, (pop, swap, roll_dn, rest, rest, cons, cons))
-    
-    F
-
-
-
-
-.. parsed-literal::
-
-    (((3, (4, 5)), 1, 2, 0), ((2, (1, 5)),))
-
-
-
-Compare with the stack effect comment and you can see it works fine:
-
-::
-
-   ([4 5 ...] 2 3 1 -- [3 2 ...])
-     3 4  5   1 2 0     2 1  5
-
-Dealing with ``cons`` and ``uncons``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-However, if we try to compose e.g. ``cons`` and ``uncons`` it won’t
-work:
-
-.. code:: ipython2
-
-    uncons = ((1, 2),), (1, 2)
-
-.. code:: ipython2
-
-    try:
-        C(cons, uncons)
-    except Exception, e:
-        print e
-
-
-.. parsed-literal::
-
-    Cannot unify (1, 2) and (1001, 1002).
-
-
-``unify()`` version 2
-^^^^^^^^^^^^^^^^^^^^^
-
-The problem is that the ``unify()`` function as written doesn’t handle
-the case when both terms are tuples. We just have to add a clause to
-deal with this recursively:
-
-.. code:: ipython2
-
-    def unify(u, v, s=None):
-        if s is None:
-            s = {}
-        elif s:
-            u = update(s, u)
-            v = update(s, v)
-    
-        if isinstance(u, int):
-            s[u] = v
-    
-        elif isinstance(v, int):
-            s[v] = u
-    
-        elif isinstance(u, tuple) and isinstance(v, tuple):
-    
-            if len(u) != 2 or len(v) != 2:
-                # Not a type error, caller passed in a bad value.
-                raise ValueError(repr((u, v)))  # FIXME this message sucks.
-    
-            (a, b), (c, d) = u, v
-            s = unify(a, c, s)
-            if s != False:
-                s = unify(b, d, s)
-        else:
-            s = False
-    
-        return s
-
-.. code:: ipython2
-
-    C(cons, uncons)
-
-
-
-
-.. parsed-literal::
-
-    ((0, 1), (0, 1))
-
-
-
-Part III: Compiling Yin Functions
----------------------------------
-
-Now consider the Python function we would like to derive:
-
-.. code:: ipython2
-
-    def F_python(stack):
-        (_, (d, (c, ((a, (b, S0)), stack)))) = stack
-        return (d, (c, S0)), stack
-
-And compare it to the input stack effect comment tuple we just computed:
-
-.. code:: ipython2
-
-    F[0]
-
-
-
-
-.. parsed-literal::
-
-    ((3, (4, 5)), 1, 2, 0)
-
-
-
-The stack-de-structuring tuple has nearly the same form as our input
-stack effect comment tuple, just in the reverse order:
-
-::
-
-   (_, (d, (c, ((a, (b, S0)), stack))))
-
-Remove the punctuation:
-
-::
-
-    _   d   c   (a, (b, S0))
-
-Reverse the order and compare:
-
-::
-
-    (a, (b, S0))   c   d   _
-   ((3, (4, 5 )),  1,  2,  0)
-
-Eh?
-
-And the return tuple
-
-.. code:: ipython2
-
-    F[1]
-
-
-
-
-.. parsed-literal::
-
-    ((2, (1, 5)),)
-
-
-
-is similar to the output stack effect comment tuple:
-
-::
-
-   ((d, (c, S0)), stack)
-   ((2, (1, 5 )),      )
-
-This should make it pretty easy to write a Python function that accepts
-the stack effect comment tuples and returns a new Python function
-(either as a string of code or a function object ready to use) that
-performs the semantics of that Joy function (described by the stack
-effect.)
-
-Python Identifiers
-~~~~~~~~~~~~~~~~~~
-
-We want to substitute Python identifiers for the integers. I’m going to
-repurpose ``joy.parser.Symbol`` class for this:
-
-.. code:: ipython2
-
-    from collections import defaultdict
-    from joy.parser import Symbol
-    
-    
-    def _names_for():
-        I = iter(xrange(1000))
-        return lambda: Symbol('a%i' % next(I))
-    
-    
-    def identifiers(term, s=None):
-        if s is None:
-            s = defaultdict(_names_for())
-        if isinstance(term, int):
-            return s[term]
-        return tuple(identifiers(inner, s) for inner in term)
-
-``doc_from_stack_effect()``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As a convenience I’ve implemented a function to convert the Python stack
-effect comment tuples to reasonable text format. There are some details
-in how this code works that related to stuff later in the notebook, so
-you should skip it for now and read it later if you’re interested.
-
-.. code:: ipython2
-
-    def doc_from_stack_effect(inputs, outputs):
-        return '(%s--%s)' % (
-            ' '.join(map(_to_str, inputs + ('',))),
-            ' '.join(map(_to_str, ('',) + outputs))
-        )
-    
-    
-    def _to_str(term):
-        if not isinstance(term, tuple):
-            try:
-                t = term.prefix == 's'
-            except AttributeError:
-                return str(term)
-            return '[.%i.]' % term.number if t else str(term)
-    
-        a = []
-        while term and isinstance(term, tuple):
-            item, term = term
-            a.append(_to_str(item))
-    
-        try:
-            n = term.number
-        except AttributeError:
-            n = term
-        else:
-            if term.prefix != 's':
-                raise ValueError('Stack label: %s' % (term,))
-    
-        a.append('.%s.' % (n,))
-        return '[%s]' % ' '.join(a)
-
-``compile_()``
-~~~~~~~~~~~~~~
-
-Now we can write a compiler function to emit Python source code. (The
-underscore suffix distiguishes it from the built-in ``compile()``
-function.)
-
-.. code:: ipython2
-
-    def compile_(name, f, doc=None):
-        if doc is None:
-            doc = doc_from_stack_effect(*f)
-        inputs, outputs = identifiers(f)
-        i = o = Symbol('stack')
-        for term in inputs:
-            i = term, i
-        for term in outputs:
-            o = term, o
-        return '''def %s(stack):
-        """%s"""
-        %s = stack
-        return %s''' % (name, doc, i, o)
-
-Here it is in action:
-
-.. code:: ipython2
-
-    source = compile_('F', F)
-    
-    print source
-
-
-.. parsed-literal::
-
-    def F(stack):
-        """([3 4 .5.] 1 2 0 -- [2 1 .5.])"""
-        (a5, (a4, (a3, ((a0, (a1, a2)), stack)))) = stack
-        return ((a4, (a3, a2)), stack)
-
-
-Compare:
-
-.. code:: ipython2
-
-    def F_python(stack):
-        (_, (d, (c, ((a, (b, S0)), stack)))) = stack
-        return ((d, (c, S0)), stack)
-
-Next steps:
-
-.. code:: ipython2
-
-    L = {}
-    
-    eval(compile(source, '__main__', 'single'), {}, L)
-    
-    L['F']
-
-
-
-
-.. parsed-literal::
-
-    <function F>
-
-
-
-Let’s try it out:
-
-.. code:: ipython2
-
-    from notebook_preamble import D, J, V
-    from joy.library import SimpleFunctionWrapper
-
-.. code:: ipython2
-
-    D['F'] = SimpleFunctionWrapper(L['F'])
-
-.. code:: ipython2
-
-    J('[4 5 ...] 2 3 1 F')
-
-
-.. parsed-literal::
-
-    [3 2 ...]
-
-
-With this, we have a partial Joy compiler that works on the subset of
-Joy functions that manipulate stacks (both what I call “stack chatter”
-and the ones that manipulate stacks on the stack.)
-
-I’m probably going to modify the definition wrapper code to detect
-definitions that can be compiled by this partial compiler and do it
-automatically. It might be a reasonable idea to detect sequences of
-compilable functions in definitions that have uncompilable functions in
-them and just compile those. However, if your library is well-factored
-this might be less helpful.
-
-Compiling Library Functions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We can use ``compile_()`` to generate many primitives in the library
-from their stack effect comments:
-
-.. code:: ipython2
-
-    def defs():
-    
-        rolldown = (1, 2, 3), (2, 3, 1)
-    
-        rollup = (1, 2, 3), (3, 1, 2)
-    
-        pop = (1,), ()
-    
-        swap = (1, 2), (2, 1)
-    
-        rest = ((1, 2),), (2,)
-        
-        rrest = C(rest, rest)
-    
-        cons = (1, 2), ((1, 2),)
-    
-        uncons = ((1, 2),), (1, 2)
-        
-        swons = C(swap, cons)
-    
-        return locals()
-
-.. code:: ipython2
-
-    for name, stack_effect_comment in sorted(defs().items()):
-        print
-        print compile_(name, stack_effect_comment)
-        print
-
-
-.. parsed-literal::
-
-    
-    def cons(stack):
-        """(1 2 -- [1 .2.])"""
-        (a1, (a0, stack)) = stack
-        return ((a0, a1), stack)
-    
-    
-    def pop(stack):
-        """(1 --)"""
-        (a0, stack) = stack
-        return stack
-    
-    
-    def rest(stack):
-        """([1 .2.] -- 2)"""
-        ((a0, a1), stack) = stack
-        return (a1, stack)
-    
-    
-    def rolldown(stack):
-        """(1 2 3 -- 2 3 1)"""
-        (a2, (a1, (a0, stack))) = stack
-        return (a0, (a2, (a1, stack)))
-    
-    
-    def rollup(stack):
-        """(1 2 3 -- 3 1 2)"""
-        (a2, (a1, (a0, stack))) = stack
-        return (a1, (a0, (a2, stack)))
-    
-    
-    def rrest(stack):
-        """([0 1 .2.] -- 2)"""
-        ((a0, (a1, a2)), stack) = stack
-        return (a2, stack)
-    
-    
-    def swap(stack):
-        """(1 2 -- 2 1)"""
-        (a1, (a0, stack)) = stack
-        return (a0, (a1, stack))
-    
-    
-    def swons(stack):
-        """(0 1 -- [1 .0.])"""
-        (a1, (a0, stack)) = stack
-        return ((a1, a0), stack)
-    
-    
-    def uncons(stack):
-        """([1 .2.] -- 1 2)"""
-        ((a0, a1), stack) = stack
-        return (a1, (a0, stack))
-    
-
-
-Part IV: Types and Subtypes of Arguments
-----------------------------------------
-
-So far we have dealt with types of functions, those dealing with simple
-stack manipulation. Let’s extend our machinery to deal with types of
-arguments.
-
-“Number” Type
-~~~~~~~~~~~~~
-
-Consider the definition of ``sqr``:
-
-::
-
-   sqr == dup mul
-
-The ``dup`` function accepts one *anything* and returns two of that:
-
-::
-
-   dup (1 -- 1 1)
-
-And ``mul`` accepts two “numbers” (we’re ignoring ints vs. floats
-vs. complex, etc., for now) and returns just one:
-
-::
-
-   mul (n n -- n)
-
-So we’re composing:
-
-::
-
-   (1 -- 1 1)∘(n n -- n)
-
-The rules say we unify 1 with ``n``:
-
-::
-
-      (1 -- 1 1)∘(n n -- n)
-   ---------------------------  w/  {1: n}
-      (1 -- 1  )∘(n   -- n)
-
-This involves detecting that “Any type” arguments can accept “numbers”.
-If we were composing these functions the other way round this is still
-the case:
-
-::
-
-      (n n -- n)∘(1 -- 1 1)
-   ---------------------------  w/  {1: n}
-      (n n --  )∘(  -- n n) 
-
-The important thing here is that the mapping is going the same way in
-both cases, from the “any” integer to the number
-
-Distinguishing Numbers
-~~~~~~~~~~~~~~~~~~~~~~
-
-We should also mind that the number that ``mul`` produces is not
-(necessarily) the same as either of its inputs, which are not
-(necessarily) the same as each other:
-
-::
-
-   mul (n2 n1 -- n3)
-
-
-      (1  -- 1  1)∘(n2 n1 -- n3)
-   --------------------------------  w/  {1: n2}
-      (n2 -- n2  )∘(n2    -- n3)
-
-
-      (n2 n1 -- n3)∘(1 -- 1  1 )
-   --------------------------------  w/  {1: n3}
-      (n2 n1 --   )∘(  -- n3 n3) 
-
-Distinguishing Types
-~~~~~~~~~~~~~~~~~~~~
-
-So we need separate domains of “any” numbers and “number” numbers, and
-we need to be able to ask the order of these domains. Now the notes on
-the right side of rule three make more sense, eh?
-
-::
-
-      (a -- b t[i])∘(c u[j] -- d)   t <= u (t is subtype of u)
-   -------------------------------
-      (a -- b     )∘(c      -- d)   t[i] == t[k] == u[j]
-                                            ^
-
-      (a -- b t[i])∘(c u[j] -- d)   u <= t (u is subtype of t)
-   -------------------------------
-      (a -- b     )∘(c      -- d)   t[i] == u[k] == u[j]
-
-The indices ``i``, ``k``, and ``j`` are the number part of our labels
-and ``t`` and ``u`` are the domains.
-
-By creative use of Python’s “double underscore” methods we can define a
-Python class hierarchy of Joy types and use the ``issubclass()`` method
-to establish domain ordering, as well as other handy behaviour that will
-make it fairly easy to reuse most of the code above.
-
-.. code:: ipython2
-
-    class AnyJoyType(object):
-    
-        prefix = 'a'
-    
-        def __init__(self, number):
-            self.number = number
-    
-        def __repr__(self):
-            return self.prefix + str(self.number)
-    
-        def __eq__(self, other):
-            return (
-                isinstance(other, self.__class__)
-                and other.prefix == self.prefix
-                and other.number == self.number
-            )
-    
-        def __ge__(self, other):
-            return issubclass(other.__class__, self.__class__)
-    
-        def __add__(self, other):
-            return self.__class__(self.number + other)
-        __radd__ = __add__
-        
-        def __hash__(self):
-            return hash(repr(self))
-    
-    
-    class NumberJoyType(AnyJoyType): prefix = 'n'
-    class FloatJoyType(NumberJoyType): prefix = 'f'
-    class IntJoyType(FloatJoyType): prefix = 'i'
-    
-    
-    class StackJoyType(AnyJoyType):
-        prefix = 's'
-    
-    
-    _R = range(10)
-    A = map(AnyJoyType, _R)
-    N = map(NumberJoyType, _R)
-    S = map(StackJoyType, _R)
-
-Mess with it a little:
-
-.. code:: ipython2
-
-    from itertools import permutations
-
-“Any” types can be specialized to numbers and stacks, but not vice
-versa:
-
-.. code:: ipython2
-
-    for a, b in permutations((A[0], N[0], S[0]), 2):
-        print a, '>=', b, '->', a >= b
-
-
-.. parsed-literal::
-
-    a0 >= n0 -> True
-    a0 >= s0 -> True
-    n0 >= a0 -> False
-    n0 >= s0 -> False
-    s0 >= a0 -> False
-    s0 >= n0 -> False
-
-
-Our crude `Numerical
-Tower <https://en.wikipedia.org/wiki/Numerical_tower>`__ of *numbers* >
-*floats* > *integers* works as well (but we’re not going to use it yet):
-
-.. code:: ipython2
-
-    for a, b in permutations((A[0], N[0], FloatJoyType(0), IntJoyType(0)), 2):
-        print a, '>=', b, '->', a >= b
-
-
-.. parsed-literal::
-
-    a0 >= n0 -> True
-    a0 >= f0 -> True
-    a0 >= i0 -> True
-    n0 >= a0 -> False
-    n0 >= f0 -> True
-    n0 >= i0 -> True
-    f0 >= a0 -> False
-    f0 >= n0 -> False
-    f0 >= i0 -> True
-    i0 >= a0 -> False
-    i0 >= n0 -> False
-    i0 >= f0 -> False
-
-
-Typing ``sqr``
-~~~~~~~~~~~~~~
-
-.. code:: ipython2
-
-    dup = (A[1],), (A[1], A[1])
-    
-    mul = (N[1], N[2]), (N[3],)
-
-.. code:: ipython2
-
-    dup
-
-
-
-
-.. parsed-literal::
-
-    ((a1,), (a1, a1))
-
-
-
-.. code:: ipython2
-
-    mul
-
-
-
-
-.. parsed-literal::
-
-    ((n1, n2), (n3,))
-
-
-
-Modifying the Inferencer
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Re-labeling still works fine:
-
-.. code:: ipython2
-
-    foo = relabel(dup, mul)
-    
-    foo
-
-
-
-
-.. parsed-literal::
-
-    (((a1,), (a1, a1)), ((n1001, n1002), (n1003,)))
-
-
-
-``delabel()`` version 2
-^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``delabel()`` function needs an overhaul. It now has to keep track
-of how many labels of each domain it has “seen”.
-
-.. code:: ipython2
-
-    from collections import Counter
-    
-    
-    def delabel(f, seen=None, c=None):
-        if seen is None:
-            assert c is None
-            seen, c = {}, Counter()
-    
-        try:
-            return seen[f]
-        except KeyError:
-            pass
-    
-        if not isinstance(f, tuple):
-            seen[f] = f.__class__(c[f.prefix] + 1)
-            c[f.prefix] += 1
-            return seen[f]
-    
-        return tuple(delabel(inner, seen, c) for inner in f)
-
-.. code:: ipython2
-
-    delabel(foo)
-
-
-
-
-.. parsed-literal::
-
-    (((a1,), (a1, a1)), ((n1, n2), (n3,)))
-
-
-
-``unify()`` version 3
-^^^^^^^^^^^^^^^^^^^^^
-
-.. code:: ipython2
-
-    def unify(u, v, s=None):
-        if s is None:
-            s = {}
-        elif s:
-            u = update(s, u)
-            v = update(s, v)
-    
-        if u == v:
-            return s
-    
-        if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
-            if u >= v:
-                s[u] = v
-                return s
-            if v >= u:
-                s[v] = u
-                return s
-            raise TypeError('Cannot unify %r and %r.' % (u, v))
-    
-        if isinstance(u, tuple) and isinstance(v, tuple):
-            if len(u) != len(v) != 2:
-                raise TypeError(repr((u, v)))
-            for uu, vv in zip(u, v):
-                s = unify(uu, vv, s)
-                if s == False: # (instead of a substitution dict.)
-                    break
-            return s
-     
-        if isinstance(v, tuple):
-            if not stacky(u):
-                raise TypeError('Cannot unify %r and %r.' % (u, v))
-            s[u] = v
-            return s
-    
-        if isinstance(u, tuple):
-            if not stacky(v):
-                raise TypeError('Cannot unify %r and %r.' % (v, u))
-            s[v] = u
-            return s
-    
-        return False
-    
-    
-    def stacky(thing):
-        return thing.__class__ in {AnyJoyType, StackJoyType}
-
-Rewrite the stack effect comments:
-
-.. code:: ipython2
-
-    def defs():
-    
-        rolldown = (A[1], A[2], A[3]), (A[2], A[3], A[1])
-    
-        rollup = (A[1], A[2], A[3]), (A[3], A[1], A[2])
-    
-        pop = (A[1],), ()
-    
-        popop = (A[2], A[1],), ()
-    
-        popd = (A[2], A[1],), (A[1],)
-    
-        popdd = (A[3], A[2], A[1],), (A[2], A[1],)
-    
-        swap = (A[1], A[2]), (A[2], A[1])
-    
-        rest = ((A[1], S[1]),), (S[1],)
-    
-        rrest = C(rest, rest)
-    
-        cons = (A[1], S[1]), ((A[1], S[1]),)
-    
-        ccons = C(cons, cons)
-    
-        uncons = ((A[1], S[1]),), (A[1], S[1])
-    
-        swons = C(swap, cons)
-    
-        dup = (A[1],), (A[1], A[1])
-    
-        dupd = (A[2], A[1]), (A[2], A[2], A[1])
-    
-        mul = (N[1], N[2]), (N[3],)
-        
-        sqrt = C(dup, mul)
-    
-        first = ((A[1], S[1]),), (A[1],)
-    
-        second = C(rest, first)
-    
-        third = C(rest, second)
-    
-        tuck = (A[2], A[1]), (A[1], A[2], A[1])
-    
-        over = (A[2], A[1]), (A[2], A[1], A[2])
-        
-        succ = pred = (N[1],), (N[2],)
-        
-        divmod_ = pm = (N[2], N[1]), (N[4], N[3])
-    
-        return locals()
-
-.. code:: ipython2
-
-    DEFS = defs()
-
-.. code:: ipython2
-
-    for name, stack_effect_comment in sorted(DEFS.items()):
-        print name, '=', doc_from_stack_effect(*stack_effect_comment)
-
-
-.. parsed-literal::
-
-    ccons = (a1 a2 [.1.] -- [a1 a2 .1.])
-    cons = (a1 [.1.] -- [a1 .1.])
-    divmod_ = (n2 n1 -- n4 n3)
-    dup = (a1 -- a1 a1)
-    dupd = (a2 a1 -- a2 a2 a1)
-    first = ([a1 .1.] -- a1)
-    mul = (n1 n2 -- n3)
-    over = (a2 a1 -- a2 a1 a2)
-    pm = (n2 n1 -- n4 n3)
-    pop = (a1 --)
-    popd = (a2 a1 -- a1)
-    popdd = (a3 a2 a1 -- a2 a1)
-    popop = (a2 a1 --)
-    pred = (n1 -- n2)
-    rest = ([a1 .1.] -- [.1.])
-    rolldown = (a1 a2 a3 -- a2 a3 a1)
-    rollup = (a1 a2 a3 -- a3 a1 a2)
-    rrest = ([a1 a2 .1.] -- [.1.])
-    second = ([a1 a2 .1.] -- a2)
-    sqrt = (n1 -- n2)
-    succ = (n1 -- n2)
-    swap = (a1 a2 -- a2 a1)
-    swons = ([.1.] a1 -- [a1 .1.])
-    third = ([a1 a2 a3 .1.] -- a3)
-    tuck = (a2 a1 -- a1 a2 a1)
-    uncons = ([a1 .1.] -- a1 [.1.])
-
-
-.. code:: ipython2
-
-    globals().update(DEFS)
-
-Compose ``dup`` and ``mul``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. code:: ipython2
-
-    C(dup, mul)
-
-
-
-
-.. parsed-literal::
-
-    ((n1,), (n2,))
-
-
-
-Revisit the ``F`` function, works fine.
-
-.. code:: ipython2
-
-    F = reduce(C, (pop, swap, rolldown, rest, rest, cons, cons))
-    F
-
-
-
-
-.. parsed-literal::
-
-    (((a1, (a2, s1)), a3, a4, a5), ((a4, (a3, s1)),))
-
-
-
-.. code:: ipython2
-
-    print doc_from_stack_effect(*F)
-
-
-.. parsed-literal::
-
-    ([a1 a2 .1.] a3 a4 a5 -- [a4 a3 .1.])
-
-
-Some otherwise inefficient functions are no longer to be feared. We can
-also get the effect of combinators in some limited cases.
-
-.. code:: ipython2
-
-    def neato(*funcs):
-        print doc_from_stack_effect(*reduce(C, funcs))
-
-.. code:: ipython2
-
-    # e.g. [swap] dip
-    neato(rollup, swap, rolldown)
-
-
-.. parsed-literal::
-
-    (a1 a2 a3 -- a2 a1 a3)
-
-
-.. code:: ipython2
-
-    # e.g. [popop] dipd
-    neato(popdd, rolldown, pop)
-
-
-.. parsed-literal::
-
-    (a1 a2 a3 a4 -- a3 a4)
-
-
-.. code:: ipython2
-
-    # Reverse the order of the top three items.
-    neato(rollup, swap)
-
-
-.. parsed-literal::
-
-    (a1 a2 a3 -- a3 a2 a1)
-
-
-``compile_()`` version 2
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-Because the type labels represent themselves as valid Python identifiers
-the ``compile_()`` function doesn’t need to generate them anymore:
-
-.. code:: ipython2
-
-    def compile_(name, f, doc=None):
-        inputs, outputs = f
-        if doc is None:
-            doc = doc_from_stack_effect(inputs, outputs)
-        i = o = Symbol('stack')
-        for term in inputs:
-            i = term, i
-        for term in outputs:
-            o = term, o
-        return '''def %s(stack):
-        """%s"""
-        %s = stack
-        return %s''' % (name, doc, i, o)
-
-.. code:: ipython2
-
-    print compile_('F', F)
-
-
-.. parsed-literal::
-
-    def F(stack):
-        """([a1 a2 .1.] a3 a4 a5 -- [a4 a3 .1.])"""
-        (a5, (a4, (a3, ((a1, (a2, s1)), stack)))) = stack
-        return ((a4, (a3, s1)), stack)
-
-
-But it cannot magically create new functions that involve e.g. math and
-such. Note that this is *not* a ``sqr`` function implementation:
-
-.. code:: ipython2
-
-    print compile_('sqr', C(dup, mul))
-
-
-.. parsed-literal::
-
-    def sqr(stack):
-        """(n1 -- n2)"""
-        (n1, stack) = stack
-        return (n2, stack)
-
-
-(Eventually I should come back around to this becuase it’s not tooo
-difficult to exend this code to be able to compile e.g.
-``n2 = mul(n1, n1)`` for ``mul`` with the right variable names and
-insert it in the right place. It requires a little more support from the
-library functions, in that we need to know to call ``mul()`` the Python
-function for ``mul`` the Joy function, but since *most* of the math
-functions (at least) are already wrappers it should be straightforward.)
-
-``compilable()``
-^^^^^^^^^^^^^^^^
-
-The functions that *can* be compiled are the ones that have only
-``AnyJoyType`` and ``StackJoyType`` labels in their stack effect
-comments. We can write a function to check that:
-
-.. code:: ipython2
-
-    from itertools import imap
-    
-    
-    def compilable(f):
-        return isinstance(f, tuple) and all(imap(compilable, f)) or stacky(f)
-
-.. code:: ipython2
-
-    for name, stack_effect_comment in sorted(defs().items()):
-        if compilable(stack_effect_comment):
-            print name, '=', doc_from_stack_effect(*stack_effect_comment)
-
-
-.. parsed-literal::
-
-    ccons = (a1 a2 [.1.] -- [a1 a2 .1.])
-    cons = (a1 [.1.] -- [a1 .1.])
-    dup = (a1 -- a1 a1)
-    dupd = (a2 a1 -- a2 a2 a1)
-    first = ([a1 .1.] -- a1)
-    over = (a2 a1 -- a2 a1 a2)
-    pop = (a1 --)
-    popd = (a2 a1 -- a1)
-    popdd = (a3 a2 a1 -- a2 a1)
-    popop = (a2 a1 --)
-    rest = ([a1 .1.] -- [.1.])
-    rolldown = (a1 a2 a3 -- a2 a3 a1)
-    rollup = (a1 a2 a3 -- a3 a1 a2)
-    rrest = ([a1 a2 .1.] -- [.1.])
-    second = ([a1 a2 .1.] -- a2)
-    swap = (a1 a2 -- a2 a1)
-    swons = ([.1.] a1 -- [a1 .1.])
-    third = ([a1 a2 a3 .1.] -- a3)
-    tuck = (a2 a1 -- a1 a2 a1)
-    uncons = ([a1 .1.] -- a1 [.1.])
-
-
-Part V: Functions that use the Stack
-------------------------------------
-
-Consider the ``stack`` function which grabs the whole stack, quotes it,
-and puts it on itself:
-
-::
-
-   stack (...     -- ... [...]        )
-   stack (... a   -- ... a [a ...]    )
-   stack (... b a -- ... b a [a b ...])
-
-We would like to represent this in Python somehow. To do this we use a
-simple, elegant trick.
-
-::
-
-   stack         S   -- (         S,           S)
-   stack     (a, S)  -- (     (a, S),      (a, S))
-   stack (a, (b, S)) -- ( (a, (b, S)), (a, (b, S)))
-
-Instead of representing the stack effect comments as a single tuple
-(with N items in it) we use the same cons-list structure to hold the
-sequence and ``unify()`` the whole comments.
-
-``stack∘uncons``
-~~~~~~~~~~~~~~~~
-
-Let’s try composing ``stack`` and ``uncons``. We want this result:
-
-::
-
-   stack∘uncons (... a -- ... a a [...])
-
-The stack effects are:
-
-::
-
-   stack = S -- (S, S)
-
-   uncons = ((a, Z), S) -- (Z, (a, S))
-
-Unifying:
-
-::
-
-     S    -- (S, S) ∘ ((a, Z), S) -- (Z, (a,   S   ))
-                                                       w/ { S: (a, Z) }
-   (a, Z) --        ∘             -- (Z, (a, (a, Z)))
-
-So:
-
-::
-
-   stack∘uncons == (a, Z) -- (Z, (a, (a, Z)))
-
-It works.
-
-``stack∘uncons∘uncons``
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Let’s try ``stack∘uncons∘uncons``:
-
-::
-
-   (a, S     ) -- (S,      (a, (a, S     ))) ∘ ((b, Z),  S`             ) -- (Z, (b,   S`   ))
-
-                                                                                   w/ { S: (b, Z) }
-                                                                                   
-   (a, (b, Z)) -- ((b, Z), (a, (a, (b, Z)))) ∘ ((b, Z),  S`             ) -- (Z, (b,   S`   ))
-
-                                                                                   w/ { S`: (a, (a, (b, Z))) }
-                                                                                   
-   (a, (b, Z)) -- ((b, Z), (a, (a, (b, Z)))) ∘ ((b, Z), (a, (a, (b, Z)))) -- (Z, (b, (a, (a, (b, Z)))))
-
-   (a, (b, Z)) -- (Z, (b, (a, (a, (b, Z)))))
-
-It works.
-
-``compose()`` version 2
-^^^^^^^^^^^^^^^^^^^^^^^
-
-This function has to be modified to use the new datastructures and it is
-no longer recursive, instead recursion happens as part of unification.
-Further, the first and second of Pöial’s rules are now handled
-automatically by the unification algorithm. (One easy way to see this is
-that now an empty stack effect comment is represented by a
-``StackJoyType`` instance which is not “falsey” and so neither of the
-first two rules’ ``if`` clauses will ever be ``True``. Later on I change
-the “truthiness” of ``StackJoyType`` to false to let e.g.
-``joy.utils.stack.concat`` work with our stack effect comment cons-list
-tuples.)
-
-.. code:: ipython2
-
-    def compose(f, g):
-        (f_in, f_out), (g_in, g_out) = f, g
-        s = unify(g_in, f_out)
-        if s == False:  # s can also be the empty dict, which is ok.
-            raise TypeError('Cannot unify %r and %r.' % (f_out, g_in))
-        return update(s, (f_in, g_out))
-
-I don’t want to rewrite all the defs myself, so I’ll write a little
-conversion function instead. This is programmer’s laziness.
-
-.. code:: ipython2
-
-    def sequence_to_stack(seq, stack=StackJoyType(23)):
-        for item in seq: stack = item, stack
-        return stack
-    
-    NEW_DEFS = {
-        name: (sequence_to_stack(i), sequence_to_stack(o))
-        for name, (i, o) in DEFS.iteritems()
-    }
-    NEW_DEFS['stack'] = S[0], (S[0], S[0])
-    NEW_DEFS['swaack'] = (S[1], S[0]), (S[0], S[1])
-    globals().update(NEW_DEFS)
-
-.. code:: ipython2
-
-    C(stack, uncons)
-
-
-
-
-.. parsed-literal::
-
-    ((a1, s1), (s1, (a1, (a1, s1))))
-
-
-
-.. code:: ipython2
-
-    reduce(C, (stack, uncons, uncons))
-
-
-
-
-.. parsed-literal::
-
-    ((a1, (a2, s1)), (s1, (a2, (a1, (a1, (a2, s1))))))
-
-
-
-The display function should be changed too.
-
-``doc_from_stack_effect()`` version 2
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Clunky junk, but it will suffice for now.
-
-.. code:: ipython2
-
-    def doc_from_stack_effect(inputs, outputs):
-        switch = [False]  # Do we need to display the '...' for the rest of the main stack?
-        i, o = _f(inputs, switch), _f(outputs, switch)
-        if switch[0]:
-            i.append('...')
-            o.append('...')
-        return '(%s--%s)' % (
-            ' '.join(reversed([''] + i)),
-            ' '.join(reversed(o + [''])),
-        )
-    
-    
-    def _f(term, switch):
-        a = []
-        while term and isinstance(term, tuple):
-            item, term = term
-            a.append(item)
-        assert isinstance(term, StackJoyType), repr(term)
-        a = [_to_str(i, term, switch) for i in a]
-        return a
-    
-    
-    def _to_str(term, stack, switch):
-        if not isinstance(term, tuple):
-            if term == stack:
-                switch[0] = True
-                return '[...]'
-            return (
-                '[.%i.]' % term.number
-                if isinstance(term, StackJoyType)
-                else str(term)
-            )
-    
-        a = []
-        while term and isinstance(term, tuple):
-            item, term = term
-            a.append(_to_str(item, stack, switch))
-        assert isinstance(term, StackJoyType), repr(term)
-        if term == stack:
-            switch[0] = True
-            end = '...'
-        else:
-            end = '.%i.' % term.number
-        a.append(end)
-        return '[%s]' % ' '.join(a)
-
-.. code:: ipython2
-
-    for name, stack_effect_comment in sorted(NEW_DEFS.items()):
-        print name, '=', doc_from_stack_effect(*stack_effect_comment)
-
-
-.. parsed-literal::
-
-    ccons = (a1 a2 [.1.] -- [a1 a2 .1.])
-    cons = (a1 [.1.] -- [a1 .1.])
-    divmod_ = (n2 n1 -- n4 n3)
-    dup = (a1 -- a1 a1)
-    dupd = (a2 a1 -- a2 a2 a1)
-    first = ([a1 .1.] -- a1)
-    mul = (n1 n2 -- n3)
-    over = (a2 a1 -- a2 a1 a2)
-    pm = (n2 n1 -- n4 n3)
-    pop = (a1 --)
-    popd = (a2 a1 -- a1)
-    popdd = (a3 a2 a1 -- a2 a1)
-    popop = (a2 a1 --)
-    pred = (n1 -- n2)
-    rest = ([a1 .1.] -- [.1.])
-    rolldown = (a1 a2 a3 -- a2 a3 a1)
-    rollup = (a1 a2 a3 -- a3 a1 a2)
-    rrest = ([a1 a2 .1.] -- [.1.])
-    second = ([a1 a2 .1.] -- a2)
-    sqrt = (n1 -- n2)
-    stack = (... -- ... [...])
-    succ = (n1 -- n2)
-    swaack = ([.1.] -- [.0.])
-    swap = (a1 a2 -- a2 a1)
-    swons = ([.1.] a1 -- [a1 .1.])
-    third = ([a1 a2 a3 .1.] -- a3)
-    tuck = (a2 a1 -- a1 a2 a1)
-    uncons = ([a1 .1.] -- a1 [.1.])
-
-
-.. code:: ipython2
-
-    print ; print doc_from_stack_effect(*stack)
-    print ; print doc_from_stack_effect(*C(stack, uncons))
-    print ; print doc_from_stack_effect(*reduce(C, (stack, uncons, uncons)))
-    print ; print doc_from_stack_effect(*reduce(C, (stack, uncons, cons)))
-
-
-.. parsed-literal::
-
-    
-    (... -- ... [...])
-    
-    (... a1 -- ... a1 a1 [...])
-    
-    (... a2 a1 -- ... a2 a1 a1 a2 [...])
-    
-    (... a1 -- ... a1 [a1 ...])
-
-
-.. code:: ipython2
-
-    print doc_from_stack_effect(*C(ccons, stack))
-
-
-.. parsed-literal::
-
-    (... a2 a1 [.1.] -- ... [a2 a1 .1.] [[a2 a1 .1.] ...])
-
-
-.. code:: ipython2
-
-    Q = C(ccons, stack)
-    
-    Q
-
-
-
-
-.. parsed-literal::
-
-    ((s1, (a1, (a2, s2))), (((a2, (a1, s1)), s2), ((a2, (a1, s1)), s2)))
-
-
-
-``compile_()`` version 3
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-This makes the ``compile_()`` function pretty simple as the stack effect
-comments are now already in the form needed for the Python code:
-
-.. code:: ipython2
-
-    def compile_(name, f, doc=None):
-        i, o = f
-        if doc is None:
-            doc = doc_from_stack_effect(i, o)
-        return '''def %s(stack):
-        """%s"""
-        %s = stack
-        return %s''' % (name, doc, i, o)
-
-.. code:: ipython2
-
-    print compile_('Q', Q)
-
-
-.. parsed-literal::
-
-    def Q(stack):
-        """(... a2 a1 [.1.] -- ... [a2 a1 .1.] [[a2 a1 .1.] ...])"""
-        (s1, (a1, (a2, s2))) = stack
-        return (((a2, (a1, s1)), s2), ((a2, (a1, s1)), s2))
-
-
-
-
-
-
-
-.. code:: ipython2
-
-    unstack = (S[1], S[0]), S[1]
-    enstacken = S[0], (S[0], S[1])
-
-.. code:: ipython2
-
-    print doc_from_stack_effect(*unstack)
-
-
-.. parsed-literal::
-
-    ([.1.] --)
-
-
-.. code:: ipython2
-
-    print doc_from_stack_effect(*enstacken)
-
-
-.. parsed-literal::
-
-    (-- [.0.])
-
-
-.. code:: ipython2
-
-    print doc_from_stack_effect(*C(cons, unstack))
-
-
-.. parsed-literal::
-
-    (a1 [.1.] -- a1)
-
-
-.. code:: ipython2
-
-    print doc_from_stack_effect(*C(cons, enstacken))
-
-
-.. parsed-literal::
-
-    (a1 [.1.] -- [[a1 .1.] .2.])
-
-
-.. code:: ipython2
-
-    C(cons, unstack)
-
-
-
-
-.. parsed-literal::
-
-    ((s1, (a1, s2)), (a1, s1))
-
-
-
-
-Part VI: Multiple Stack Effects
--------------------------------
-
-…
-
-.. code:: ipython2
-
-    class IntJoyType(NumberJoyType): prefix = 'i'
-    
-    
-    F = map(FloatJoyType, _R)
-    I = map(IntJoyType, _R)
-
-.. code:: ipython2
-
-    muls = [
-         ((I[2], (I[1], S[0])), (I[3], S[0])),
-         ((F[2], (I[1], S[0])), (F[3], S[0])),
-         ((I[2], (F[1], S[0])), (F[3], S[0])),
-         ((F[2], (F[1], S[0])), (F[3], S[0])),
-    ]
-
-.. code:: ipython2
-
-    for f in muls:
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    (i1 i2 -- i3)
-    (i1 f2 -- f3)
-    (f1 i2 -- f3)
-    (f1 f2 -- f3)
-
-
-.. code:: ipython2
-
-    for f in muls:
-        try:
-            e = C(dup, f)
-        except TypeError:
-            continue
-        print doc_from_stack_effect(*dup), doc_from_stack_effect(*f), doc_from_stack_effect(*e)
-
-
-.. parsed-literal::
-
-    (a1 -- a1 a1) (i1 i2 -- i3) (i1 -- i2)
-    (a1 -- a1 a1) (f1 f2 -- f3) (f1 -- f2)
-
-
-.. code:: ipython2
-
-    from itertools import product
-    
-    
-    def meta_compose(F, G):
-        for f, g in product(F, G):
-            try:
-                yield C(f, g)
-            except TypeError:
-                pass
-    
-    
-    def MC(F, G):
-        return sorted(set(meta_compose(F, G)))
-
-.. code:: ipython2
-
-    for f in MC([dup], [mul]):
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    (n1 -- n2)
-
-
-.. code:: ipython2
-
-    for f in MC([dup], muls):
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    (f1 -- f2)
-    (i1 -- i2)
-
-
-Representing an Unbounded Sequence of Types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We can borrow a trick from `Brzozowski’s Derivatives of Regular
-Expressions <https://en.wikipedia.org/wiki/Brzozowski_derivative>`__ to
-invent a new type of type variable, a “sequence type” (I think this is
-what they mean in the literature by that term…) or “`Kleene
-Star <https://en.wikipedia.org/wiki/Kleene_star>`__” type. I’m going to
-represent it as a type letter and the asterix, so a sequence of zero or
-more ``AnyJoyType`` variables would be:
-
-::
-
-   A*
-
-The ``A*`` works by splitting the universe into two alternate histories:
-
-::
-
-   A* -> 0 | A A*
-
-The Kleene star variable disappears in one universe, and in the other it
-turns into an ``AnyJoyType`` variable followed by itself again. We have
-to return all universes (represented by their substitution dicts, the
-“unifiers”) that don’t lead to type conflicts.
-
-Consider unifying two stacks (the lowercase letters are any type
-variables of the kinds we have defined so far):
-
-::
-
-   [a A* b .0.] U [c d .1.]
-                             w/ {c: a}
-   [  A* b .0.] U [  d .1.]
-
-Now we have to split universes to unify ``A*``. In the first universe it
-disappears:
-
-::
-
-   [b .0.] U [d .1.]
-                      w/ {d: b, .1.: .0.} 
-        [] U []
-
-While in the second it spawns an ``A``, which we will label ``e``:
-
-::
-
-   [e A* b .0.] U [d .1.]
-                           w/ {d: e}
-   [  A* b .0.] U [  .1.]
-                           w/ {.1.: A* b .0.}
-   [  A* b .0.] U [  A* b .0.]
-
-Giving us two unifiers:
-
-::
-
-   {c: a,  d: b,  .1.:      .0.}
-   {c: a,  d: e,  .1.: A* b .0.}
-
-.. code:: ipython2
-
-    class KleeneStar(object):
-    
-        kind = AnyJoyType
-    
-        def __init__(self, number):
-            self.number = number
-            self.count = 0
-            self.prefix = repr(self)
-    
-        def __repr__(self):
-            return '%s%i*' % (self.kind.prefix, self.number)
-    
-        def another(self):
-            self.count += 1
-            return self.kind(10000 * self.number + self.count)
-    
-        def __eq__(self, other):
-            return (
-                isinstance(other, self.__class__)
-                and other.number == self.number
-            )
-    
-        def __ge__(self, other):
-            return self.kind >= other.kind
-    
-        def __add__(self, other):
-            return self.__class__(self.number + other)
-        __radd__ = __add__
-        
-        def __hash__(self):
-            return hash(repr(self))
-    
-    class AnyStarJoyType(KleeneStar): kind = AnyJoyType
-    class NumberStarJoyType(KleeneStar): kind = NumberJoyType
-    #class FloatStarJoyType(KleeneStar): kind = FloatJoyType
-    #class IntStarJoyType(KleeneStar): kind = IntJoyType
-    class StackStarJoyType(KleeneStar): kind = StackJoyType
-    
-    
-    As = map(AnyStarJoyType, _R)
-    Ns = map(NumberStarJoyType, _R)
-    Ss = map(StackStarJoyType, _R)
-
-``unify()`` version 4
-^^^^^^^^^^^^^^^^^^^^^
-
-Can now return multiple results…
-
-.. code:: ipython2
-
-    def unify(u, v, s=None):
-        if s is None:
-            s = {}
-        elif s:
-            u = update(s, u)
-            v = update(s, v)
-    
-        if u == v:
-            return s,
-    
-        if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
-            if u >= v:
-                s[u] = v
-                return s,
-            if v >= u:
-                s[v] = u
-                return s,
-            raise TypeError('Cannot unify %r and %r.' % (u, v))
-    
-        if isinstance(u, tuple) and isinstance(v, tuple):
-            if len(u) != len(v) != 2:
-                raise TypeError(repr((u, v)))
-                
-            a, b = v
-            if isinstance(a, KleeneStar):
-                # Two universes, in one the Kleene star disappears and unification
-                # continues without it...
-                s0 = unify(u, b)
-                
-                # In the other it spawns a new variable.
-                s1 = unify(u, (a.another(), v))
-                
-                t = s0 + s1
-                for sn in t:
-                    sn.update(s)
-                return t
-    
-            a, b = u
-            if isinstance(a, KleeneStar):
-                s0 = unify(v, b)
-                s1 = unify(v, (a.another(), u))
-                t = s0 + s1
-                for sn in t:
-                    sn.update(s)
-                return t
-    
-            ses = unify(u[0], v[0], s)
-            results = ()
-            for sn in ses:
-                results += unify(u[1], v[1], sn)
-            return results
-     
-        if isinstance(v, tuple):
-            if not stacky(u):
-                raise TypeError('Cannot unify %r and %r.' % (u, v))
-            s[u] = v
-            return s,
-    
-        if isinstance(u, tuple):
-            if not stacky(v):
-                raise TypeError('Cannot unify %r and %r.' % (v, u))
-            s[v] = u
-            return s,
-    
-        return ()
-    
-    
-    def stacky(thing):
-        return thing.__class__ in {AnyJoyType, StackJoyType}
-
-.. code:: ipython2
-
-    a = (As[1], S[1])
-    a
-
-
-
-
-.. parsed-literal::
-
-    (a1*, s1)
-
-
-
-.. code:: ipython2
-
-    b = (A[1], S[2])
-    b
-
-
-
-
-.. parsed-literal::
-
-    (a1, s2)
-
-
-
-.. code:: ipython2
-
-    for result in unify(b, a):
-        print result, '->', update(result, a), update(result, b)
-
-
-.. parsed-literal::
-
-    {s1: (a1, s2)} -> (a1*, (a1, s2)) (a1, s2)
-    {a1: a10001, s2: (a1*, s1)} -> (a1*, s1) (a10001, (a1*, s1))
-
-
-.. code:: ipython2
-
-    for result in unify(a, b):
-        print result, '->', update(result, a), update(result, b)
-
-
-.. parsed-literal::
-
-    {s1: (a1, s2)} -> (a1*, (a1, s2)) (a1, s2)
-    {a1: a10002, s2: (a1*, s1)} -> (a1*, s1) (a10002, (a1*, s1))
-
-
-::
-
-   (a1*, s1)       [a1*]       (a1, s2)        [a1]
-
-   (a1*, (a1, s2)) [a1* a1]    (a1, s2)        [a1]
-
-   (a1*, s1)       [a1*]       (a2, (a1*, s1)) [a2 a1*]
-
-.. code:: ipython2
-
-    sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0])
-    
-    print doc_from_stack_effect(*sum_)
-
-
-.. parsed-literal::
-
-    ([n1* .1.] -- n0)
-
-
-.. code:: ipython2
-
-    f = (N[1], (N[2], (N[3], S[1]))), S[0]
-    
-    print doc_from_stack_effect(S[0], f)
-
-
-.. parsed-literal::
-
-    (-- [n1 n2 n3 .1.])
-
-
-.. code:: ipython2
-
-    for result in unify(sum_[0], f):
-        print result, '->', update(result, sum_[1])
-
-
-.. parsed-literal::
-
-    {s1: (n1, (n2, (n3, s1)))} -> (n0, s0)
-    {n1: n10001, s1: (n2, (n3, s1))} -> (n0, s0)
-    {n1: n10001, s1: (n3, s1), n2: n10002} -> (n0, s0)
-    {n1: n10001, s1: (n1*, s1), n3: n10003, n2: n10002} -> (n0, s0)
-
-
-``compose()`` version 3
-^^^^^^^^^^^^^^^^^^^^^^^
-
-This function has to be modified to yield multiple results.
-
-.. code:: ipython2
-
-    def compose(f, g):
-        (f_in, f_out), (g_in, g_out) = f, g
-        s = unify(g_in, f_out)
-        if not s:
-            raise TypeError('Cannot unify %r and %r.' % (f_out, g_in))
-        for result in s:
-            yield update(result, (f_in, g_out))
-
-
-
-.. code:: ipython2
-
-    def meta_compose(F, G):
-        for f, g in product(F, G):
-            try:
-                for result in C(f, g):
-                    yield result
-            except TypeError:
-                pass
-    
-    
-    def C(f, g):
-        f, g = relabel(f, g)
-        for fg in compose(f, g):
-            yield delabel(fg)
-
-.. code:: ipython2
-
-    for f in MC([dup], muls):
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    (f1 -- f2)
-    (i1 -- i2)
-
-
-.. code:: ipython2
-
-    
-    
-    for f in MC([dup], [sum_]):
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    ([n1* .1.] -- [n1* .1.] n1)
-
-
-.. code:: ipython2
-
-    
-    
-    for f in MC([cons], [sum_]):
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    (a1 [.1.] -- n1)
-    (n1 [n1* .1.] -- n2)
-
-
-.. code:: ipython2
-
-    sum_ = (((N[1], (Ns[1], S[1])), S[0]), (N[0], S[0]))
-    print doc_from_stack_effect(*cons),
-    print doc_from_stack_effect(*sum_),
-    
-    for f in MC([cons], [sum_]):
-        print doc_from_stack_effect(*f)
-
-
-.. parsed-literal::
-
-    (a1 [.1.] -- [a1 .1.]) ([n1 n1* .1.] -- n0) (n1 [n1* .1.] -- n2)
-
-
-.. code:: ipython2
-
-    a = (A[4], (As[1], (A[3], S[1])))
-    a
-
-
-
-
-.. parsed-literal::
-
-    (a4, (a1*, (a3, s1)))
-
-
-
-.. code:: ipython2
-
-    b = (A[1], (A[2], S[2]))
-    b
-
-
-
-
-.. parsed-literal::
-
-    (a1, (a2, s2))
-
-
-
-.. code:: ipython2
-
-    for result in unify(b, a):
-        print result
-
-
-.. parsed-literal::
-
-    {a1: a4, s2: s1, a2: a3}
-    {a1: a4, s2: (a1*, (a3, s1)), a2: a10003}
-
-
-.. code:: ipython2
-
-    for result in unify(a, b):
-        print result
-
-
-.. parsed-literal::
-
-    {s2: s1, a2: a3, a4: a1}
-    {s2: (a1*, (a3, s1)), a2: a10004, a4: a1}
-
-
-Part VII: Typing Combinators
-----------------------------
-
-In order to compute the stack effect of combinators you kinda have to
-have the quoted programs they expect available. In the most general
-case, the ``i`` combinator, you can’t say anything about its stack
-effect other than it expects one quote:
-
-::
-
-   i (... [.1.] -- ... .1.)
-
-Or
-
-::
-
-   i (... [A* .1.] -- ... A*)
-
-Consider the type of:
-
-::
-
-   [cons] dip
-
-Obviously it would be:
-
-::
-
-   (a1 [..1] a2 -- [a1 ..1] a2)
-
-``dip`` itself could have:
-
-::
-
-   (a1 [..1] -- ... then what?
-
-Without any information about the contents of the quote we can’t say
-much about the result.
-
-Hybrid Inferencer/Interpreter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-I think there’s a way forward. If we convert our list (of terms we are
-composing) into a stack structure we can use it as a *Joy expression*,
-then we can treat the *output half* of a function’s stack effect comment
-as a Joy interpreter stack, and just execute combinators directly. We
-can hybridize the compostition function with an interpreter to evaluate
-combinators, compose non-combinator functions, and put type variables on
-the stack. For combinators like ``branch`` that can have more than one
-stack effect we have to “split universes” again and return both.
-
-Joy Types for Functions
-^^^^^^^^^^^^^^^^^^^^^^^
-
-We need a type variable for Joy functions that can go in our expressions
-and be used by the hybrid inferencer/interpreter. They have to store a
-name and a list of stack effects.
-
-.. code:: ipython2
-
-    class FunctionJoyType(AnyJoyType):
-    
-        def __init__(self, name, sec, number):
-            self.name = name
-            self.stack_effects = sec
-            self.number = number
-    
-        def __add__(self, other):
-            return self
-        __radd__ = __add__
-    
-        def __repr__(self):
-            return self.name
-
-Specialized for Simple Functions and Combinators
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-For non-combinator functions the stack effects list contains stack
-effect comments (represented by pairs of cons-lists as described above.)
-
-.. code:: ipython2
-
-    class SymbolJoyType(FunctionJoyType):
-        prefix = 'F'
-
-For combinators the list contains Python functions.
-
-.. code:: ipython2
-
-    class CombinatorJoyType(FunctionJoyType):
-    
-        prefix = 'C'
-    
-        def __init__(self, name, sec, number, expect=None):
-            super(CombinatorJoyType, self).__init__(name, sec, number)
-            self.expect = expect
-    
-        def enter_guard(self, f):
-            if self.expect is None:
-                return f
-            g = self.expect, self.expect
-            new_f = list(compose(f, g, ()))
-            assert len(new_f) == 1, repr(new_f)
-            return new_f[0][1]
-
-For simple combinators that have only one effect (like ``dip``) you only
-need one function and it can be the combinator itself.
-
-.. code:: ipython2
-
-    import joy.library
-    
-    dip = CombinatorJoyType('dip', [joy.library.dip], 23)
-
-For combinators that can have more than one effect (like ``branch``) you
-have to write functions that each implement the action of one of the
-effects.
-
-.. code:: ipython2
-
-    def branch_true(stack, expression, dictionary):
-        (then, (else_, (flag, stack))) = stack
-        return stack, concat(then, expression), dictionary
-    
-    def branch_false(stack, expression, dictionary):
-        (then, (else_, (flag, stack))) = stack
-        return stack, concat(else_, expression), dictionary
-    
-    branch = CombinatorJoyType('branch', [branch_true, branch_false], 100)
-
-You can also provide an optional stack effect, input-side only, that
-will then be used as an identity function (that accepts and returns
-stacks that match the “guard” stack effect) which will be used to guard
-against type mismatches going into the evaluation of the combinator.
-
-``infer()``
-^^^^^^^^^^^
-
-With those in place, we can define a function that accepts a sequence of
-Joy type variables, including ones representing functions (not just
-values), and attempts to grind out all the possible stack effects of
-that expression.
-
-One tricky thing is that type variables *in the expression* have to be
-updated along with the stack effects after doing unification or we risk
-losing useful information. This was a straightforward, if awkward,
-modification to the call structure of ``meta_compose()`` et. al.
-
-.. code:: ipython2
-
-    ID = S[0], S[0]  # Identity function.
-    
-    
-    def infer(*expression):
-        return sorted(set(_infer(list_to_stack(expression))))
-    
-    
-    def _infer(e, F=ID):
-        _log_it(e, F)
-        if not e:
-            return [F]
-    
-        n, e = e
-    
-        if isinstance(n, SymbolJoyType):
-            eFG = meta_compose([F], n.stack_effects, e)
-            res = flatten(_infer(e, Fn) for e, Fn in eFG)
-    
-        elif isinstance(n, CombinatorJoyType):
-            fi, fo = n.enter_guard(F)
-            res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects)
-    
-        elif isinstance(n, Symbol):
-            assert n not in FUNCTIONS, repr(n)
-            func = joy.library._dictionary[n]
-            res = _interpret(func, F[0], F[1], e)
-    
-        else:
-            fi, fo = F
-            res = _infer(e, (fi, (n, fo)))
-    
-        return res
-    
-    
-    def _interpret(f, fi, fo, e):
-        new_fo, ee, _ = f(fo, e, {})
-        ee = update(FUNCTIONS, ee)  # Fix Symbols.
-        new_F = fi, new_fo
-        return _infer(ee, new_F)
-    
-    
-    def _log_it(e, F):
-        _log.info(
-            u'%3i %s ∘ %s',
-            len(inspect_stack()),
-            doc_from_stack_effect(*F),
-            expression_to_string(e),
-            )
-
-Work in Progress
-^^^^^^^^^^^^^^^^
-
-And that brings us to current Work-In-Progress. The mixed-mode
-inferencer/interpreter ``infer()`` function seems to work well. There
-are details I should document, and the rest of the code in the ``types``
-module (FIXME link to its docs here!) should be explained… There is
-cruft to convert the definitions in ``DEFS`` to the new
-``SymbolJoyType`` objects, and some combinators. Here is an example of
-output from the current code :
-
-.. code:: ipython2
-
-    1/0  # (Don't try to run this cell!  It's not going to work.  This is "read only" code heh..)
-    
-    logging.basicConfig(format='%(message)s', stream=sys.stdout, level=logging.INFO)
-    
-    globals().update(FUNCTIONS)
-    
-    h = infer((pred, s2), (mul, s3), (div, s4), (nullary, (bool, s5)), dipd, branch)
-    
-    print '-' * 40
-    
-    for fi, fo in h:
-        print doc_from_stack_effect(fi, fo)
-
-
-::
-
-
-    ---------------------------------------------------------------------------
-
-    ZeroDivisionError                         Traceback (most recent call last)
-
-    <ipython-input-1-9a9d60354c35> in <module>()
-    ----> 1 1/0  # (Don't try to run this cell!  It's not going to work.  This is "read only" code heh..)
-          2 
-          3 logging.basicConfig(format='%(message)s', stream=sys.stdout, level=logging.INFO)
-          4 
-          5 globals().update(FUNCTIONS)
-
-
-    ZeroDivisionError: integer division or modulo by zero
-
-
-The numbers at the start of the lines are the current depth of the
-Python call stack. They’re followed by the current computed stack effect
-(initialized to ``ID``) then the pending expression (the inference of
-the stack effect of which is the whole object of the current example.)
-
-In this example we are implementing (and inferring) ``ifte`` as
-``[nullary bool] dipd branch`` which shows off a lot of the current
-implementation in action.
-
-::
-
-     7 (--) ∘ [pred] [mul] [div] [nullary bool] dipd branch
-     8 (-- [pred ...2]) ∘ [mul] [div] [nullary bool] dipd branch
-     9 (-- [pred ...2] [mul ...3]) ∘ [div] [nullary bool] dipd branch
-    10 (-- [pred ...2] [mul ...3] [div ...4]) ∘ [nullary bool] dipd branch
-    11 (-- [pred ...2] [mul ...3] [div ...4] [nullary bool ...5]) ∘ dipd branch
-    15 (-- [pred ...5]) ∘ nullary bool [mul] [div] branch
-    19 (-- [pred ...2]) ∘ [stack] dinfrirst bool [mul] [div] branch
-    20 (-- [pred ...2] [stack ]) ∘ dinfrirst bool [mul] [div] branch
-    22 (-- [pred ...2] [stack ]) ∘ dip infra first bool [mul] [div] branch
-    26 (--) ∘ stack [pred] infra first bool [mul] [div] branch
-    29 (... -- ... [...]) ∘ [pred] infra first bool [mul] [div] branch
-    30 (... -- ... [...] [pred ...1]) ∘ infra first bool [mul] [div] branch
-    34 (--) ∘ pred s1 swaack first bool [mul] [div] branch
-    37 (n1 -- n2) ∘ [n1] swaack first bool [mul] [div] branch
-    38 (... n1 -- ... n2 [n1 ...]) ∘ swaack first bool [mul] [div] branch
-    41 (... n1 -- ... n1 [n2 ...]) ∘ first bool [mul] [div] branch
-    44 (n1 -- n1 n2) ∘ bool [mul] [div] branch
-    47 (n1 -- n1 b1) ∘ [mul] [div] branch
-    48 (n1 -- n1 b1 [mul ...1]) ∘ [div] branch
-    49 (n1 -- n1 b1 [mul ...1] [div ...2]) ∘ branch
-    53 (n1 -- n1) ∘ div
-    56 (f2 f1 -- f3) ∘ 
-    56 (i1 f1 -- f2) ∘ 
-    56 (f1 i1 -- f2) ∘ 
-    56 (i2 i1 -- f1) ∘ 
-    53 (n1 -- n1) ∘ mul
-    56 (f2 f1 -- f3) ∘ 
-    56 (i1 f1 -- f2) ∘ 
-    56 (f1 i1 -- f2) ∘ 
-    56 (i2 i1 -- i3) ∘ 
-   ----------------------------------------
-   (f2 f1 -- f3)
-   (i1 f1 -- f2)
-   (f1 i1 -- f2)
-   (i2 i1 -- f1)
-   (i2 i1 -- i3)
-
-Conclusion
-----------
-
-We built a simple type inferencer, and a kind of crude “compiler” for a
-subset of Joy functions. Then we built a more powerful inferencer that
-actually does some evaluation and explores branching code paths
-
-Work remains to be done:
-
--  the rest of the library has to be covered
--  figure out how to deal with ``loop`` and ``genrec``, etc..
--  extend the types to check values (see the appendix)
--  other kinds of “higher order” type variables, OR, AND, etc..
--  maybe rewrite in Prolog for great good?
--  definitions
-
-   -  don’t permit composition of functions that don’t compose
-   -  auto-compile compilable functions
-
--  Compiling more than just the Yin functions.
--  getting better visibility (than Python debugger.)
--  DOOOOCS!!!! Lots of docs!
-
-   -  docstrings all around
-   -  improve this notebook (it kinda falls apart at the end
-      narratively. I went off and just started writing code to see if it
-      would work. It does, but now I have to come back and describe here
-      what I did.
-
-Appendix: Joy in the Logical Paradigm
--------------------------------------
-
-For *type checking* to work the type label classes have to be modified
-to let ``T >= t`` succeed, where e.g. ``T`` is ``IntJoyType`` and ``t``
-is ``int``. If you do that you can take advantage of the *logical
-relational* nature of the stack effect comments to “compute in reverse”
-as it were. There’s a working demo of this at the end of the ``types``
-module. But if you’re interested in all that you should just use Prolog!
-
-Anyhow, type *checking* is a few easy steps away.
-
-.. code:: ipython2
-
-    def _ge(self, other):
-        return (issubclass(other.__class__, self.__class__)
-                or hasattr(self, 'accept')
-                and isinstance(other, self.accept))
-    
-    AnyJoyType.__ge__ = _ge
-    AnyJoyType.accept = tuple, int, float, long, str, unicode, bool, Symbol
-    StackJoyType.accept = tuple