From 968556c7f326416172a1b6e763229cac08d227b3 Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Thu, 21 Jun 2018 21:23:34 -0700 Subject: [PATCH] Simple type inference and compiler. The compiler works for the subset of Joy functions that deal strictly in manipulating stacks and their contents. --- docs/Types.html | 1330 ++++++++++++++++- docs/Types.md | 658 +++++++- docs/Types.rst | 725 ++++++++- .../_build/html/_modules/index.html | 1 + .../_build/html/_modules/joy/library.html | 304 ++-- .../_build/html/_sources/library.rst.txt | 6 + docs/sphinx_docs/_build/html/genindex.html | 70 +- docs/sphinx_docs/_build/html/index.html | 1 + docs/sphinx_docs/_build/html/library.html | 358 +++-- .../_build/html/notebooks/Developing.html | 4 +- .../_build/html/notebooks/NoUpdates.html | 4 +- docs/sphinx_docs/_build/html/objects.inv | Bin 1325 -> 1403 bytes docs/sphinx_docs/_build/html/py-modindex.html | 5 + docs/sphinx_docs/_build/html/searchindex.js | 2 +- docs/sphinx_docs/library.rst | 6 + joy/utils/generated_library.py | 365 +++++ joy/utils/types.py | 297 ++++ 17 files changed, 3789 insertions(+), 347 deletions(-) create mode 100644 joy/utils/generated_library.py create mode 100644 joy/utils/types.py diff --git a/docs/Types.html b/docs/Types.html index 1231e62..8add8a5 100644 --- a/docs/Types.html +++ b/docs/Types.html @@ -14042,11 +14042,11 @@ i0 >= f0 -> False if v >= u: s[v] = u return s - raise ValueError('Cannot unify %r and %r.' % (u, v)) + raise TypeError('Cannot unify %r and %r.' % (u, v)) if isinstance(u, tuple) and isinstance(v, tuple): if len(u) != len(v) != 2: - raise ValueError(repr((u, v))) + 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.) @@ -14055,13 +14055,13 @@ i0 >= f0 -> False if isinstance(v, tuple): if not stacky(u): - raise ValueError('Cannot unify %r and %r.' % (u, v)) + raise TypeError('Cannot unify %r and %r.' % (u, v)) s[u] = v return s if isinstance(u, tuple): if not stacky(v): - raise ValueError('Cannot unify %r and %r.' % (v, u)) + raise TypeError('Cannot unify %r and %r.' % (v, u)) s[v] = u return s @@ -15379,10 +15379,207 @@ uncons = ([a1 .1.] -- a1 [.1.])
-

Sets of Stack Effects

...

+

Multiple Stack Effects

...

