The simplest way to “compile” this function would be something like:
-.. code:: python
+.. code:: ipython2
def poswrd(s, e, d):
return rolldown(*swap(*pop(s, e, d)))
We should be able to directly write out a Python function like:
-.. code:: python
+.. code:: ipython2
def poswrd(stack):
(_, (a, (b, (c, stack)))) = stack
From this stack effect comment it should be possible to construct the
following Python code:
-.. code:: python
+.. code:: ipython2
def F(stack):
(_, (d, (c, ((a, (b, S0)), stack)))) = stack
I’m going to use pairs of tuples of type descriptors, which will be
integers or tuples of type descriptors:
-.. code:: python
+.. code:: ipython2
roll_dn = (1, 2, 3), (2, 3, 1)
``compose()``
~~~~~~~~~~~~~
-.. code:: python
+.. code:: ipython2
def compose(f, g):
``unify()``
~~~~~~~~~~~
-.. code:: python
+.. code:: ipython2
def unify(u, v, s=None):
if s is None:
``update()``
~~~~~~~~~~~~
-.. code:: python
+.. code:: ipython2
def update(s, term):
if not isinstance(term, tuple):
``relabel()``
~~~~~~~~~~~~~
-.. code:: python
+.. code:: ipython2
def relabel(left, right):
return left, _1000(right)
``delabel()``
~~~~~~~~~~~~~
-.. code:: python
+.. code:: ipython2
def delabel(f):
s = {u: i for i, u in enumerate(sorted(_unique(f)))}
stack effect comments and returns their composition (or raises and
exception if they can’t be composed due to type conflicts.)
-.. code:: python
+.. code:: ipython2
def C(f, g):
f, g = relabel(f, g)
Let’s try it out.
-.. code:: python
+.. code:: ipython2
C(pop, swap)
-.. code:: python
+.. code:: ipython2
C(C(pop, swap), roll_dn)
-.. code:: python
+.. code:: ipython2
C(swap, roll_dn)
-.. code:: python
+.. code:: ipython2
C(pop, C(swap, roll_dn))
-.. code:: python
+.. code:: ipython2
poswrd = reduce(C, (pop, swap, roll_dn))
poswrd
manipulate stacks. We use a cons-list of tuples and give the tails their
own numbers. Then everything above already works.
-.. code:: python
+.. code:: ipython2
rest = ((1, 2),), (2,)
cons = (1, 2), ((1, 2),)
-.. code:: python
+.. code:: ipython2
C(poswrd, rest)
0: 0,
}
-.. code:: python
+.. code:: ipython2
F = reduce(C, (pop, swap, roll_dn, rest, rest, cons, cons))
However, if we try to compose e.g. ``cons`` and ``uncons`` it won’t
work:
-.. code:: python
+.. code:: ipython2
uncons = ((1, 2),), (1, 2)
-.. code:: python
+.. code:: ipython2
try:
C(cons, uncons)
the case when both terms are tuples. We just have to add a clause to
deal with this recursively:
-.. code:: python
+.. code:: ipython2
def unify(u, v, s=None):
if s is None:
return s
-.. code:: python
+.. code:: ipython2
C(cons, uncons)
Now consider the Python function we would like to derive:
-.. code:: python
+.. code:: ipython2
def F_python(stack):
(_, (d, (c, ((a, (b, S0)), stack)))) = stack
And compare it to the input stack effect comment tuple we just computed:
-.. code:: python
+.. code:: ipython2
F[0]
And the return tuple
-.. code:: python
+.. code:: ipython2
F[1]
We want to substitute Python identifiers for the integers. I’m going to
repurpose ``joy.parser.Symbol`` class for this:
-.. code:: python
+.. code:: ipython2
from collections import defaultdict
from joy.parser import Symbol
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:: python
+.. code:: ipython2
def doc_from_stack_effect(inputs, outputs):
return '(%s--%s)' % (
underscore suffix distiguishes it from the built-in ``compile()``
function.)
-.. code:: python
+.. code:: ipython2
def compile_(name, f, doc=None):
if doc is None:
Here it is in action:
-.. code:: python
+.. code:: ipython2
source = compile_('F', F)
Compare:
-.. code:: python
+.. code:: ipython2
def F_python(stack):
(_, (d, (c, ((a, (b, S0)), stack)))) = stack
Next steps:
-.. code:: python
+.. code:: ipython2
L = {}
Let’s try it out:
-.. code:: python
+.. code:: ipython2
from notebook_preamble import D, J, V
from joy.library import SimpleFunctionWrapper
-.. code:: python
+.. code:: ipython2
D['F'] = SimpleFunctionWrapper(L['F'])
-.. code:: python
+.. code:: ipython2
J('[4 5 ...] 2 3 1 F')
We can use ``compile_()`` to generate many primitives in the library
from their stack effect comments:
-.. code:: python
+.. code:: ipython2
def defs():
return locals()
-.. code:: python
+.. code:: ipython2
for name, stack_effect_comment in sorted(defs().items()):
print
to establish domain ordering, as well as other handy behaviour that will
make it fairly easy to reuse most of the code above.
-.. code:: python
+.. code:: ipython2
class AnyJoyType(object):
Mess with it a little:
-.. code:: python
+.. code:: ipython2
from itertools import permutations
“Any” types can be specialized to numbers and stacks, but not vice
versa:
-.. code:: python
+.. code:: ipython2
for a, b in permutations((A[0], N[0], S[0]), 2):
print a, '>=', b, '->', a >= b
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:: python
+.. code:: ipython2
for a, b in permutations((A[0], N[0], FloatJoyType(0), IntJoyType(0)), 2):
print a, '>=', b, '->', a >= b
Typing ``sqr``
~~~~~~~~~~~~~~
-.. code:: python
+.. code:: ipython2
dup = (A[1],), (A[1], A[1])
mul = (N[1], N[2]), (N[3],)
-.. code:: python
+.. code:: ipython2
dup
-.. code:: python
+.. code:: ipython2
mul
Re-labeling still works fine:
-.. code:: python
+.. code:: ipython2
foo = relabel(dup, mul)
The ``delabel()`` function needs an overhaul. It now has to keep track
of how many labels of each domain it has “seen”.
-.. code:: python
+.. code:: ipython2
from collections import Counter
return tuple(delabel(inner, seen, c) for inner in f)
-.. code:: python
+.. code:: ipython2
delabel(foo)
``unify()`` version 3
^^^^^^^^^^^^^^^^^^^^^
-.. code:: python
+.. code:: ipython2
def unify(u, v, s=None):
if s is None:
Rewrite the stack effect comments:
-.. code:: python
+.. code:: ipython2
def defs():
return locals()
-.. code:: python
+.. code:: ipython2
DEFS = defs()
-.. code:: python
+.. code:: ipython2
for name, stack_effect_comment in sorted(DEFS.items()):
print name, '=', doc_from_stack_effect(*stack_effect_comment)
uncons = ([a1 .1.] -- a1 [.1.])
-.. code:: python
+.. code:: ipython2
globals().update(DEFS)
Compose ``dup`` and ``mul``
^^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. code:: python
+.. code:: ipython2
C(dup, mul)
Revisit the ``F`` function, works fine.
-.. code:: python
+.. code:: ipython2
F = reduce(C, (pop, swap, rolldown, rest, rest, cons, cons))
F
-.. code:: python
+.. code:: ipython2
print doc_from_stack_effect(*F)
Some otherwise inefficient functions are no longer to be feared. We can
also get the effect of combinators in some limited cases.
-.. code:: python
+.. code:: ipython2
def neato(*funcs):
print doc_from_stack_effect(*reduce(C, funcs))
-.. code:: python
+.. code:: ipython2
# e.g. [swap] dip
neato(rollup, swap, rolldown)
(a1 a2 a3 -- a2 a1 a3)
-.. code:: python
+.. code:: ipython2
# e.g. [popop] dipd
neato(popdd, rolldown, pop)
(a1 a2 a3 a4 -- a3 a4)
-.. code:: python
+.. code:: ipython2
# Reverse the order of the top three items.
neato(rollup, swap)
Because the type labels represent themselves as valid Python identifiers
the ``compile_()`` function doesn’t need to generate them anymore:
-.. code:: python
+.. code:: ipython2
def compile_(name, f, doc=None):
inputs, outputs = f
%s = stack
return %s''' % (name, doc, i, o)
-.. code:: python
+.. code:: ipython2
print compile_('F', F)
But it cannot magically create new functions that involve e.g. math and
such. Note that this is *not* a ``sqr`` function implementation:
-.. code:: python
+.. code:: ipython2
print compile_('sqr', C(dup, mul))
``AnyJoyType`` and ``StackJoyType`` labels in their stack effect
comments. We can write a function to check that:
-.. code:: python
+.. code:: ipython2
from itertools import imap
def compilable(f):
return isinstance(f, tuple) and all(imap(compilable, f)) or stacky(f)
-.. code:: python
+.. code:: ipython2
for name, stack_effect_comment in sorted(defs().items()):
if compilable(stack_effect_comment):
``joy.utils.stack.concat`` work with our stack effect comment cons-list
tuples.)
-.. code:: python
+.. code:: ipython2
def compose(f, g):
(f_in, f_out), (g_in, g_out) = f, g
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:: python
+.. code:: ipython2
def sequence_to_stack(seq, stack=StackJoyType(23)):
for item in seq: stack = item, stack
NEW_DEFS['swaack'] = (S[1], S[0]), (S[0], S[1])
globals().update(NEW_DEFS)
-.. code:: python
+.. code:: ipython2
C(stack, uncons)
-.. code:: python
+.. code:: ipython2
reduce(C, (stack, uncons, uncons))
Clunky junk, but it will suffice for now.
-.. code:: python
+.. code:: ipython2
def doc_from_stack_effect(inputs, outputs):
switch = [False] # Do we need to display the '...' for the rest of the main stack?
a.append(end)
return '[%s]' % ' '.join(a)
-.. code:: python
+.. code:: ipython2
for name, stack_effect_comment in sorted(NEW_DEFS.items()):
print name, '=', doc_from_stack_effect(*stack_effect_comment)
uncons = ([a1 .1.] -- a1 [.1.])
-.. code:: python
+.. code:: ipython2
print ; print doc_from_stack_effect(*stack)
print ; print doc_from_stack_effect(*C(stack, uncons))
(... a1 -- ... a1 [a1 ...])
-.. code:: python
+.. code:: ipython2
print doc_from_stack_effect(*C(ccons, stack))
(... a2 a1 [.1.] -- ... [a2 a1 .1.] [[a2 a1 .1.] ...])
-.. code:: python
+.. code:: ipython2
Q = C(ccons, stack)
This makes the ``compile_()`` function pretty simple as the stack effect
comments are now already in the form needed for the Python code:
-.. code:: python
+.. code:: ipython2
def compile_(name, f, doc=None):
i, o = f
%s = stack
return %s''' % (name, doc, i, o)
-.. code:: python
+.. code:: ipython2
print compile_('Q', Q)
-.. code:: python
+.. code:: ipython2
unstack = (S[1], S[0]), S[1]
enstacken = S[0], (S[0], S[1])
-.. code:: python
+.. code:: ipython2
print doc_from_stack_effect(*unstack)
([.1.] --)
-.. code:: python
+.. code:: ipython2
print doc_from_stack_effect(*enstacken)
(-- [.0.])
-.. code:: python
+.. code:: ipython2
print doc_from_stack_effect(*C(cons, unstack))
(a1 [.1.] -- a1)
-.. code:: python
+.. code:: ipython2
print doc_from_stack_effect(*C(cons, enstacken))
(a1 [.1.] -- [[a1 .1.] .2.])
-.. code:: python
+.. code:: ipython2
C(cons, unstack)
…
-.. code:: python
+.. code:: ipython2
class IntJoyType(NumberJoyType): prefix = 'i'
F = map(FloatJoyType, _R)
I = map(IntJoyType, _R)
-.. code:: python
+.. code:: ipython2
muls = [
((I[2], (I[1], S[0])), (I[3], S[0])),
((F[2], (F[1], S[0])), (F[3], S[0])),
]
-.. code:: python
+.. code:: ipython2
for f in muls:
print doc_from_stack_effect(*f)
(f1 f2 -- f3)
-.. code:: python
+.. code:: ipython2
for f in muls:
try:
(a1 -- a1 a1) (f1 f2 -- f3) (f1 -- f2)
-.. code:: python
+.. code:: ipython2
from itertools import product
def MC(F, G):
return sorted(set(meta_compose(F, G)))
-.. code:: python
+.. code:: ipython2
for f in MC([dup], [mul]):
print doc_from_stack_effect(*f)
(n1 -- n2)
-.. code:: python
+.. code:: ipython2
for f in MC([dup], muls):
print doc_from_stack_effect(*f)
{c: a, d: b, .1.: .0.}
{c: a, d: e, .1.: A* b .0.}
-.. code:: python
+.. code:: ipython2
class KleeneStar(object):
Can now return multiple results…
-.. code:: python
+.. code:: ipython2
def unify(u, v, s=None):
if s is None:
def stacky(thing):
return thing.__class__ in {AnyJoyType, StackJoyType}
-.. code:: python
+.. code:: ipython2
a = (As[1], S[1])
a
-.. code:: python
+.. code:: ipython2
b = (A[1], S[2])
b
-.. code:: python
+.. code:: ipython2
for result in unify(b, a):
print result, '->', update(result, a), update(result, b)
{a1: a10001, s2: (a1*, s1)} -> (a1*, s1) (a10001, (a1*, s1))
-.. code:: python
+.. code:: ipython2
for result in unify(a, b):
print result, '->', update(result, a), update(result, b)
(a1*, s1) [a1*] (a2, (a1*, s1)) [a2 a1*]
-.. code:: python
+.. code:: ipython2
sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0])
([n1* .1.] -- n0)
-.. code:: python
+.. code:: ipython2
f = (N[1], (N[2], (N[3], S[1]))), S[0]
(-- [n1 n2 n3 .1.])
-.. code:: python
+.. code:: ipython2
for result in unify(sum_[0], f):
print result, '->', update(result, sum_[1])
This function has to be modified to yield multiple results.
-.. code:: python
+.. code:: ipython2
def compose(f, g):
(f_in, f_out), (g_in, g_out) = f, g
-.. code:: python
+.. code:: ipython2
def meta_compose(F, G):
for f, g in product(F, G):
for fg in compose(f, g):
yield delabel(fg)
-.. code:: python
+.. code:: ipython2
for f in MC([dup], muls):
print doc_from_stack_effect(*f)
(i1 -- i2)
-.. code:: python
+.. code:: ipython2
([n1* .1.] -- [n1* .1.] n1)
-.. code:: python
+.. code:: ipython2
(n1 [n1* .1.] -- n2)
-.. code:: python
+.. code:: ipython2
sum_ = (((N[1], (Ns[1], S[1])), S[0]), (N[0], S[0]))
print doc_from_stack_effect(*cons),
(a1 [.1.] -- [a1 .1.]) ([n1 n1* .1.] -- n0) (n1 [n1* .1.] -- n2)
-.. code:: python
+.. code:: ipython2
a = (A[4], (As[1], (A[3], S[1])))
a
-.. code:: python
+.. code:: ipython2
b = (A[1], (A[2], S[2]))
b
-.. code:: python
+.. code:: ipython2
for result in unify(b, a):
print result
{a1: a4, s2: (a1*, (a3, s1)), a2: a10003}
-.. code:: python
+.. code:: ipython2
for result in unify(a, b):
print result
and be used by the hybrid inferencer/interpreter. They have to store a
name and a list of stack effects.
-.. code:: python
+.. code:: ipython2
class FunctionJoyType(AnyJoyType):
For non-combinator functions the stack effects list contains stack
effect comments (represented by pairs of cons-lists as described above.)
-.. code:: python
+.. code:: ipython2
class SymbolJoyType(FunctionJoyType):
prefix = 'F'
For combinators the list contains Python functions.
-.. code:: python
+.. code:: ipython2
class CombinatorJoyType(FunctionJoyType):
For simple combinators that have only one effect (like ``dip``) you only
need one function and it can be the combinator itself.
-.. code:: python
+.. code:: ipython2
import joy.library
have to write functions that each implement the action of one of the
effects.
-.. code:: python
+.. code:: ipython2
def branch_true(stack, expression, dictionary):
(then, (else_, (flag, stack))) = stack
losing useful information. This was a straightforward, if awkward,
modification to the call structure of ``meta_compose()`` et. al.
-.. code:: python
+.. code:: ipython2
ID = S[0], S[0] # Identity function.
``SymbolJoyType`` objects, and some combinators. Here is an example of
output from the current code :
-.. code:: python
+.. code:: ipython2
1/0 # (Don't try to run this cell! It's not going to work. This is "read only" code heh..)
Anyhow, type *checking* is a few easy steps away.
-.. code:: python
+.. code:: ipython2
def _ge(self, other):
return (issubclass(other.__class__, self.__class__)