Implementation
--------------
-.. code:: ipython2
+.. code:: python
from functools import partial as curry
from itertools import product
The empty set and the set of just the empty string.
-.. code:: ipython2
+.. code:: python
phi = frozenset() # ϕ
y = frozenset({''}) # λ
I chose the names ``O`` and ``l`` (uppercase “o” and lowercase “L”) to
look like ``0`` and ``1`` (zero and one) respectively.
-.. code:: ipython2
+.. code:: python
syms = O, l = frozenset({'0'}), frozenset({'1'})
Where ``R`` and ``S`` stand for *regular expressions*.
-.. code:: ipython2
+.. code:: python
AND, CONS, KSTAR, NOT, OR = 'and cons * not or'.split() # Tags are just strings.
String Representation of RE Datastructures
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def stringy(re):
'''
I = (0|1)*
-.. code:: ipython2
+.. code:: python
I = (KSTAR, (OR, O, l))
-.. code:: ipython2
+.. code:: python
print stringy(I)
Note that it contains one of everything.
-.. code:: ipython2
+.. code:: python
a = (CONS, I, (CONS, l, (CONS, l, (CONS, l, I))))
b = (CONS, I, (CONS, O, l))
c = (CONS, l, (KSTAR, l))
it = (AND, a, (NOT, (OR, b, c)))
-.. code:: ipython2
+.. code:: python
print stringy(it)
Let’s get that auxiliary predicate function ``δ`` out of the way.
-.. code:: ipython2
+.. code:: python
def nully(R):
'''
but does waaaay too much work because the expressions grow each
derivation.
-.. code:: ipython2
+.. code:: python
def D(symbol):
Compaction Rules
~~~~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def _compaction_rule(relation, one, zero, a, b):
return (
An elegant symmetry.
-.. code:: ipython2
+.. code:: python
# R ∧ I = I ∧ R = R
# R ∧ ϕ = ϕ ∧ R = ϕ
computed. RE datastructures are immutable and the ``derv()`` functions
are *pure* so this is fine.
-.. code:: ipython2
+.. code:: python
class Memo(object):
This version uses the rules above to perform compaction. It keeps the
expressions from growing too large.
-.. code:: ipython2
+.. code:: python
def D_compaction(symbol):
(FIXME: redo.)
-.. code:: ipython2
+.. code:: python
o, z = D_compaction('0'), D_compaction('1')
REs = set()
in the way that the ``.111.`` on the left-hand side of the ``&``
disappears once it has been matched.
-.. code:: ipython2
+.. code:: python
from collections import defaultdict
from pprint import pprint
from string import ascii_lowercase
-.. code:: ipython2
+.. code:: python
d0, d1 = D_compaction('0'), D_compaction('1')
``explore()``
~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def explore(re):
return table, accepting
-.. code:: ipython2
+.. code:: python
table, accepting = explore(it)
table
-.. code:: ipython2
+.. code:: python
accepting
Once we have the FSM table and the set of accepting states we can
generate the diagram above.
-.. code:: ipython2
+.. code:: python
_template = '''\
digraph finite_state_machine {
)
)
-.. code:: ipython2
+.. code:: python
print make_graph(table, accepting)
Python has no GOTO statement but we can fake it with a “trampoline”
function.
-.. code:: ipython2
+.. code:: python
def trampoline(input_, jump_from, accepting):
I = iter(input_)
Little helpers to process the iterator of our data (a “stream” of “1”
and “0” characters, not bits.)
-.. code:: ipython2
+.. code:: python
getch = lambda I: int(next(I))
branches in assembly and that the state names are branch destination
labels.)
-.. code:: ipython2
+.. code:: python
a = lambda I: c if getch(I) else b
b = lambda I: _0(I) or d
``h = g`` and we could eliminate one in the code but ``h`` is an
accepting state and ``g`` isn’t.
-.. code:: ipython2
+.. code:: python
def acceptable(input_):
return trampoline(input_, a, {h, i})
-.. code:: ipython2
+.. code:: python
for n in range(2**5):
s = bin(n)[2:]
Find the sum of all the multiples of 3 or 5 below 1000.
-.. code:: ipython2
+.. code:: python
from notebook_preamble import J, V, define
Let's create a predicate that returns ``True`` if a number is a multiple
of 3 or 5 and ``False`` otherwise.
-.. code:: ipython2
+.. code:: python
define('P == [3 % not] dupdip 5 % not or')
-.. code:: ipython2
+.. code:: python
V('80 P')
PE1.1 == + [+] dupdip
-.. code:: ipython2
+.. code:: python
define('PE1.1 == + [+] dupdip')
-.. code:: ipython2
+.. code:: python
V('0 0 3 PE1.1')
3 3 .
-.. code:: ipython2
+.. code:: python
V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
How many multiples to sum?
^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. code:: ipython2
+.. code:: python
1000 / 15
-.. code:: ipython2
+.. code:: python
66 * 15
-.. code:: ipython2
+.. code:: python
1000 - 990
We only want the terms *less than* 1000.
-.. code:: ipython2
+.. code:: python
999 - 990
That means we want to run the full list of numbers sixty-six times to
get to 990 and then the first four numbers 3 2 1 3 to get to 999.
-.. code:: ipython2
+.. code:: python
define('PE1 == 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
-.. code:: ipython2
+.. code:: python
J('PE1')
3 2 1 3 1 2 3
0b 11 10 01 11 01 10 11 == 14811
-.. code:: ipython2
+.. code:: python
0b11100111011011
-.. code:: ipython2
+.. code:: python
define('PE1.2 == [3 & PE1.1] dupdip 2 >>')
-.. code:: ipython2
+.. code:: python
V('0 0 14811 PE1.2')
3 3 3702 .
-.. code:: ipython2
+.. code:: python
V('3 3 3702 PE1.2')
8 5 925 .
-.. code:: ipython2
+.. code:: python
V('0 0 14811 7 [PE1.2] times pop')
And so we have at last:
-.. code:: ipython2
+.. code:: python
define('PE1 == 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
-.. code:: ipython2
+.. code:: python
J('PE1')
14811 n [PE1.2] times pop
n 14811 swap [PE1.2] times pop
-.. code:: ipython2
+.. code:: python
define('PE1.3 == 14811 swap [PE1.2] times pop')
Now we can simplify the definition above:
-.. code:: ipython2
+.. code:: python
define('PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
-.. code:: ipython2
+.. code:: python
J('PE1')
generator that can be repeatedly driven by the ``x`` combinator to
produce a stream of the seven numbers repeating over and over again.
-.. code:: ipython2
+.. code:: python
define('PE1.terms == [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
-.. code:: ipython2
+.. code:: python
J('PE1.terms 21 [x] times')
We know from above that we need sixty-six times seven then four more
terms to reach up to but not over one thousand.
-.. code:: ipython2
+.. code:: python
J('7 66 * 4 +')
Here they are...
~~~~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
J('PE1.terms 466 [x] times pop')
...and they do sum to 999.
~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
J('[PE1.terms 466 [x] times pop] run sum')
``pop`` the generator and the counter from the stack when we're done,
leaving just the sum.
-.. code:: ipython2
+.. code:: python
J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
Consider finding the sum of the positive integers less than or equal to
ten.
-.. code:: ipython2
+.. code:: python
J('[10 9 8 7 6 5 4 3 2 1] sum')
(The formula also works for odd values of N, I'll leave that to you if
you want to work it out or you can take my word for it.)
-.. code:: ipython2
+.. code:: python
define('F == dup ++ * 2 floordiv')
-.. code:: ipython2
+.. code:: python
V('10 F')
If we reverse one of these two blocks and sum pairs...
-.. code:: ipython2
+.. code:: python
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
[[978 15] [980 12] [981 10] [984 9] [985 6] [987 5] [990 3]]
-.. code:: ipython2
+.. code:: python
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
(Interesting that the sequence of seven numbers appears again in the
rightmost digit of each term.)
-.. code:: ipython2
+.. code:: python
J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
So we can give the "sum of all the multiples of 3 or 5 below 1000" like
so:
-.. code:: ipython2
+.. code:: python
J('6945 33 * [993 995 996 999] cons sum')
Cf. jp-reprod.html
-.. code:: ipython2
+.. code:: python
from notebook_preamble import J, V, define
Let’s try it:
-.. code:: ipython2
+.. code:: python
V('[0 swap [dup ++] dip rest cons] x')
After one application of ``x`` the quoted program contains ``1`` and
``0`` is below it on the stack.
-.. code:: ipython2
+.. code:: python
J('[0 swap [dup ++] dip rest cons] x x x x x pop')
``direco``
----------
-.. code:: ipython2
+.. code:: python
define('direco == dip rest cons')
-.. code:: ipython2
+.. code:: python
V('[0 swap [dup ++] direco] x')
G == [direco] cons [swap] swap concat cons
G == [direco] cons [swap] swoncat cons
-.. code:: ipython2
+.. code:: python
define('G == [direco] cons [swap] swoncat cons')
Let’s try it out:
-.. code:: ipython2
+.. code:: python
J('0 [dup ++] G')
[0 swap [dup ++] direco]
-.. code:: ipython2
+.. code:: python
J('0 [dup ++] G x x x pop')
Powers of 2
~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
J('1 [dup 1 <<] G x x x x x x x x x pop')
If we have one of these quoted programs we can drive it using ``times``
with the ``x`` combinator.
-.. code:: ipython2
+.. code:: python
J('23 [dup ++] G 5 [x] times')
And pick them off by masking with 3 (binary 11) and then shifting the
int right two bits.
-.. code:: ipython2
+.. code:: python
define('PE1.1 == dup [3 &] dip 2 >>')
-.. code:: ipython2
+.. code:: python
V('14811 PE1.1')
If we plug ``14811`` and ``[PE1.1]`` into our generator form…
-.. code:: ipython2
+.. code:: python
J('14811 [PE1.1] G')
…we get a generator that works for seven cycles before it reaches zero:
-.. code:: ipython2
+.. code:: python
J('[14811 swap [PE1.1] direco] 7 [x] times')
We need a function that checks if the int has reached zero and resets it
if so.
-.. code:: ipython2
+.. code:: python
define('PE1.1.check == dup [pop 14811] [] branch')
-.. code:: ipython2
+.. code:: python
J('14811 [PE1.1.check PE1.1] G')
[14811 swap [PE1.1.check PE1.1] direco]
-.. code:: ipython2
+.. code:: python
J('[14811 swap [PE1.1.check PE1.1] direco] 21 [x] times')
five less than 1000. It’s worked out that we need to use all seven
numbers sixty-six times and then four more.
-.. code:: ipython2
+.. code:: python
J('7 66 * 4 +')
If we drive our generator 466 times and sum the stack we get 999.
-.. code:: ipython2
+.. code:: python
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times')
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 [57 swap [PE1.1.check PE1.1] direco]
-.. code:: ipython2
+.. code:: python
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times pop enstacken sum')
Project Euler Problem One
-------------------------
-.. code:: ipython2
+.. code:: python
define('PE1.2 == + dup [+] dip')
Now we can add ``PE1.2`` to the quoted program given to ``G``.
-.. code:: ipython2
+.. code:: python
J('0 0 0 [PE1.1.check PE1.1] G 466 [x [PE1.2] dip] times popop')
F == + [popdd over] cons infra uncons
fib_gen == [1 1 F]
-.. code:: ipython2
+.. code:: python
define('fib == + [popdd over] cons infra uncons')
-.. code:: ipython2
+.. code:: python
define('fib_gen == [1 1 fib]')
-.. code:: ipython2
+.. code:: python
J('fib_gen 10 [x] times')
function that adds a term in the sequence to a sum if it is even, and
``pop``\ s it otherwise.
-.. code:: ipython2
+.. code:: python
define('PE2.1 == dup 2 % [+] [pop] branch')
And a predicate function that detects when the terms in the series
“exceed four million”.
-.. code:: ipython2
+.. code:: python
define('>4M == 4000000 >')
generates terms in the Fibonacci sequence until they exceed four million
and sums the even ones.
-.. code:: ipython2
+.. code:: python
define('PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec')
-.. code:: ipython2
+.. code:: python
J('PE2')
Every third term is even.
-.. code:: ipython2
+.. code:: python
J('[1 0 fib] x x x') # To start the sequence with 1 1 2 3 instead of 1 2 3.
Drive the generator three times and ``popop`` the two odd terms.
-.. code:: ipython2
+.. code:: python
J('[1 0 fib] x x x [popop] dipd')
2 [3 2 fib]
-.. code:: ipython2
+.. code:: python
define('PE2.2 == x x x [popop] dipd')
-.. code:: ipython2
+.. code:: python
J('[1 0 fib] 10 [PE2.2] times')
Replace ``x`` with our new driver function ``PE2.2`` and start our
``fib`` generator at ``1 0``.
-.. code:: ipython2
+.. code:: python
J('0 [1 0 fib] PE2.2 [pop >4M] [popop] [[PE2.1] dip PE2.2] primrec')
An Interesting Variation
------------------------
-.. code:: ipython2
+.. code:: python
define('codireco == cons dip rest cons')
-.. code:: ipython2
+.. code:: python
V('[0 [dup ++] codireco] x')
0 [1 [dup ++] codireco] .
-.. code:: ipython2
+.. code:: python
define('G == [codireco] cons cons')
-.. code:: ipython2
+.. code:: python
J('230 [dup ++] G 5 [x] times pop')
Examples
~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
joy.parser.text_to_expression('1 2 3 4 5') # A simple sequence.
(1, (2, (3, (4, (5, ())))))
-.. code:: ipython2
+.. code:: python
joy.parser.text_to_expression('[1 2 3] 4 5') # Three items, the first is a list with three items
((1, (2, (3, ()))), (4, (5, ())))
-.. code:: ipython2
+.. code:: python
joy.parser.text_to_expression('1 23 ["four" [-5.0] cons] 8888') # A mixed bag. cons is
# a Symbol, no lookup at
-.. code:: ipython2
+.. code:: python
joy.parser.text_to_expression('[][][][][]') # Five empty lists.
-.. code:: ipython2
+.. code:: python
joy.parser.text_to_expression('[[[[[]]]]]') # Five nested lists.
Many of the functions are defined in Python, like ``dip``:
-.. code:: ipython2
+.. code:: python
print inspect.getsource(joy.library.dip)
pushes its body expression onto the pending expression (the
continuation) and returns control to the interpreter.
-.. code:: ipython2
+.. code:: python
print joy.library.definitions
a contingent existence predicated on the disciplined use of these
functions on otherwise undistinguished Joy datastructures.)
-.. code:: ipython2
+.. code:: python
from notebook_preamble import D, J, V, define, DefinitionWrapper
Tree-new == swap [[] []] cons cons
-.. code:: ipython2
+.. code:: python
define('Tree-new == swap [[] []] cons cons')
-.. code:: ipython2
+.. code:: python
J('"v" "k" Tree-new')
P < == pop roll> pop first <
P == pop roll> pop first
-.. code:: ipython2
+.. code:: python
define('P == pop roll> pop first')
-.. code:: ipython2
+.. code:: python
J('["old_key" 23 [] []] 17 "new_key" ["..."] P')
T == cons cons [dipdd] cons infra
-.. code:: ipython2
+.. code:: python
define('T == cons cons [dipdd] cons infra')
-.. code:: ipython2
+.. code:: python
J('["old_k" "old_value" "left" "right"] "new_value" "new_key" ["Tree-add"] T')
[key_n value_n left right] value key [Tree-add] E
[key_n value_n left right] value key [Tree-add] [P <] [Te] [Ee] ifte
-.. code:: ipython2
+.. code:: python
define('E == [P <] [Te] [Ee] ifte')
Te == cons cons [dipd] cons infra
-.. code:: ipython2
+.. code:: python
define('Te == cons cons [dipd] cons infra')
-.. code:: ipython2
+.. code:: python
J('["old_k" "old_value" "left" "right"] "new_value" "new_key" ["Tree-add"] Te')
key new_value [ left right] cons cons
[key new_value left right]
-.. code:: ipython2
+.. code:: python
define('Ee == pop swap roll< rest rest cons cons')
-.. code:: ipython2
+.. code:: python
J('["k" "old_value" "left" "right"] "new_value" "k" ["Tree-add"] Ee')
Tree-add == [popop not] [[pop] dipd Tree-new] [] [R] genrec
-.. code:: ipython2
+.. code:: python
define('Tree-add == [popop not] [[pop] dipd Tree-new] [] [[P >] [T] [E] ifte] genrec')
Examples
~~~~~~~~
-.. code:: ipython2
+.. code:: python
J('[] 23 "b" Tree-add') # Initial
['b' 23 [] []]
-.. code:: ipython2
+.. code:: python
J('["b" 23 [] []] 88 "c" Tree-add') # Greater than
['b' 23 [] ['c' 88 [] []]]
-.. code:: ipython2
+.. code:: python
J('["b" 23 [] []] 88 "a" Tree-add') # Less than
['b' 23 ['a' 88 [] []] []]
-.. code:: ipython2
+.. code:: python
J('["b" 23 [] []] 88 "b" Tree-add') # Equal to
['b' 88 [] []]
-.. code:: ipython2
+.. code:: python
J('[] 23 "b" Tree-add 88 "a" Tree-add 44 "c" Tree-add') # Series.
['b' 23 ['a' 88 [] []] ['c' 44 [] []]]
-.. code:: ipython2
+.. code:: python
J('[] [[23 "b"] [88 "a"] [44 "c"]] [i Tree-add] step')
------------------------- a < b
L
-.. code:: ipython2
+.. code:: python
J("1 0 ['G'] ['E'] ['L'] cmp")
'G'
-.. code:: ipython2
+.. code:: python
J("1 1 ['G'] ['E'] ['L'] cmp")
'E'
-.. code:: ipython2
+.. code:: python
J("0 1 ['G'] ['E'] ['L'] cmp")
P == over [popop popop first] nullary
-.. code:: ipython2
+.. code:: python
define('P == over [popop popop first] nullary')
Tree-add == [popop not] [[pop] dipd Tree-new] [] [P [T] [Ee] [Te] cmp] genrec
-.. code:: ipython2
+.. code:: python
define('Tree-add == [popop not] [[pop] dipd Tree-new] [] [P [T] [Ee] [Te] cmp] genrec')
-.. code:: ipython2
+.. code:: python
J('[] 23 "b" Tree-add 88 "a" Tree-add 44 "c" Tree-add') # Still works.
Tree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec
-.. code:: ipython2
+.. code:: python
define('Tree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec')
Examples
~~~~~~~~
-.. code:: ipython2
+.. code:: python
J('[] [foo] Tree-iter') # It doesn't matter what F is as it won't be used.
-.. code:: ipython2
+.. code:: python
J("['b' 23 ['a' 88 [] []] ['c' 44 [] []]] [first] Tree-iter")
'b' 'a' 'c'
-.. code:: ipython2
+.. code:: python
J("['b' 23 ['a' 88 [] []] ['c' 44 [] []]] [second] Tree-iter")
`:math:`O(\log_2 N)` <https://en.wikipedia.org/wiki/Binary_search_tree#cite_note-2>`__
time.
-.. code:: ipython2
+.. code:: python
J('[] [3 9 5 2 8 6 7 8 4] [0 swap Tree-add] step')
[3 0 [2 0 [] []] [9 0 [5 0 [4 0 [] []] [8 0 [6 0 [] [7 0 [] []]] []]] []]]
-.. code:: ipython2
+.. code:: python
define('to_set == [] swap [0 swap Tree-add] step')
-.. code:: ipython2
+.. code:: python
J('[3 9 5 2 8 6 7 8 4] to_set')
And with that we can write a little program ``unique`` to remove
duplicate items from a list.
-.. code:: ipython2
+.. code:: python
define('unique == [to_set [first] Tree-iter] cons run')
-.. code:: ipython2
+.. code:: python
J('[3 9 3 5 2 9 8 8 8 6 2 7 8 4 3] unique') # Filter duplicate items.
Now we can sort sequences.
-.. code:: ipython2
+.. code:: python
#define('Tree-iter-order == [not] [pop] [dup third] [[cons dip] dupdip [[first] dupdip] dip [rest rest rest first] dip i] genrec')
-.. code:: ipython2
+.. code:: python
J('[3 9 5 2 8 6 7 8 4] to_set Tree-iter-order')
Tree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec
-.. code:: ipython2
+.. code:: python
# I don't want to deal with name conflicts with the above so I'm inlining everything here.
# The original Joy system has "hide" which is a meta-command which allows you to use named
] genrec
''')
-.. code:: ipython2
+.. code:: python
J('["gary" 23 [] []] "mike" [popd " not in tree" +] Tree-get')
'mike not in tree'
-.. code:: ipython2
+.. code:: python
J('["gary" 23 [] []] "gary" [popop "err"] Tree-get')
23
-.. code:: ipython2
+.. code:: python
J('''
2
-.. code:: ipython2
+.. code:: python
J('''
By the standards of the code I’ve written so far, this is a *huge* Joy
program.
-.. code:: ipython2
+.. code:: python
DefinitionWrapper.add_definitions('''
first_two == uncons uncons pop
Tree-Delete == [pop not] [pop] [R0] [R1] genrec
''', D)
-.. code:: ipython2
+.. code:: python
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'c' Tree-Delete ")
['a' 23 [] ['b' 88 [] []]]
-.. code:: ipython2
+.. code:: python
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'b' Tree-Delete ")
['a' 23 [] ['c' 44 [] []]]
-.. code:: ipython2
+.. code:: python
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'a' Tree-Delete ")
['b' 88 [] ['c' 44 [] []]]
-.. code:: ipython2
+.. code:: python
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'der' Tree-Delete ")
['a' 23 [] ['b' 88 [] ['c' 44 [] []]]]
-.. code:: ipython2
+.. code:: python
J('[] [4 2 3 1 6 7 5 ] [0 swap Tree-add] step')
[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]]
-.. code:: ipython2
+.. code:: python
J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 3 Tree-Delete ")
[4 0 [2 0 [1 0 [] []] []] [6 0 [5 0 [] []] [7 0 [] []]]]
-.. code:: ipython2
+.. code:: python
J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 4 Tree-Delete ")
-.. code:: ipython2
+.. code:: python
from notebook_preamble import J, V, define
The three arguments are to the left, so we can “chop off” everything to
the right and say it’s the definition of the ``quadratic`` function:
-.. code:: ipython2
+.. code:: python
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2')
Let’s try it out:
-.. code:: ipython2
+.. code:: python
J('3 1 1 quadratic')
by incorporating the values on the stack. Then that program runs and you
get the results. This is pretty typical of Joy code.
-.. code:: ipython2
+.. code:: python
V('-5 1 4 quadratic')
-.. code:: ipython2
+.. code:: python
from notebook_preamble import D, DefinitionWrapper, J, V, define
It may be helpful to see this function implemented in imperative Python
code.
-.. code:: ipython2
+.. code:: python
def hylomorphism(c, F, P, G):
'''Return a hylomorphism function H.'''
hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec
-.. code:: ipython2
+.. code:: python
define('hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec')
- ``[G]`` is ``[-- dup]``
- ``[F]`` is ``[+]``
-.. code:: ipython2
+.. code:: python
define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
Let’s try it:
-.. code:: ipython2
+.. code:: python
J('5 triangular_number')
10
-.. code:: ipython2
+.. code:: python
J('[0 1 2 3 4 5 6] [triangular_number] map')
H1 == [P] [pop c] [G] [dip F] genrec
== [0 <=] [pop []] [-- dup] [dip swons] genrec
-.. code:: ipython2
+.. code:: python
define('range == [0 <=] [] [-- dup] [swons] hylomorphism')
-.. code:: ipython2
+.. code:: python
J('5 range')
H2 == c swap [P] [pop] [G [F] dip] primrec
== [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
-.. code:: ipython2
+.. code:: python
define('range_reverse == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec')
-.. code:: ipython2
+.. code:: python
J('5 range_reverse')
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
== [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
-.. code:: ipython2
+.. code:: python
define('ranger == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec')
-.. code:: ipython2
+.. code:: python
J('5 ranger')
H4 == c swap [P] [pop] [[F] dupdip G ] primrec
== [] swap [0 <=] [pop] [[swons] dupdip --] primrec
-.. code:: ipython2
+.. code:: python
define('ranger_reverse == [] swap [0 <=] [pop] [[swons] dupdip --] primrec')
-.. code:: ipython2
+.. code:: python
J('5 ranger_reverse')
C == [not] c [uncons swap] [F] hylomorphism
-.. code:: ipython2
+.. code:: python
define('swuncons == uncons swap') # Awkward name.
sum == [not] 0 [swuncons] [+] hylomorphism
-.. code:: ipython2
+.. code:: python
define('sum == [not] 0 [swuncons] [+] hylomorphism')
-.. code:: ipython2
+.. code:: python
J('[5 4 3 2 1] sum')
The ``step`` combinator will usually be better to use than
``catamorphism``.
-.. code:: ipython2
+.. code:: python
J('[step] help')
-.. code:: ipython2
+.. code:: python
define('sum == 0 swap [+] step')
-.. code:: ipython2
+.. code:: python
J('[5 4 3 2 1] sum')
G == --
P == 1 <=
-.. code:: ipython2
+.. code:: python
define('factorial == 1 swap [1 <=] [pop] [[*] dupdip --] primrec')
-.. code:: ipython2
+.. code:: python
J('5 factorial')
G == rest dup
P == not
-.. code:: ipython2
+.. code:: python
define('tails == [] swap [not] [pop] [rest dup [swons] dip] primrec')
-.. code:: ipython2
+.. code:: python
J('[1 2 3] tails')
function to the dictionary is a meta-interpreter action, you have to do
it in Python, not Joy.
-.. code:: ipython2
+.. code:: python
from notebook_preamble import D, J, V
A long trace
------------
-.. code:: ipython2
+.. code:: python
V('[23 18] average')
``size`` we can use a “compiled” version hand-written in Python to speed
up evaluation and make the trace more readable.
-.. code:: ipython2
+.. code:: python
from joy.library import SimpleFunctionWrapper
from joy.utils.stack import iter_stack
Now we replace the old version in the dictionary with the new version,
and re-evaluate the expression.
-.. code:: ipython2
+.. code:: python
D['size'] = size
You can see that ``size`` now executes in a single step.
-.. code:: ipython2
+.. code:: python
V('[23 18] average')
Define ``treestep``
-------------------
-.. code:: ipython2
+.. code:: python
from notebook_preamble import D, J, V, define, DefinitionWrapper
-.. code:: ipython2
+.. code:: python
DefinitionWrapper.add_definitions('''
sumtree == [pop 0] [] [sum +] treestep
-.. code:: ipython2
+.. code:: python
define('sumtree == [pop 0] [] [sum +] treestep')
------------------------------------
0
-.. code:: ipython2
+.. code:: python
J('[] sumtree') # Empty tree.
n m +
n+m
-.. code:: ipython2
+.. code:: python
J('[23] sumtree') # No child trees.
23
-.. code:: ipython2
+.. code:: python
J('[23 []] sumtree') # Child tree, empty.
23
-.. code:: ipython2
+.. code:: python
J('[23 [2 [4]] [3]] sumtree') # Non-empty child trees.
32
-.. code:: ipython2
+.. code:: python
J('[23 [2 [8] [9]] [3] [4 []]] sumtree') # Etc...
49
-.. code:: ipython2
+.. code:: python
J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep') # Alternate "spelling".
49
-.. code:: ipython2
+.. code:: python
J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep') # Replace each node.
[23 [23 [23] [23]] [23] [23 []]]
-.. code:: ipython2
+.. code:: python
J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')
[1 [1 [1] [1]] [1] [1 []]]
-.. code:: ipython2
+.. code:: python
J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')
6
-.. code:: ipython2
+.. code:: python
J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function.
6
-.. code:: ipython2
+.. code:: python
J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function.
This doesn’t quite work:
-.. code:: ipython2
+.. code:: python
J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] ["B"] [first] [i] treestep')
[] [first] [flatten cons] treestep
-.. code:: ipython2
+.. code:: python
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [first] [flatten cons] treestep')
[] [i roll< swons concat] [first] treestep
-.. code:: ipython2
+.. code:: python
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [uncons pop] [i roll< swons concat] treestep')
[key value] N [left right] [K] C
-.. code:: ipython2
+.. code:: python
J('[["key" "value"] ["left"] ["right"] ] ["B"] ["N"] ["C"] treegrind')
Iteration through the nodes
-.. code:: ipython2
+.. code:: python
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] ["N"] [step] treegrind')
Sum the nodes’ keys.
-.. code:: ipython2
+.. code:: python
J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] [first +] [step] treegrind')
Rebuild the tree using ``map`` (imitating ``treestep``.)
-.. code:: ipython2
+.. code:: python
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [[100 +] infra] [map cons] treegrind')
To me, that seems simpler than the ``genrec`` version.
-.. code:: ipython2
+.. code:: python
DefinitionWrapper.add_definitions('''
''', D)
-.. code:: ipython2
+.. code:: python
J('''\
15
-.. code:: ipython2
+.. code:: python
J('''\
Type Checking
=============
-.. code:: ipython2
+.. code:: python
import logging, sys
level=logging.INFO,
)
-.. code:: ipython2
+.. code:: python
from joy.utils.types import (
doc_from_stack_effect,
JoyTypeError,
)
-.. code:: ipython2
+.. code:: python
D = FUNCTIONS.copy()
del D['product']
An Example
----------
-.. code:: ipython2
+.. code:: python
fi, fo = infer(pop, swap, rolldown, rrest, ccons)[0]
40 ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) ∘
-.. code:: ipython2
+.. code:: python
print doc_from_stack_effect(fi, fo)
([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
-.. code:: ipython2
+.. code:: python
from joy.parser import text_to_expression
from joy.utils.stack import stack_to_string
-.. code:: ipython2
+.. code:: python
e = text_to_expression('0 1 2 [3 4]') # reverse order
print stack_to_string(e)
[3 4] 2 1 0
-.. code:: ipython2
+.. code:: python
u = unify(e, fi)[0]
u
-.. code:: ipython2
+.. code:: python
g = reify(u, (fi, fo))
print doc_from_stack_effect(*g)
Unification Works “in Reverse”
------------------------------
-.. code:: ipython2
+.. code:: python
e = text_to_expression('[2 3]')
-.. code:: ipython2
+.. code:: python
u = unify(e, fo)[0] # output side, not input side
u
-.. code:: ipython2
+.. code:: python
g = reify(u, (fi, fo))
print doc_from_stack_effect(*g)
Failing a Check
---------------
-.. code:: ipython2
+.. code:: python
fi, fo = infer(dup, mul)[0]
31 (i1 -- i2) ∘
-.. code:: ipython2
+.. code:: python
e = text_to_expression('"two"')
print stack_to_string(e)
'two'
-.. code:: ipython2
+.. code:: python
try:
unify(e, fi)
The simplest way to “compile” this function would be something like:
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
def poswrd(stack):
(_, (a, (b, (c, stack)))) = stack
From this stack effect comment it should be possible to construct the
following Python code:
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
roll_dn = (1, 2, 3), (2, 3, 1)
``compose()``
~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def compose(f, g):
``unify()``
~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def unify(u, v, s=None):
if s is None:
``update()``
~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def update(s, term):
if not isinstance(term, tuple):
``relabel()``
~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
def relabel(left, right):
return left, _1000(right)
``delabel()``
~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
def C(f, g):
f, g = relabel(f, g)
Let’s try it out.
-.. code:: ipython2
+.. code:: python
C(pop, swap)
-.. code:: ipython2
+.. code:: python
C(C(pop, swap), roll_dn)
-.. code:: ipython2
+.. code:: python
C(swap, roll_dn)
-.. code:: ipython2
+.. code:: python
C(pop, C(swap, roll_dn))
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
rest = ((1, 2),), (2,)
cons = (1, 2), ((1, 2),)
-.. code:: ipython2
+.. code:: python
C(poswrd, rest)
0: 0,
}
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
uncons = ((1, 2),), (1, 2)
-.. code:: ipython2
+.. code:: python
try:
C(cons, uncons)
the case when both terms are tuples. We just have to add a clause to
deal with this recursively:
-.. code:: ipython2
+.. code:: python
def unify(u, v, s=None):
if s is None:
return s
-.. code:: ipython2
+.. code:: python
C(cons, uncons)
Now consider the Python function we would like to derive:
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
F[0]
And the return tuple
-.. code:: ipython2
+.. code:: python
F[1]
We want to substitute Python identifiers for the integers. I’m going to
repurpose ``joy.parser.Symbol`` class for this:
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
def doc_from_stack_effect(inputs, outputs):
return '(%s--%s)' % (
underscore suffix distiguishes it from the built-in ``compile()``
function.)
-.. code:: ipython2
+.. code:: python
def compile_(name, f, doc=None):
if doc is None:
Here it is in action:
-.. code:: ipython2
+.. code:: python
source = compile_('F', F)
Compare:
-.. code:: ipython2
+.. code:: python
def F_python(stack):
(_, (d, (c, ((a, (b, S0)), stack)))) = stack
Next steps:
-.. code:: ipython2
+.. code:: python
L = {}
Let’s try it out:
-.. code:: ipython2
+.. code:: python
from notebook_preamble import D, J, V
from joy.library import SimpleFunctionWrapper
-.. code:: ipython2
+.. code:: python
D['F'] = SimpleFunctionWrapper(L['F'])
-.. code:: ipython2
+.. code:: python
J('[4 5 ...] 2 3 1 F')
We can use ``compile_()`` to generate many primitives in the library
from their stack effect comments:
-.. code:: ipython2
+.. code:: python
def defs():
return locals()
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
class AnyJoyType(object):
Mess with it a little:
-.. code:: ipython2
+.. code:: python
from itertools import permutations
“Any” types can be specialized to numbers and stacks, but not vice
versa:
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
for a, b in permutations((A[0], N[0], FloatJoyType(0), IntJoyType(0)), 2):
print a, '>=', b, '->', a >= b
Typing ``sqr``
~~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: python
dup = (A[1],), (A[1], A[1])
mul = (N[1], N[2]), (N[3],)
-.. code:: ipython2
+.. code:: python
dup
-.. code:: ipython2
+.. code:: python
mul
Re-labeling still works fine:
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
from collections import Counter
return tuple(delabel(inner, seen, c) for inner in f)
-.. code:: ipython2
+.. code:: python
delabel(foo)
``unify()`` version 3
^^^^^^^^^^^^^^^^^^^^^
-.. code:: ipython2
+.. code:: python
def unify(u, v, s=None):
if s is None:
Rewrite the stack effect comments:
-.. code:: ipython2
+.. code:: python
def defs():
return locals()
-.. code:: ipython2
+.. code:: python
DEFS = defs()
-.. code:: ipython2
+.. code:: python
for name, stack_effect_comment in sorted(DEFS.items()):
print name, '=', doc_from_stack_effect(*stack_effect_comment)
uncons = ([a1 .1.] -- a1 [.1.])
-.. code:: ipython2
+.. code:: python
globals().update(DEFS)
Compose ``dup`` and ``mul``
^^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. code:: ipython2
+.. code:: python
C(dup, mul)
Revisit the ``F`` function, works fine.
-.. code:: ipython2
+.. code:: python
F = reduce(C, (pop, swap, rolldown, rest, rest, cons, cons))
F
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
def neato(*funcs):
print doc_from_stack_effect(*reduce(C, funcs))
-.. code:: ipython2
+.. code:: python
# e.g. [swap] dip
neato(rollup, swap, rolldown)
(a1 a2 a3 -- a2 a1 a3)
-.. code:: ipython2
+.. code:: python
# e.g. [popop] dipd
neato(popdd, rolldown, pop)
(a1 a2 a3 a4 -- a3 a4)
-.. code:: ipython2
+.. code:: python
# 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:: ipython2
+.. code:: python
def compile_(name, f, doc=None):
inputs, outputs = f
%s = stack
return %s''' % (name, doc, i, o)
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
print compile_('sqr', C(dup, mul))
``AnyJoyType`` and ``StackJoyType`` labels in their stack effect
comments. We can write a function to check that:
-.. code:: ipython2
+.. code:: python
from itertools import imap
def compilable(f):
return isinstance(f, tuple) and all(imap(compilable, f)) or stacky(f)
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
C(stack, uncons)
-.. code:: ipython2
+.. code:: python
reduce(C, (stack, uncons, uncons))
Clunky junk, but it will suffice for now.
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
print ; print doc_from_stack_effect(*stack)
print ; print doc_from_stack_effect(*C(stack, uncons))
(... a1 -- ... a1 [a1 ...])
-.. code:: ipython2
+.. code:: python
print doc_from_stack_effect(*C(ccons, stack))
(... a2 a1 [.1.] -- ... [a2 a1 .1.] [[a2 a1 .1.] ...])
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
def compile_(name, f, doc=None):
i, o = f
%s = stack
return %s''' % (name, doc, i, o)
-.. code:: ipython2
+.. code:: python
print compile_('Q', Q)
-.. code:: ipython2
+.. code:: python
unstack = (S[1], S[0]), S[1]
enstacken = S[0], (S[0], S[1])
-.. code:: ipython2
+.. code:: python
print doc_from_stack_effect(*unstack)
([.1.] --)
-.. code:: ipython2
+.. code:: python
print doc_from_stack_effect(*enstacken)
(-- [.0.])
-.. code:: ipython2
+.. code:: python
print doc_from_stack_effect(*C(cons, unstack))
(a1 [.1.] -- a1)
-.. code:: ipython2
+.. code:: python
print doc_from_stack_effect(*C(cons, enstacken))
(a1 [.1.] -- [[a1 .1.] .2.])
-.. code:: ipython2
+.. code:: python
C(cons, unstack)
…
-.. code:: ipython2
+.. code:: python
class IntJoyType(NumberJoyType): prefix = 'i'
F = map(FloatJoyType, _R)
I = map(IntJoyType, _R)
-.. code:: ipython2
+.. code:: python
muls = [
((I[2], (I[1], S[0])), (I[3], S[0])),
((F[2], (F[1], S[0])), (F[3], S[0])),
]
-.. code:: ipython2
+.. code:: python
for f in muls:
print doc_from_stack_effect(*f)
(f1 f2 -- f3)
-.. code:: ipython2
+.. code:: python
for f in muls:
try:
(a1 -- a1 a1) (f1 f2 -- f3) (f1 -- f2)
-.. code:: ipython2
+.. code:: python
from itertools import product
def MC(F, G):
return sorted(set(meta_compose(F, G)))
-.. code:: ipython2
+.. code:: python
for f in MC([dup], [mul]):
print doc_from_stack_effect(*f)
(n1 -- n2)
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
class KleeneStar(object):
Can now return multiple results…
-.. code:: ipython2
+.. code:: python
def unify(u, v, s=None):
if s is None:
def stacky(thing):
return thing.__class__ in {AnyJoyType, StackJoyType}
-.. code:: ipython2
+.. code:: python
a = (As[1], S[1])
a
-.. code:: ipython2
+.. code:: python
b = (A[1], S[2])
b
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
for result in unify(a, b):
print result, '->', update(result, a), update(result, b)
(a1*, s1) [a1*] (a2, (a1*, s1)) [a2 a1*]
-.. code:: ipython2
+.. code:: python
sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0])
([n1* .1.] -- n0)
-.. code:: ipython2
+.. code:: python
f = (N[1], (N[2], (N[3], S[1]))), S[0]
(-- [n1 n2 n3 .1.])
-.. code:: ipython2
+.. code:: python
for result in unify(sum_[0], f):
print result, '->', update(result, sum_[1])
This function has to be modified to yield multiple results.
-.. code:: ipython2
+.. code:: python
def compose(f, g):
(f_in, f_out), (g_in, g_out) = f, g
-.. code:: ipython2
+.. code:: python
def meta_compose(F, G):
for f, g in product(F, G):
for fg in compose(f, g):
yield delabel(fg)
-.. code:: ipython2
+.. code:: python
for f in MC([dup], muls):
print doc_from_stack_effect(*f)
(i1 -- i2)
-.. code:: ipython2
+.. code:: python
([n1* .1.] -- [n1* .1.] n1)
-.. code:: ipython2
+.. code:: python
(n1 [n1* .1.] -- n2)
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
a = (A[4], (As[1], (A[3], S[1])))
a
-.. code:: ipython2
+.. code:: python
b = (A[1], (A[2], S[2]))
b
-.. code:: ipython2
+.. code:: python
for result in unify(b, a):
print result
{a1: a4, s2: (a1*, (a3, s1)), a2: a10003}
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
class SymbolJoyType(FunctionJoyType):
prefix = 'F'
For combinators the list contains Python functions.
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
import joy.library
have to write functions that each implement the action of one of the
effects.
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
ID = S[0], S[0] # Identity function.
``SymbolJoyType`` objects, and some combinators. Here is an example of
output from the current code :
-.. code:: ipython2
+.. code:: python
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:: ipython2
+.. code:: python
def _ge(self, other):
return (issubclass(other.__class__, self.__class__)
Given a datastructure on the stack we can navigate through it, modify
it, and rebuild it using the “zipper” technique.
-.. code:: ipython2
+.. code:: python
from notebook_preamble import J, V, define
`trees <https://en.wikipedia.org/wiki/Tree_%28data_structure%29>`__ out
of sequences.
-.. code:: ipython2
+.. code:: python
J('[1 [2 [3 4 25 6] 7] 8]')
these a lot it would make sense to write Python versions for efficiency,
but see below.
-.. code:: ipython2
+.. code:: python
define('z-down == [] swap uncons swap')
define('z-up == swons swap shunt')
define('z-right == [swons] cons dip uncons swap')
define('z-left == swons [uncons swap] dip swap')
-.. code:: ipython2
+.. code:: python
V('[1 [2 [3 4 25 6] 7] 8] z-down')
[] [[2 [3 4 25 6] 7] 8] 1 .
-.. code:: ipython2
+.. code:: python
V('[] [[2 [3 4 25 6] 7] 8] 1 z-right')
[1] [8] [2 [3 4 25 6] 7] .
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2 [3 4 25 6] 7] z-down')
[1] [8] [] [[3 4 25 6] 7] 2
-.. code:: ipython2
+.. code:: python
J('[1] [8] [] [[3 4 25 6] 7] 2 z-right')
[1] [8] [2] [7] [3 4 25 6]
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2] [7] [3 4 25 6] z-down')
[1] [8] [2] [7] [] [4 25 6] 3
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2] [7] [] [4 25 6] 3 z-right')
[1] [8] [2] [7] [3] [25 6] 4
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2] [7] [3] [25 6] 4 z-right')
[1] [8] [2] [7] [4 3] [6] 25
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2] [7] [4 3] [6] 25 sqr')
[1] [8] [2] [7] [4 3] [6] 625
-.. code:: ipython2
+.. code:: python
V('[1] [8] [2] [7] [4 3] [6] 625 z-up')
[1] [8] [2] [7] [3 4 625 6] .
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2] [7] [3 4 625 6] z-up')
[1] [8] [2 [3 4 625 6] 7]
-.. code:: ipython2
+.. code:: python
J('[1] [8] [2 [3 4 625 6] 7] z-up')
In Joy we have the ``dip`` and ``infra`` combinators which can “target”
or “address” any particular item in a Joy tree structure.
-.. code:: ipython2
+.. code:: python
V('[1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] infra')
The ``Z`` function isn’t hard to make.
-.. code:: ipython2
+.. code:: python
define('Z == [[] cons cons] step i')
Here it is in action in a simplified scenario.
-.. code:: ipython2
+.. code:: python
V('1 [2 3 4] Z')
And here it is doing the main thing.
-.. code:: ipython2
+.. code:: python
J('[1 [2 [3 4 25 6] 7] 8] [sqr] [dip dip infra dip infra dip infra] Z')