+ +
+
+
In [82]:
+
+
+
class IntJoyType(NumberJoyType): prefix = 'i'
+
+
+F = map(FloatJoyType, _R)
+I = map(IntJoyType, _R)
+
+ +
+
+
+ +
+
+
+
In [83]:
+
+
+
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])),
+]
+
+ +
+
+
+ +
+
+
+
In [84]:
+
+
+
for f in muls:
+    print doc_from_stack_effect(*f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(i1 i2 -- i3)
+(i1 f2 -- f3)
+(f1 i2 -- f3)
+(f1 f2 -- f3)
+
+
+
+ +
+
+ +
+
+
+
In [85]:
+
+
+
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)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(a1 -- a1 a1) (i1 i2 -- i3) (i0 -- i1)
+(a1 -- a1 a1) (f1 f2 -- f3) (f0 -- f1)
+
+
+
+ +
+
+ +
+
+
+
In [86]:
+
+
+
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)))
+
+ +
+
+
+ +
+
+
+
In [87]:
+
+
+
for f in MC([dup], muls):
+    print doc_from_stack_effect(*f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(f0 -- f1)
+(i0 -- i1)
+
+
+
+ +
+
+ +
+
+
+
In [88]:
+
+
+
for f in MC([dup], [mul]):
+    print doc_from_stack_effect(*f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(n0 -- n1)
+
+
+
+ +
+
+
@@ -15400,7 +15597,7 @@ uncons = ([a1 .1.] -- a1 [.1.])
-
In [82]:
+
In [89]:
concat = (S[0], S[1]), ((S[0], S[1]),)
@@ -15429,7 +15626,7 @@ uncons = ([a1 .1.] -- a1 [.1.])
 
-
In [83]:
+
In [90]:
concat = (S[0], S[1]), (S[2],)
@@ -15460,24 +15657,1115 @@ uncons = ([a1 .1.] -- a1 [.1.])
 
-

Which works but can lose information. Consider cons concat, this is how much information we could retain:

+

Brzo...'s Derivitives of Regular Expressions

We can 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" 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:

-
(1 [.0.] [.1.] -- [1 .0. .1.]) uncons uncons
-
-(1 [.0.] [.1.] -- 1 [.0. .1.])        uncons
-                                                So far so good...
-(1 [2 .2.] [.1.] -- 1 2 [.2. .1.])
-
-
-
-
-(1 [.0.] [.1.] -- 1 [.0. .1.]) ([a1 .10.] -- a1 [.10.])
-                                                         w/ { [a1 .10.] : [  .0.   .1.] }
-                                                       -or-
-                                                         w/ { [  .0.   .1.] : [a1 .10.    ] }
+
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 [  .1.]
+ +
+
+
+
+
+
+
+

Giving us two unifiers:

+ +
{c: a,  d: b,  .1.:      .0.}
+{c: a,  d: e,  .1.: A* b .0.}
+ +
+
+
+
+
+
In [91]:
+
+
+
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...

+ +
+
+
+
+
+
In [92]:
+
+
+
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])
+        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}
+
+ +
+
+
+ +
+
+
+
In [93]:
+
+
+
a = (As[1], S[1])
+a
+
+ +
+
+
+ +
+
+ + +
+ +
Out[93]:
+ + + + +
+
(a1*, s1)
+
+ +
+ +
+
+ +
+
+
+
In [94]:
+
+
+
b = (A[1], S[2])
+b
+
+ +
+
+
+ +
+
+ + +
+ +
Out[94]:
+ + + + +
+
(a1, s2)
+
+ +
+ +
+
+ +
+
+
+
In [95]:
+
+
+
for result in unify(b, a):
+    print result, '->', update(result, a), update(result, b)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
{s1: (a1, s2)} -> (a1*, (a1, s2)) (a1, s2)
+{a1: a10001, s2: (a1*, s1)} -> (a1*, s1) (a10001, (a1*, s1))
+
+
+
+ +
+
+ +
+
+
+
In [96]:
+
+
+
for result in unify(a, b):
+    print result, '->', update(result, a), update(result, b)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
{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*]
+ +
+
+
+
+
+
In [97]:
+
+
+
sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0])
+
+print doc_from_stack_effect(*sum_)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
([n1* .1.] -- n0)
+
+
+
+ +
+
+ +
+
+
+
In [98]:
+
+
+
f = (N[1], (N[2], (N[3], S[1]))), S[0]
+
+print doc_from_stack_effect(S[0], f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(-- [n1 n2 n3 .1.])
+
+
+
+ +
+
+ +
+
+
+
In [99]:
+
+
+
for result in unify(sum_[0], f):
+    print result, '->', update(result, sum_[1])
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
{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 use the new datastructures and it is no longer recursive, instead recursion happens as part of unification.

+ +
+
+
+
+
+
In [100]:
+
+
+
def compose(f, g):
+
+    (f_in, f_out), (g_in, g_out) = f, g
+
+    if not g_in:
+        yield f_in, stack_concat(g_out, f_out)
+
+    elif not f_out:
+        yield stack_concat(f_in, g_in), g_out
+
+    else: # Unify and update.
+
+        s = unify(g_in, f_out)
+
+        if not s:
+            raise TypeError('Cannot unify %r and %r.' % (fo, gi))
+
+        for result in s:
+            yield update(result, (f_in, g_out))
+
+ +
+
+
+ +
+
+
+
In [101]:
+
+
+
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)
+
+ +
+
+
+ +
+
+
+
In [102]:
+
+
+
for f in MC([dup], muls):
+    print doc_from_stack_effect(*f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(a0 -- f0)
+(a0 -- i0)
+
+
+
+ +
+
+ +
+
+
+
In [103]:
+
+
+
for f in MC([dup], [sum_]):
+    print doc_from_stack_effect(*f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
([n0* .0.] -- [n0* .0.] n0)
+
+
+
+ +
+
+ +
+
+
+
In [104]:
+
+
+
for f in MC([cons], [sum_]):
+    print doc_from_stack_effect(*f)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(a0 [.0.] -- n0)
+(n0 [n0* .0.] -- n1)
+
+
+
+ +
+
+ +
+
+
+
In [105]:
+
+
+
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)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
(a1 [.1.] -- [a1 .1.]) ([n1 n1* .1.] -- n0) (n0 [n0* .0.] -- n1)
+
+
+
+ +
+
+ +
+
+
+
In [106]:
+
+
+
a = (A[4], (As[1], (A[3], S[1])))
+a
+
+ +
+
+
+ +
+
+ + +
+ +
Out[106]:
+ + + + +
+
(a4, (a1*, (a3, s1)))
+
+ +
+ +
+
+ +
+
+
+
In [107]:
+
+
+
b = (A[1], (A[2], S[2]))
+b
+
+ +
+
+
+ +
+
+ + +
+ +
Out[107]:
+ + + + +
+
(a1, (a2, s2))
+
+ +
+ +
+
+ +
+
+
+
In [108]:
+
+
+
for result in unify(b, a):
+    print result
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
{a1: a4, s2: s1, a2: a3}
+{a1: a4, s2: (a1*, (a3, s1)), a2: a10003}
+
+
+
+ +
+
+ +
+
+
+
In [109]:
+
+
+
for result in unify(a, b):
+    print result
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
{s2: s1, a2: a3, a4: a1}
+{s2: (a1*, (a3, s1)), a2: a10004, a4: a1}
+
+
+
+ +
+
+ +
+
+
+
+
+

represent concat

+
([.0.] [.1.] -- [A*(.0.) .1.])
+
+
+

Meaning that A* on the right-hand side should all the crap from .0..

+ +
([      .0.] [.1.] -- [      A* .1.])
+([a     .0.] [.1.] -- [a     A* .1.])
+([a b   .0.] [.1.] -- [a b   A* .1.])
+([a b c .0.] [.1.] -- [a b c A* .1.])
+ +
+
+
+
+
+
+
+

or...

+ +
([       .0.] [.1.] -- [       .1.])
+([a      .0.] [.1.] -- [a      .1.])
+([a b    .0.] [.1.] -- [a b    .1.])
+([a b  c .0.] [.1.] -- [a b  c .1.])
+([a A* c .0.] [.1.] -- [a A* c .1.])
+ +
+
+
+
+
+
+
+ +
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
+ +
+
+
+
+
+
In [110]:
+
+
+
class Astar(object):
+    def __repr__(self):
+        return 'A*'
+
+
+def concat(s0, s1):
+    a = []
+    while isinstance(s0, tuple):
+        term, s0 = s0
+        a.append(term)
+    assert isinstance(s0, StackJoyType), repr(s0)
+    s1 = Astar(), s1
+    for term in reversed(a):
+        s1 = term, s1
+    return s1
+
+ +
+
+
+ +
+
+
+
In [111]:
+
+
+
a, b = (A[1], S[0]), (A[2], S[1])
+
+ +
+
+
+ +
+
+
+
In [112]:
+
+
+
concat(a, b)
+
+ +
+
+
+ +
+
+ + +
+ +
Out[112]:
+ + + + +
+
(a1, (A*, (a2, s1)))
+
+ +
+ +
+
+ +
+
+
+
+
+

Joy in the Logical Paradigm

For this 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

+ +
+
+
+
+
+
In [113]:
+
+
+
F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons))
+
+print doc_from_stack_effect(*F)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
+---------------------------------------------------------------------------
+ValueError                                Traceback (most recent call last)
+<ipython-input-113-4b4cb6ff86e5> in <module>()
+      1 F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons))
+      2 
+----> 3 print doc_from_stack_effect(*F)
+
+<ipython-input-101-ddee30dbb1a6> in C(f, g)
+     10 def C(f, g):
+     11     f, g = relabel(f, g)
+---> 12     for fg in compose(f, g):
+     13         yield delabel(fg)
+
+<ipython-input-100-4237a6bb159d> in compose(f, g)
+      1 def compose(f, g):
+      2 
+----> 3     (f_in, f_out), (g_in, g_out) = f, g
+      4 
+      5     if not g_in:
+
+<ipython-input-101-ddee30dbb1a6> in C(f, g)
+     10 def C(f, g):
+     11     f, g = relabel(f, g)
+---> 12     for fg in compose(f, g):
+     13         yield delabel(fg)
+
+<ipython-input-100-4237a6bb159d> in compose(f, g)
+      1 def compose(f, g):
+      2 
+----> 3     (f_in, f_out), (g_in, g_out) = f, g
+      4 
+      5     if not g_in:
+
+<ipython-input-101-ddee30dbb1a6> in C(f, g)
+     10 def C(f, g):
+     11     f, g = relabel(f, g)
+---> 12     for fg in compose(f, g):
+     13         yield delabel(fg)
+
+<ipython-input-100-4237a6bb159d> in compose(f, g)
+      1 def compose(f, g):
+      2 
+----> 3     (f_in, f_out), (g_in, g_out) = f, g
+      4 
+      5     if not g_in:
+
+<ipython-input-101-ddee30dbb1a6> in C(f, g)
+     10 def C(f, g):
+     11     f, g = relabel(f, g)
+---> 12     for fg in compose(f, g):
+     13         yield delabel(fg)
+
+<ipython-input-100-4237a6bb159d> in compose(f, g)
+      1 def compose(f, g):
+      2 
+----> 3     (f_in, f_out), (g_in, g_out) = f, g
+      4 
+      5     if not g_in:
+
+<ipython-input-101-ddee30dbb1a6> in C(f, g)
+     10 def C(f, g):
+     11     f, g = relabel(f, g)
+---> 12     for fg in compose(f, g):
+     13         yield delabel(fg)
+
+<ipython-input-100-4237a6bb159d> in compose(f, g)
+      1 def compose(f, g):
+      2 
+----> 3     (f_in, f_out), (g_in, g_out) = f, g
+      4 
+      5     if not g_in:
+
+ValueError: need more than 1 value to unpack
+
+
+ +
+
+ +
+
+
+
In [ ]:
+
+
+
from joy.parser import text_to_expression
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
s = text_to_expression('[3 4 ...] 2 1')
+s
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
L = unify(F[1], s)
+L
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
F[1]
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
F[1][0]
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
s[0]
+
+ +
+
+
+
diff --git a/docs/Types.md b/docs/Types.md index d1e2a6b..35be81b 100644 --- a/docs/Types.md +++ b/docs/Types.md @@ -1172,11 +1172,11 @@ def unify(u, v, s=None): if v >= u: s[v] = u return s - raise ValueError('Cannot unify %r and %r.' % (u, v)) + raise TypeError('Cannot unify %r and %r.' % (u, v)) if isinstance(u, tuple) and isinstance(v, tuple): if len(u) != len(v) != 2: - raise ValueError(repr((u, v))) + 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.) @@ -1185,13 +1185,13 @@ def unify(u, v, s=None): if isinstance(v, tuple): if not stacky(u): - raise ValueError('Cannot unify %r and %r.' % (u, v)) + raise TypeError('Cannot unify %r and %r.' % (u, v)) s[u] = v return s if isinstance(u, tuple): if not stacky(v): - raise ValueError('Cannot unify %r and %r.' % (v, u)) + raise TypeError('Cannot unify %r and %r.' % (v, u)) s[v] = u return s @@ -1796,9 +1796,90 @@ C(cons, unstack) -## Sets of Stack Effects +## Multiple Stack Effects ... + +```python +class IntJoyType(NumberJoyType): prefix = 'i' + + +F = map(FloatJoyType, _R) +I = map(IntJoyType, _R) +``` + + +```python +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])), +] +``` + + +```python +for f in muls: + print doc_from_stack_effect(*f) +``` + + (i1 i2 -- i3) + (i1 f2 -- f3) + (f1 i2 -- f3) + (f1 f2 -- f3) + + + +```python +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) +``` + + (a1 -- a1 a1) (i1 i2 -- i3) (i0 -- i1) + (a1 -- a1 a1) (f1 f2 -- f3) (f0 -- f1) + + + +```python +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))) +``` + + +```python +for f in MC([dup], muls): + print doc_from_stack_effect(*f) +``` + + (f0 -- f1) + (i0 -- i1) + + + +```python +for f in MC([dup], [mul]): + print doc_from_stack_effect(*f) +``` + + (n0 -- n1) + + ## `concat` How to deal with `concat`? @@ -1834,29 +1915,578 @@ As opposed to just: (1 [.0.] [.1.] -- [.2.]) +### Brzo...'s Derivitives of Regular Expressions + +We can 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" 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 [ .1.] + +Giving us two unifiers: + + {c: a, d: b, .1.: .0.} + {c: a, d: e, .1.: A* b .0.} + + +```python +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__ -Which works but can lose information. Consider `cons concat`, this is how much information we *could* retain: + def __hash__(self): + return hash(repr(self)) - (1 [.0.] [.1.] -- [1 .0. .1.]) uncons uncons +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 - (1 [.0.] [.1.] -- 1 [.0. .1.]) uncons - So far so good... - (1 [2 .2.] [.1.] -- 1 2 [.2. .1.]) + +As = map(AnyStarJoyType, _R) +Ns = map(NumberStarJoyType, _R) +Ss = map(StackStarJoyType, _R) +``` + +#### `unify()` version 4 +Can now return multiple results... + + +```python +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]) + 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} +``` + + +```python +a = (As[1], S[1]) +a +``` - (1 [.0.] [.1.] -- 1 [.0. .1.]) ([a1 .10.] -- a1 [.10.]) - w/ { [a1 .10.] : [ .0. .1.] } - -or- - w/ { [ .0. .1.] : [a1 .10. ] } + (a1*, s1) +```python +b = (A[1], S[2]) +b +``` + + (a1, s2) + + + + +```python +for result in unify(b, a): + print result, '->', update(result, a), update(result, b) +``` + + {s1: (a1, s2)} -> (a1*, (a1, s2)) (a1, s2) + {a1: a10001, s2: (a1*, s1)} -> (a1*, s1) (a10001, (a1*, s1)) + + + +```python +for result in unify(a, b): + print result, '->', update(result, a), update(result, b) +``` + + {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*] + + +```python +sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0]) + +print doc_from_stack_effect(*sum_) +``` + + ([n1* .1.] -- n0) + + + +```python +f = (N[1], (N[2], (N[3], S[1]))), S[0] + +print doc_from_stack_effect(S[0], f) +``` + + (-- [n1 n2 n3 .1.]) + + + +```python +for result in unify(sum_[0], f): + print result, '->', update(result, sum_[1]) +``` + + {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 use the new datastructures and it is no longer recursive, instead recursion happens as part of unification. + + +```python +def compose(f, g): + + (f_in, f_out), (g_in, g_out) = f, g + + if not g_in: + yield f_in, stack_concat(g_out, f_out) + + elif not f_out: + yield stack_concat(f_in, g_in), g_out + + else: # Unify and update. + + s = unify(g_in, f_out) + + if not s: + raise TypeError('Cannot unify %r and %r.' % (fo, gi)) + + for result in s: + yield update(result, (f_in, g_out)) + +``` + + +```python +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) +``` + + +```python +for f in MC([dup], muls): + print doc_from_stack_effect(*f) +``` + + (a0 -- f0) + (a0 -- i0) + + + +```python + + +for f in MC([dup], [sum_]): + print doc_from_stack_effect(*f) +``` + + ([n0* .0.] -- [n0* .0.] n0) + + + +```python + + +for f in MC([cons], [sum_]): + print doc_from_stack_effect(*f) +``` + + (a0 [.0.] -- n0) + (n0 [n0* .0.] -- n1) + + + +```python +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) +``` + + (a1 [.1.] -- [a1 .1.]) ([n1 n1* .1.] -- n0) (n0 [n0* .0.] -- n1) + + + +```python +a = (A[4], (As[1], (A[3], S[1]))) +a +``` + + + + + (a4, (a1*, (a3, s1))) + + + + +```python +b = (A[1], (A[2], S[2])) +b +``` + + + + + (a1, (a2, s2)) + + + + +```python +for result in unify(b, a): + print result +``` + + {a1: a4, s2: s1, a2: a3} + {a1: a4, s2: (a1*, (a3, s1)), a2: a10003} + + + +```python +for result in unify(a, b): + print result +``` + + {s2: s1, a2: a3, a4: a1} + {s2: (a1*, (a3, s1)), a2: a10004, a4: a1} + + +### represent `concat` + + ([.0.] [.1.] -- [A*(.0.) .1.]) + +Meaning that `A*` on the right-hand side should all the crap from `.0.`. + + ([ .0.] [.1.] -- [ A* .1.]) + ([a .0.] [.1.] -- [a A* .1.]) + ([a b .0.] [.1.] -- [a b A* .1.]) + ([a b c .0.] [.1.] -- [a b c A* .1.]) + + + +or... + + ([ .0.] [.1.] -- [ .1.]) + ([a .0.] [.1.] -- [a .1.]) + ([a b .0.] [.1.] -- [a b .1.]) + ([a b c .0.] [.1.] -- [a b c .1.]) + ([a A* c .0.] [.1.] -- [a A* c .1.]) + + + + (a, (b, S0)) . S1 = (a, (b, (A*, S1))) + + +```python +class Astar(object): + def __repr__(self): + return 'A*' + + +def concat(s0, s1): + a = [] + while isinstance(s0, tuple): + term, s0 = s0 + a.append(term) + assert isinstance(s0, StackJoyType), repr(s0) + s1 = Astar(), s1 + for term in reversed(a): + s1 = term, s1 + return s1 +``` + + +```python +a, b = (A[1], S[0]), (A[2], S[1]) +``` + + +```python +concat(a, b) +``` + + + + + (a1, (A*, (a2, s1))) + + + +## Joy in the Logical Paradigm +For this 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` + + +```python +F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons)) + +print doc_from_stack_effect(*F) +``` + + + --------------------------------------------------------------------------- + + ValueError Traceback (most recent call last) + + in () + 1 F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons)) + 2 + ----> 3 print doc_from_stack_effect(*F) + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + ValueError: need more than 1 value to unpack + + + +```python +from joy.parser import text_to_expression +``` + + +```python +s = text_to_expression('[3 4 ...] 2 1') +s +``` + + +```python +L = unify(F[1], s) +L +``` + + +```python +F[1] +``` + + +```python +F[1][0] +``` + + +```python +s[0] +``` + ## Typing Combinators TBD diff --git a/docs/Types.rst b/docs/Types.rst index 971fdfc..c6ab68e 100644 --- a/docs/Types.rst +++ b/docs/Types.rst @@ -1410,11 +1410,11 @@ of how many labels of each domain it has "seen". if v >= u: s[v] = u return s - raise ValueError('Cannot unify %r and %r.' % (u, v)) + raise TypeError('Cannot unify %r and %r.' % (u, v)) if isinstance(u, tuple) and isinstance(v, tuple): if len(u) != len(v) != 2: - raise ValueError(repr((u, v))) + 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.) @@ -1423,13 +1423,13 @@ of how many labels of each domain it has "seen". if isinstance(v, tuple): if not stacky(u): - raise ValueError('Cannot unify %r and %r.' % (u, v)) + raise TypeError('Cannot unify %r and %r.' % (u, v)) s[u] = v return s if isinstance(u, tuple): if not stacky(v): - raise ValueError('Cannot unify %r and %r.' % (v, u)) + raise TypeError('Cannot unify %r and %r.' % (v, u)) s[v] = u return s @@ -2100,11 +2100,97 @@ comments are now already in the form needed for the Python code: -Sets of Stack Effects ---------------------- +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) (i0 -- i1) + (a1 -- a1 a1) (f1 f2 -- f3) (f0 -- f1) + + +.. 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], muls): + print doc_from_stack_effect(*f) + + +.. parsed-literal:: + + (f0 -- f1) + (i0 -- i1) + + +.. code:: ipython2 + + for f in MC([dup], [mul]): + print doc_from_stack_effect(*f) + + +.. parsed-literal:: + + (n0 -- n1) + + ``concat`` ---------- @@ -2150,24 +2236,631 @@ As opposed to just: (1 [.0.] [.1.] -- [.2.]) -Which works but can lose information. Consider ``cons concat``, this is -how much information we *could* retain: +Brzo...'s Derivitives of Regular Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We can 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" 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: :: - (1 [.0.] [.1.] -- [1 .0. .1.]) uncons uncons + A* - (1 [.0.] [.1.] -- 1 [.0. .1.]) uncons - So far so good... - (1 [2 .2.] [.1.] -- 1 2 [.2. .1.]) +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 [ .1.] + +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]) + 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 - (1 [.0.] [.1.] -- 1 [.0. .1.]) ([a1 .10.] -- a1 [.10.]) - w/ { [a1 .10.] : [ .0. .1.] } - -or- - w/ { [ .0. .1.] : [a1 .10. ] } +.. 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 use the new datastructures and it is +no longer recursive, instead recursion happens as part of unification. + +.. code:: ipython2 + + def compose(f, g): + + (f_in, f_out), (g_in, g_out) = f, g + + if not g_in: + yield f_in, stack_concat(g_out, f_out) + + elif not f_out: + yield stack_concat(f_in, g_in), g_out + + else: # Unify and update. + + s = unify(g_in, f_out) + + if not s: + raise TypeError('Cannot unify %r and %r.' % (fo, gi)) + + 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:: + + (a0 -- f0) + (a0 -- i0) + + +.. code:: ipython2 + + + + for f in MC([dup], [sum_]): + print doc_from_stack_effect(*f) + + +.. parsed-literal:: + + ([n0* .0.] -- [n0* .0.] n0) + + +.. code:: ipython2 + + + + for f in MC([cons], [sum_]): + print doc_from_stack_effect(*f) + + +.. parsed-literal:: + + (a0 [.0.] -- n0) + (n0 [n0* .0.] -- n1) + + +.. 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) (n0 [n0* .0.] -- n1) + + +.. 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} + + +represent ``concat`` +~~~~~~~~~~~~~~~~~~~~ + +:: + + ([.0.] [.1.] -- [A*(.0.) .1.]) + +Meaning that ``A*`` on the right-hand side should all the crap from +``.0.``. + +:: + + ([ .0.] [.1.] -- [ A* .1.]) + ([a .0.] [.1.] -- [a A* .1.]) + ([a b .0.] [.1.] -- [a b A* .1.]) + ([a b c .0.] [.1.] -- [a b c A* .1.]) + +or... + +:: + + ([ .0.] [.1.] -- [ .1.]) + ([a .0.] [.1.] -- [a .1.]) + ([a b .0.] [.1.] -- [a b .1.]) + ([a b c .0.] [.1.] -- [a b c .1.]) + ([a A* c .0.] [.1.] -- [a A* c .1.]) + +:: + + (a, (b, S0)) . S1 = (a, (b, (A*, S1))) + +.. code:: ipython2 + + class Astar(object): + def __repr__(self): + return 'A*' + + + def concat(s0, s1): + a = [] + while isinstance(s0, tuple): + term, s0 = s0 + a.append(term) + assert isinstance(s0, StackJoyType), repr(s0) + s1 = Astar(), s1 + for term in reversed(a): + s1 = term, s1 + return s1 + +.. code:: ipython2 + + a, b = (A[1], S[0]), (A[2], S[1]) + +.. code:: ipython2 + + concat(a, b) + + + + +.. parsed-literal:: + + (a1, (A*, (a2, s1))) + + + +Joy in the Logical Paradigm +--------------------------- + +For this 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`` + +.. code:: ipython2 + + F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons)) + + print doc_from_stack_effect(*F) + + +:: + + + --------------------------------------------------------------------------- + + ValueError Traceback (most recent call last) + + in () + 1 F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons)) + 2 + ----> 3 print doc_from_stack_effect(*F) + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + in C(f, g) + 10 def C(f, g): + 11 f, g = relabel(f, g) + ---> 12 for fg in compose(f, g): + 13 yield delabel(fg) + + + in compose(f, g) + 1 def compose(f, g): + 2 + ----> 3 (f_in, f_out), (g_in, g_out) = f, g + 4 + 5 if not g_in: + + + ValueError: need more than 1 value to unpack + + +.. code:: ipython2 + + from joy.parser import text_to_expression + +.. code:: ipython2 + + s = text_to_expression('[3 4 ...] 2 1') + s + +.. code:: ipython2 + + L = unify(F[1], s) + L + +.. code:: ipython2 + + F[1] + +.. code:: ipython2 + + F[1][0] + +.. code:: ipython2 + + s[0] Typing Combinators ------------------ diff --git a/docs/sphinx_docs/_build/html/_modules/index.html b/docs/sphinx_docs/_build/html/_modules/index.html index 2908d2d..22e2981 100644 --- a/docs/sphinx_docs/_build/html/_modules/index.html +++ b/docs/sphinx_docs/_build/html/_modules/index.html @@ -34,6 +34,7 @@ diff --git a/docs/sphinx_docs/_build/html/_modules/joy/library.html b/docs/sphinx_docs/_build/html/_modules/joy/library.html index 3f18dc6..23354be 100644 --- a/docs/sphinx_docs/_build/html/_modules/joy/library.html +++ b/docs/sphinx_docs/_build/html/_modules/joy/library.html @@ -58,12 +58,15 @@ ''' from inspect import getdoc from functools import wraps +from inspect import getmembers, isfunction import operator, math from .parser import text_to_expression, Symbol from .utils.stack import list_to_stack, iter_stack, pick, concat from .utils.brutal_hackery import rename_code_object +from .utils import generated_library as genlib + _dictionary = {} @@ -121,12 +124,8 @@ definitions = ('''\ -second == rest first -third == rest rest first of == swap at product == 1 swap [*] step -swons == swap cons -swoncat == swap concat flatten == [] swap [concat] step unit == [] cons quoted == [unit] dip @@ -161,6 +160,10 @@ make_generator == [codireco] ccons ccons == cons cons ''' +##second == rest first +##third == rest rest first +##swons == swap cons +##swoncat == swap concat ##Zipper ##z-down == [] swap uncons swap @@ -291,6 +294,11 @@ # +# Load the auto-generated primitives into the dictionary. +for name, primitive in getmembers(genlib, isfunction): + inscribe(SimpleFunctionWrapper(primitive)) + +
[docs]@inscribe @SimpleFunctionWrapper def parse(stack): @@ -300,30 +308,30 @@ return expression, stack
-
[docs]@inscribe -@SimpleFunctionWrapper -def first(stack): - ''' - :: - - first == uncons pop - - ''' - ((head, tail), stack) = stack - return head, stack
+##@inscribe +##@SimpleFunctionWrapper +##def first(stack): +## ''' +## :: +## +## first == uncons pop +## +## ''' +## ((head, tail), stack) = stack +## return head, stack -
[docs]@inscribe -@SimpleFunctionWrapper -def rest(stack): - ''' - :: - - rest == uncons popd - - ''' - ((head, tail), stack) = stack - return tail, stack
+##@inscribe +##@SimpleFunctionWrapper +##def rest(stack): +## ''' +## :: +## +## rest == uncons popd +## +## ''' +## ((head, tail), stack) = stack +## return tail, stack
[docs]@inscribe @@ -512,28 +520,28 @@ return list_to_stack(sorted(iter_stack(tos))), stack
-
[docs]@inscribe -@SimpleFunctionWrapper -def cons(S): - ''' - The cons operator expects a list on top of the stack and the potential - member below. The effect is to add the potential member into the - aggregate. - ''' - (tos, (second, stack)) = S - return (second, tos), stack
+##@inscribe +##@SimpleFunctionWrapper +##def cons(S): +## ''' +## The cons operator expects a list on top of the stack and the potential +## member below. The effect is to add the potential member into the +## aggregate. +## ''' +## (tos, (second, stack)) = S +## return (second, tos), stack -
[docs]@inscribe -@SimpleFunctionWrapper -def uncons(S): - ''' - Inverse of cons, removes an item from the top of the list on the stack - and places it under the remaining list. - ''' - (tos, stack) = S - item, tos = tos - return tos, (item, stack)
+##@inscribe +##@SimpleFunctionWrapper +##def uncons(S): +## ''' +## Inverse of cons, removes an item from the top of the list on the stack +## and places it under the remaining list. +## ''' +## (tos, stack) = S +## item, tos = tos +## return tos, (item, stack)
[docs]@inscribe @@ -549,52 +557,52 @@ return ()
-
[docs]@inscribe -@SimpleFunctionWrapper -def dup(S): - '''Duplicate the top item on the stack.''' - (tos, stack) = S - return tos, (tos, stack)
+##@inscribe +##@SimpleFunctionWrapper +##def dup(S): +## '''Duplicate the top item on the stack.''' +## (tos, stack) = S +## return tos, (tos, stack) -
[docs]@inscribe -@SimpleFunctionWrapper -def over(S): - ''' - Copy the second item down on the stack to the top of the stack. - :: - - a b over - -------------- - a b a - - ''' - second = S[1][0] - return second, S
+##@inscribe +##@SimpleFunctionWrapper +##def over(S): +## ''' +## Copy the second item down on the stack to the top of the stack. +## :: +## +## a b over +## -------------- +## a b a +## +## ''' +## second = S[1][0] +## return second, S -
[docs]@inscribe -@SimpleFunctionWrapper -def tuck(S): - ''' - Copy the item at TOS under the second item of the stack. - :: - - a b tuck - -------------- - b a b - - ''' - (tos, (second, stack)) = S - return tos, (second, (tos, stack))
+##@inscribe +##@SimpleFunctionWrapper +##def tuck(S): +## ''' +## Copy the item at TOS under the second item of the stack. +## :: +## +## a b tuck +## -------------- +## b a b +## +## ''' +## (tos, (second, stack)) = S +## return tos, (second, (tos, stack)) -
[docs]@inscribe -@SimpleFunctionWrapper -def swap(S): - '''Swap the top two items on stack.''' - (tos, (second, stack)) = S - return second, (tos, stack)
+##@inscribe +##@SimpleFunctionWrapper +##def swap(S): +## '''Swap the top two items on stack.''' +## (tos, (second, stack)) = S +## return second, (tos, stack)
[docs]@inscribe @@ -605,14 +613,14 @@ return stack, old_stack
-
[docs]@inscribe -@SimpleFunctionWrapper -def stack_(stack): - ''' - The stack operator pushes onto the stack a list containing all the - elements of the stack. - ''' - return stack, stack
+##@inscribe +##@SimpleFunctionWrapper +##def stack_(stack): +## ''' +## The stack operator pushes onto the stack a list containing all the +## elements of the stack. +## ''' +## return stack, stack
[docs]@inscribe @@ -625,42 +633,42 @@ return stack[0]
-
[docs]@inscribe -@SimpleFunctionWrapper -def pop(stack): - '''Pop and discard the top item from the stack.''' - return stack[1]
+##@inscribe +##@SimpleFunctionWrapper +##def pop(stack): +## '''Pop and discard the top item from the stack.''' +## return stack[1] -
[docs]@inscribe -@SimpleFunctionWrapper -def popd(stack): - '''Pop and discard the second item from the stack.''' - (tos, (_, stack)) = stack - return tos, stack
+##@inscribe +##@SimpleFunctionWrapper +##def popd(stack): +## '''Pop and discard the second item from the stack.''' +## (tos, (_, stack)) = stack +## return tos, stack -
[docs]@inscribe -@SimpleFunctionWrapper -def popdd(stack): - '''Pop and discard the third item from the stack.''' - (tos, (second, (_, stack))) = stack - return tos, (second, stack)
+##@inscribe +##@SimpleFunctionWrapper +##def popdd(stack): +## '''Pop and discard the third item from the stack.''' +## (tos, (second, (_, stack))) = stack +## return tos, (second, stack) -
[docs]@inscribe -@SimpleFunctionWrapper -def popop(stack): - '''Pop and discard the first and second items from the stack.''' - return stack[1][1]
+##@inscribe +##@SimpleFunctionWrapper +##def popop(stack): +## '''Pop and discard the first and second items from the stack.''' +## return stack[1][1] -
[docs]@inscribe -@SimpleFunctionWrapper -def dupd(S): - '''Duplicate the second item on the stack.''' - (tos, (second, stack)) = S - return tos, (second, (second, stack))
+##@inscribe +##@SimpleFunctionWrapper +##def dupd(S): +## '''Duplicate the second item on the stack.''' +## (tos, (second, stack)) = S +## return tos, (second, (second, stack))
[docs]@inscribe @@ -793,34 +801,34 @@ return r
-
[docs]@inscribe -@SimpleFunctionWrapper -def rollup(S): - ''' - :: - - a b c - ----------- - b c a - - ''' - (a, (b, (c, stack))) = S - return b, (c, (a, stack))
+##@inscribe +##@SimpleFunctionWrapper +##def rollup(S): +## ''' +## :: +## +## a b c +## ----------- +## b c a +## +## ''' +## (a, (b, (c, stack))) = S +## return b, (c, (a, stack)) -
[docs]@inscribe -@SimpleFunctionWrapper -def rolldown(S): - ''' - :: - - a b c - ----------- - c a b - - ''' - (a, (b, (c, stack))) = S - return c, (a, (b, stack))
+##@inscribe +##@SimpleFunctionWrapper +##def rolldown(S): +## ''' +## :: +## +## a b c +## ----------- +## c a b +## +## ''' +## (a, (b, (c, stack))) = S +## return c, (a, (b, stack)) #def execute(S): diff --git a/docs/sphinx_docs/_build/html/_sources/library.rst.txt b/docs/sphinx_docs/_build/html/_sources/library.rst.txt index 7e9995f..63a621e 100644 --- a/docs/sphinx_docs/_build/html/_sources/library.rst.txt +++ b/docs/sphinx_docs/_build/html/_sources/library.rst.txt @@ -11,3 +11,9 @@ Function Reference :members: +Auto-generated Functions +--------------------------- + +.. automodule:: joy.utils.generated_library + :members: + diff --git a/docs/sphinx_docs/_build/html/genindex.html b/docs/sphinx_docs/_build/html/genindex.html index 8a0dc82..09b0294 100644 --- a/docs/sphinx_docs/_build/html/genindex.html +++ b/docs/sphinx_docs/_build/html/genindex.html @@ -96,6 +96,8 @@

C

@@ -126,15 +128,17 @@
  • dipdd() (in module joy.library)
  • - - +
    -
    -
    -joy.library.cons(stack, expression, dictionary)[source]¶
    -

    The cons operator expects a list on top of the stack and the potential -member below. The effect is to add the potential member into the -aggregate.

    -
    -
    joy.library.dip(stack, expression, dictionary)[source]¶
    @@ -305,18 +297,6 @@ n items removed off the top.

    -
    -
    -joy.library.dup(stack, expression, dictionary)[source]¶
    -

    Duplicate the top item on the stack.

    -
    - -
    -
    -joy.library.dupd(stack, expression, dictionary)[source]¶
    -

    Duplicate the second item on the stack.

    -
    -
    joy.library.dupdip(stack, expression, dictionary)[source]¶
    @@ -330,14 +310,6 @@ n items removed off the top.

    -
    -
    -joy.library.first(stack, expression, dictionary)[source]¶
    -
    first == uncons pop
    -
    -
    -
    -
    joy.library.floor(x)[source]¶
    @@ -509,17 +481,6 @@ new list with the results (in place of the program and original list.

    Given a list find the minimum.

    -
    -
    -joy.library.over(stack, expression, dictionary)[source]¶
    -

    Copy the second item down on the stack to the top of the stack.

    -
       a b over
    ---------------
    -    a b a
    -
    -
    -
    -
    joy.library.parse(stack, expression, dictionary)[source]¶
    @@ -537,30 +498,6 @@ new list with the results (in place of the program and original list.

    -
    -
    -joy.library.pop(stack, expression, dictionary)[source]¶
    -

    Pop and discard the top item from the stack.

    -
    - -
    -
    -joy.library.popd(stack, expression, dictionary)[source]¶
    -

    Pop and discard the second item from the stack.

    -
    - -
    -
    -joy.library.popdd(stack, expression, dictionary)[source]¶
    -

    Pop and discard the third item from the stack.

    -
    - -
    -
    -joy.library.popop(stack, expression, dictionary)[source]¶
    -

    Pop and discard the first and second items from the stack.

    -
    -
    joy.library.pred(stack, expression, dictionary)[source]¶
    @@ -579,14 +516,6 @@ from the the quote. The item is only removed once.

    -
    -
    -joy.library.rest(stack, expression, dictionary)[source]¶
    -
    rest == uncons popd
    -
    -
    -
    -
    joy.library.reverse(stack, expression, dictionary)[source]¶
    @@ -596,26 +525,6 @@ from the the quote. The item is only removed once.

    -
    -
    -joy.library.rolldown(stack, expression, dictionary)[source]¶
    -
       a b c
    ------------
    -   c a b
    -
    -
    -
    - -
    -
    -joy.library.rollup(stack, expression, dictionary)[source]¶
    -
       a b c
    ------------
    -   b c a
    -
    -
    -
    -
    joy.library.select(stack, expression, dictionary)[source]¶
    @@ -667,13 +576,6 @@ Boolean value (so empty string, zero, etc. are counted as false, etc.)

    Negative numbers return complex roots.

    -
    -
    -joy.library.stack_(stack, expression, dictionary)[source]¶
    -

    The stack operator pushes onto the stack a list containing all the -elements of the stack.

    -
    -
    joy.library.step(S, expression, dictionary)[source]¶
    @@ -716,12 +618,6 @@ on top of the stack.

    swap stack

    -
    -
    -joy.library.swap(stack, expression, dictionary)[source]¶
    -

    Swap the top two items on stack.

    -
    -
    joy.library.take(stack, expression, dictionary)[source]¶
    @@ -756,24 +652,6 @@ use reverse if needed.)

    -
    -
    -joy.library.tuck(stack, expression, dictionary)[source]¶
    -

    Copy the item at TOS under the second item of the stack.

    -
       a b tuck
    ---------------
    -    b a b
    -
    -
    -
    - -
    -
    -joy.library.uncons(stack, expression, dictionary)[source]¶
    -

    Inverse of cons, removes an item from the top of the list on the stack -and places it under the remaining list.

    -
    -
    joy.library.unique(stack, expression, dictionary)[source]¶
    @@ -824,6 +702,241 @@ the stack discarding the rest of the stack.

    from each list. The smallest list sets the length of the result list.

    +
    +
    +

    Auto-generated Functions¶

    +
    +
    +joy.utils.generated_library.ccons(stack)[source]¶
    +
    (a1 a0 [...0] -- [a1 a0 ...0])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.cons(stack)[source]¶
    +
    (a1 [...0] -- [a1 ...0])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.dup(stack)[source]¶
    +
    (a1 -- a1 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.dupd(stack)[source]¶
    +
    (a2 a1 -- a2 a2 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.dupdd(stack)[source]¶
    +
    (a3 a2 a1 -- a3 a3 a2 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.first(stack)[source]¶
    +
    ([a1 ...1] -- a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.first_two(stack)[source]¶
    +
    ([a0 a1 ...0] -- a0 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.fourth(stack)[source]¶
    +
    ([a0 a1 a2 a3 ...0] -- a3)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.over(stack)[source]¶
    +
    (a2 a1 -- a2 a1 a2)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.pop(stack)[source]¶
    +
    (a1 --)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.popd(stack)[source]¶
    +
    (a2 a1 -- a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.popdd(stack)[source]¶
    +
    (a3 a2 a1 -- a2 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.popop(stack)[source]¶
    +
    (a2 a1 --)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.popopd(stack)[source]¶
    +
    (a3 a2 a1 -- a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.popopdd(stack)[source]¶
    +
    (a4 a3 a2 a1 -- a2 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.rest(stack)[source]¶
    +
    ([a1 ...0] -- [...0])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.rolldown(stack)[source]¶
    +
    (a1 a2 a3 -- a2 a3 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.rollup(stack)[source]¶
    +
    (a1 a2 a3 -- a3 a1 a2)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.rrest(stack)[source]¶
    +
    ([a0 a1 ...0] -- [...0])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.second(stack)[source]¶
    +
    ([a0 a1 ...0] -- a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.stack(stack)[source]¶
    +
    (... -- ... [...])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.stuncons(stack)[source]¶
    +
    (... a0 -- ... a0 a0 [...])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.stununcons(stack)[source]¶
    +
    (... a1 a0 -- ... a1 a0 a0 a1 [...])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.swap(stack)[source]¶
    +
    (a1 a2 -- a2 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.swons(stack)[source]¶
    +
    ([...0] a0 -- [a0 ...0])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.third(stack)[source]¶
    +
    ([a0 a1 a2 ...0] -- a2)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.tuck(stack)[source]¶
    +
    (a2 a1 -- a1 a2 a1)
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.uncons(stack)[source]¶
    +
    ([a1 ...0] -- a1 [...0])
    +
    +
    +
    + +
    +
    +joy.utils.generated_library.unswons(stack)[source]¶
    +
    ([a0 ...0] -- [...0] a0)
    +
    +
    +
    +
    @@ -837,6 +950,7 @@ from each list. The smallest list sets the length of the result list.

    diff --git a/docs/sphinx_docs/_build/html/notebooks/Developing.html b/docs/sphinx_docs/_build/html/notebooks/Developing.html index c3a6c34..0e2fe6b 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Developing.html +++ b/docs/sphinx_docs/_build/html/notebooks/Developing.html @@ -16,7 +16,7 @@ - + @@ -689,7 +689,7 @@ is just:

  • Documentation overview
  • diff --git a/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html b/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html index 9b4f30e..7dc3da9 100644 --- a/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html +++ b/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html @@ -17,7 +17,7 @@ - + @@ -67,7 +67,7 @@