Bunches of new docs.

Type inference!

A new treatment of recursion combinator patterns.
This commit is contained in:
Simon Forman
2018-06-21 21:13:50 -07:00
parent 049cfd22b7
commit ca05ea404a
32 changed files with 42303 additions and 1064 deletions
+102 -127
View File
@@ -1,104 +1,92 @@
`Quadratic formula <https://en.wikipedia.org/wiki/Quadratic_formula>`__
=======================================================================
.. code:: ipython2
from notebook_preamble import J, V, define
`Quadratic formula <https://en.wikipedia.org/wiki/Quadratic_formula>`__
=======================================================================
Cf.
`jp-quadratic.html <http://www.kevinalbrecht.com/code/joy-mirror/jp-quadratic.html>`__
::
-b +/- sqrt(b^2 - 4 * a * c)
-----------------------------
2 * a
-b ± sqrt(b^2 - 4 * a * c)
--------------------------------
2 * a
:math:`\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}`
Write a straightforward program with variable names.
----------------------------------------------------
::
This math translates to Joy code in a straightforward manner. We are
going to use named variables to keep track of the arguments, then write
a definition without them.
b neg b sqr 4 a c * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2
We use ``cleave`` to compute the sum and difference and then ``app2`` to
finish computing both roots using a quoted program ``[2a truediv]``
built with ``cons``.
Check it.
~~~~~~~~~
Evaluating by hand:
``-b``
~~~~~~
::
b neg b sqr 4 a c * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2
-b b sqr 4 a c * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2
-b b^2 4 a c * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2
-b b^2 4ac - sqrt [+] [-] cleave a 2 * [truediv] cons app2
-b b^2-4ac sqrt [+] [-] cleave a 2 * [truediv] cons app2
-b sqrt(b^2-4ac) [+] [-] cleave a 2 * [truediv] cons app2
b neg
-b -b+sqrt(b^2-4ac) -b-sqrt(b^2-4ac) a 2 * [truediv] cons app2
-b -b+sqrt(b^2-4ac) -b-sqrt(b^2-4ac) 2a [truediv] cons app2
-b -b+sqrt(b^2-4ac) -b-sqrt(b^2-4ac) [2a truediv] app2
-b -b+sqrt(b^2-4ac)/2a -b-sqrt(b^2-4ac)/2a
``sqrt(b^2 - 4 * a * c)``
~~~~~~~~~~~~~~~~~~~~~~~~~
(Eventually well be able to use e.g. Sympy versions of the Joy commands
to do this sort of thing symbolically. This is part of what is meant by
a “categorical” language.)
::
Cleanup
b sqr 4 a c * * - sqrt
``/2a``
~~~~~~~
::
-b -b+sqrt(b^2-4ac)/2a -b-sqrt(b^2-4ac)/2a roll< pop
-b+sqrt(b^2-4ac)/2a -b-sqrt(b^2-4ac)/2a -b pop
-b+sqrt(b^2-4ac)/2a -b-sqrt(b^2-4ac)/2a
a 2 * /
Derive a definition.
--------------------
``±``
~~~~~
::
b neg b sqr 4 a c * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2 roll< pop
b [neg] dupdip sqr 4 a c * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2 roll< pop
b a c [[neg] dupdip sqr 4] dipd * * - sqrt [+] [-] cleave a 2 * [truediv] cons app2 roll< pop
b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt [+] [-] cleave] dip 2 * [truediv] cons app2 roll< pop
b a c over [[[neg] dupdip sqr 4] dipd * * - sqrt [+] [-] cleave] dip 2 * [truediv] cons app2 roll< pop
.. code:: ipython2
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt [+] [-] cleave] dip 2 * [truediv] cons app2 roll< pop')
.. code:: ipython2
J('3 1 1 quadratic')
.. parsed-literal::
-0.3819660112501051 -2.618033988749895
Simplify
--------
We can define a ``pm`` plus-or-minus function:
There is a function ``pm`` that accepts two values on the stack and
replaces them with their sum and difference.
::
pm == [+] [-] cleave popdd
Then ``quadratic`` becomes:
Putting Them Together
~~~~~~~~~~~~~~~~~~~~~
::
b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
We use ``app2`` to compute both roots by using a quoted program
``[2a /]`` built with ``cons``.
Derive a definition.
--------------------
Working backwards we use ``dip`` and ``dipd`` to extract the code from
the variables:
::
b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b [neg] dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b a c [[neg] dupdip sqr 4] dipd * * - sqrt pm a 2 * [/] cons app2
b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
b a c over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
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
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [truediv] cons app2')
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2')
Let's try it out:
.. code:: ipython2
@@ -110,74 +98,61 @@ Then ``quadratic`` becomes:
-0.3819660112501051 -2.618033988749895
Define a "native" ``pm`` function.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The definition of ``pm`` above is pretty elegant, but the implementation
takes a lot of steps relative to what it's accomplishing. Since we are
likely to use ``pm`` more than once in the future, let's write a
primitive in Python and add it to the dictionary. (This has been done
already.)
If you look at the Joy evaluation trace you can see that the first few
lines are the ``dip`` and ``dipd`` combinators building the main program
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
def pm(stack):
a, (b, stack) = stack
p, m, = b + a, b - a
return m, (p, stack)
The resulting trace is short enough to fit on a page.
.. code:: ipython2
V('3 1 1 quadratic')
V('-5 1 4 quadratic')
.. parsed-literal::
. 3 1 1 quadratic
3 . 1 1 quadratic
3 1 . 1 quadratic
3 1 1 . quadratic
3 1 1 . over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [truediv] cons app2
3 1 1 1 . [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [truediv] cons app2
3 1 1 1 [[[neg] dupdip sqr 4] dipd * * - sqrt pm] . dip 2 * [truediv] cons app2
3 1 1 . [[neg] dupdip sqr 4] dipd * * - sqrt pm 1 2 * [truediv] cons app2
3 1 1 [[neg] dupdip sqr 4] . dipd * * - sqrt pm 1 2 * [truediv] cons app2
3 . [neg] dupdip sqr 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
3 [neg] . dupdip sqr 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
3 . neg 3 sqr 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 . 3 sqr 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 3 . sqr 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 3 . dup mul 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 3 3 . mul 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 9 . 4 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 9 4 . 1 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 9 4 1 . 1 * * - sqrt pm 1 2 * [truediv] cons app2
-3 9 4 1 1 . * * - sqrt pm 1 2 * [truediv] cons app2
-3 9 4 1 . * - sqrt pm 1 2 * [truediv] cons app2
-3 9 4 . - sqrt pm 1 2 * [truediv] cons app2
-3 5 . sqrt pm 1 2 * [truediv] cons app2
-3 2.23606797749979 . pm 1 2 * [truediv] cons app2
-0.7639320225002102 -5.23606797749979 . 1 2 * [truediv] cons app2
-0.7639320225002102 -5.23606797749979 1 . 2 * [truediv] cons app2
-0.7639320225002102 -5.23606797749979 1 2 . * [truediv] cons app2
-0.7639320225002102 -5.23606797749979 2 . [truediv] cons app2
-0.7639320225002102 -5.23606797749979 2 [truediv] . cons app2
-0.7639320225002102 -5.23606797749979 [2 truediv] . app2
[-0.7639320225002102] [2 truediv] . infra first [-5.23606797749979] [2 truediv] infra first
-0.7639320225002102 . 2 truediv [] swaack first [-5.23606797749979] [2 truediv] infra first
-0.7639320225002102 2 . truediv [] swaack first [-5.23606797749979] [2 truediv] infra first
-0.3819660112501051 . [] swaack first [-5.23606797749979] [2 truediv] infra first
-0.3819660112501051 [] . swaack first [-5.23606797749979] [2 truediv] infra first
[-0.3819660112501051] . first [-5.23606797749979] [2 truediv] infra first
-0.3819660112501051 . [-5.23606797749979] [2 truediv] infra first
-0.3819660112501051 [-5.23606797749979] . [2 truediv] infra first
-0.3819660112501051 [-5.23606797749979] [2 truediv] . infra first
-5.23606797749979 . 2 truediv [-0.3819660112501051] swaack first
-5.23606797749979 2 . truediv [-0.3819660112501051] swaack first
-2.618033988749895 . [-0.3819660112501051] swaack first
-2.618033988749895 [-0.3819660112501051] . swaack first
-0.3819660112501051 [-2.618033988749895] . first
-0.3819660112501051 -2.618033988749895 .
. -5 1 4 quadratic
-5 . 1 4 quadratic
-5 1 . 4 quadratic
-5 1 4 . quadratic
-5 1 4 . over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
-5 1 4 1 . [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
-5 1 4 1 [[[neg] dupdip sqr 4] dipd * * - sqrt pm] . dip 2 * [/] cons app2
-5 1 4 . [[neg] dupdip sqr 4] dipd * * - sqrt pm 1 2 * [/] cons app2
-5 1 4 [[neg] dupdip sqr 4] . dipd * * - sqrt pm 1 2 * [/] cons app2
-5 . [neg] dupdip sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
-5 [neg] . dupdip sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
-5 . neg -5 sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 . -5 sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 -5 . sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 -5 . dup mul 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 -5 -5 . mul 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 . 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 4 . 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 4 1 . 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 4 1 4 . * * - sqrt pm 1 2 * [/] cons app2
5 25 4 4 . * - sqrt pm 1 2 * [/] cons app2
5 25 16 . - sqrt pm 1 2 * [/] cons app2
5 9 . sqrt pm 1 2 * [/] cons app2
5 3.0 . pm 1 2 * [/] cons app2
8.0 2.0 . 1 2 * [/] cons app2
8.0 2.0 1 . 2 * [/] cons app2
8.0 2.0 1 2 . * [/] cons app2
8.0 2.0 2 . [/] cons app2
8.0 2.0 2 [/] . cons app2
8.0 2.0 [2 /] . app2
[8.0] [2 /] . infra first [2.0] [2 /] infra first
8.0 . 2 / [] swaack first [2.0] [2 /] infra first
8.0 2 . / [] swaack first [2.0] [2 /] infra first
4.0 . [] swaack first [2.0] [2 /] infra first
4.0 [] . swaack first [2.0] [2 /] infra first
[4.0] . first [2.0] [2 /] infra first
4.0 . [2.0] [2 /] infra first
4.0 [2.0] . [2 /] infra first
4.0 [2.0] [2 /] . infra first
2.0 . 2 / [4.0] swaack first
2.0 2 . / [4.0] swaack first
1.0 . [4.0] swaack first
1.0 [4.0] . swaack first
4.0 [1.0] . first
4.0 1.0 .
@@ -0,0 +1,695 @@
.. code:: ipython2
from notebook_preamble import D, DefinitionWrapper, J, V, define
Recursive Combinators
=====================
This article describes the ``genrec`` combinator, how to use it, and
several generic specializations.
::
[if] [then] [rec1] [rec2] genrec
---------------------------------------------------------------------
[if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
"The genrec combinator takes four program parameters in addition to
whatever data parameters it needs. Fourth from the top is an
if-part, followed by a then-part. If the if-part yields true, then
the then-part is executed and the combinator terminates. The other
two parameters are the rec1-part and the rec2-part. If the if-part
yields false, the rec1-part is executed. Following that the four
program parameters and the combinator are again pushed onto the
stack bundled up in a quoted form. Then the rec2-part is executed,
where it will find the bundled form. Typically it will then execute
the bundled form, either with i or with app2, or some other
combinator."
Designing Recursive Functions
-----------------------------
The way to design one of these is to fix your base case and test and
then treat ``R1`` and ``R2`` as an else-part "sandwiching" a quotation
of the whole function.
For example, given a (general recursive) function ``F``:
::
F == [I] [T] [R1] [R2] genrec
== [I] [T] [R1 [F] R2] ifte
If the ``[I]`` predicate is false you must derive ``R1`` and ``R2``
from:
::
... R1 [F] R2
Set the stack arguments in front and figure out what ``R1`` and ``R2``
have to do to apply the quoted ``[F]`` in the proper way.
Primitive Recursive Functions
-----------------------------
Primitive recursive functions are those where ``R2 == i``.
::
P == [I] [T] [R] primrec
== [I] [T] [R [P] i] ifte
== [I] [T] [R P] ifte
`Hylomorphism <https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>`__
------------------------------------------------------------------------------------
A
`hylomorphism <https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>`__
is a recursive function ``H :: A -> C`` that converts a value of type
``A`` into a value of type ``C`` by means of:
- A generator ``G :: A -> (B, A)``
- A combiner ``F :: (B, C) -> C``
- A predicate ``P :: A -> Bool`` to detect the base case
- A base case value ``c :: C``
- Recursive calls (zero or more); it has a "call stack in the form of a
cons list".
It may be helpful to see this function implemented in imperative Python
code.
.. code:: ipython2
def hylomorphism(c, F, P, G):
'''Return a hylomorphism function H.'''
def H(a):
if P(a):
result = c
else:
b, aa = G(a)
result = F(b, H(aa)) # b is stored in the stack frame during recursive call to H().
return result
return H
Cf. `"Bananas, Lenses, & Barbed
Wire" <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
Note that during evaluation of ``H()`` the intermediate ``b`` values are
stored in the Python call stack. This is what is meant by "call stack in
the form of a cons list".
Hylomorphism in Joy
-------------------
We can define a combinator ``hylomorphism`` that will make a
hylomorphism combinator ``H`` from constituent parts.
::
H == [P] c [G] [F] hylomorphism
The function ``H`` is recursive, so we start with ``ifte`` and set the
else-part to some function ``J`` that will contain a quoted copy of
``H``. (The then-part just discards the leftover ``a`` and replaces it
with the base case value ``c``.)
::
H == [P] [pop c] [J] ifte
The else-part ``J`` gets just the argument ``a`` on the stack.
::
a J
a G The first thing to do is use the generator G
aa b which produces b and a new aa
aa b [H] dip we recur with H on the new aa
aa H b F and run F on the result.
This gives us a definition for ``J``.
::
J == G [H] dip F
Plug it in and convert to genrec.
::
H == [P] [pop c] [G [H] dip F] ifte
H == [P] [pop c] [G] [dip F] genrec
This is the form of a hylomorphism in Joy, which nicely illustrates that
it is a simple specialization of the general recursion combinator.
::
H == [P] c [G] [F] hylomorphism == [P] [pop c] [G] [dip F] genrec
Derivation of ``hylomorphism`` combinator
-----------------------------------------
Now we just need to derive a definition that builds the ``genrec``
arguments out of the pieces given to the ``hylomorphism`` combinator.
::
[P] c [G] [F] hylomorphism
------------------------------------------
[P] [pop c] [G] [dip F] genrec
Working in reverse:
- Use ``swoncat`` twice to decouple ``[c]`` and ``[F]``.
- Use ``unit`` to dequote ``c``.
- Use ``dipd`` to untangle ``[unit [pop] swoncat]`` from the givens.
So:
::
H == [P] [pop c] [G] [dip F] genrec
[P] [c] [pop] swoncat [G] [F] [dip] swoncat genrec
[P] c unit [pop] swoncat [G] [F] [dip] swoncat genrec
[P] c [G] [F] [unit [pop] swoncat] dipd [dip] swoncat genrec
At this point all of the arguments (givens) to the hylomorphism are to
the left so we have a definition for ``hylomorphism``:
::
hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec
.. code:: ipython2
define('hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec')
Example: Finding `Triangular Numbers <https://en.wikipedia.org/wiki/Triangular_number>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's write a function that, given a positive integer, returns the sum
of all positive integers less than that one. (In this case the types
``A``, ``B`` and ``C`` are all ``int``.)
To sum a range of integers from 0 to *n* - 1:
- ``[P]`` is ``[1 <=]``
- ``c`` is ``0``
- ``[G]`` is ``[-- dup]``
- ``[F]`` is ``[+]``
.. code:: ipython2
define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
Let's try it:
.. code:: ipython2
J('5 triangular_number')
.. parsed-literal::
10
.. code:: ipython2
J('[0 1 2 3 4 5 6] [triangular_number] map')
.. parsed-literal::
[0 0 1 3 6 10 15]
Four Specializations
--------------------
There are at least four kinds of recursive combinator, depending on two
choices. The first choice is whether the combiner function ``F`` should
be evaluated during the recursion or pushed into the pending expression
to be "collapsed" at the end. The second choice is whether the combiner
needs to operate on the current value of the datastructure or the
generator's output, in other words, whether ``F`` or ``G`` should run
first in the recursive branch.
::
H1 == [P] [pop c] [G ] [dip F] genrec
H2 == c swap [P] [pop] [G [F] dip ] [i] genrec
H3 == [P] [pop c] [ [G] dupdip ] [dip F] genrec
H4 == c swap [P] [pop] [ [F] dupdip G] [i] genrec
The working of the generator function ``G`` differs slightly for each.
Consider the recursive branches:
::
... a G [H1] dip F w/ a G == a b
... c a G [F] dip H2 a G == b a
... a [G] dupdip [H3] dip F a G == a
... c a [F] dupdip G H4 a G == a
The following four sections illustrate how these work, omitting the
predicate evaluation.
``H1``
~~~~~~
::
H1 == [P] [pop c] [G] [dip F] genrec
Iterate n times.
::
... a G [H1] dip F
... a b [H1] dip F
... a H1 b F
... a G [H1] dip F b F
... a″ b [H1] dip F b F
... a″ H1 b F b F
... a″ G [H1] dip F b F b F
... a‴ b″ [H1] dip F b F b F
... a‴ H1 b″ F b F b F
... a‴ pop c b″ F b F b F
... c b″ F b F b F
... d b F b F
... d b F
... d″
This form builds up a pending expression (continuation) that contains
the intermediate results along with the pending combiner functions. When
the base case is reached the last term is replaced by the identity value
``c`` and the continuation "collapses" into the final result using the
combiner ``F``.
``H2``
~~~~~~
When you can start with the identity value ``c`` on the stack and the
combiner ``F`` can operate as you go using the intermediate results
immediately rather than queuing them up, use this form. An important
difference is that the generator function must return its results in the
reverse order.
::
H2 == c swap [P] [pop] [G [F] dip] primrec
... c a G [F] dip H2
... c b a [F] dip H2
... c b F a H2
... d a H2
... d a G [F] dip H2
... d b a″ [F] dip H2
... d b F a″ H2
... d a″ H2
... d a″ G [F] dip H2
... d b″ a‴ [F] dip H2
... d b″ F a‴ H2
... d″ a‴ H2
... d″ a‴ pop
... d″
``H3``
~~~~~~
If you examine the traces above you'll see that the combiner ``F`` only
gets to operate on the results of ``G``, it never "sees" the first value
``a``. If the combiner and the generator both need to work on the
current value then ``dup`` must be used, and the generator must produce
one item instead of two (the b is instead the duplicate of a.)
::
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
... a [G] dupdip [H3] dip F
... a G a [H3] dip F
... a a [H3] dip F
... a H3 a F
... a [G] dupdip [H3] dip F a F
... a G a [H3] dip F a F
... a″ a [H3] dip F a F
... a″ H3 a F a F
... a″ [G] dupdip [H3] dip F a F a F
... a″ G a″ [H3] dip F a F a F
... a‴ a″ [H3] dip F a F a F
... a‴ H3 a″ F a F a F
... a‴ pop c a″ F a F a F
... c a″ F a F a F
... d a F a F
... d a F
... d″
``H4``
~~~~~~
And, last but not least, if you can combine as you go, starting with
``c``, and the combiner ``F`` needs to work on the current item, this is
the form:
::
H4 == c swap [P] [pop] [[F] dupdip G] primrec
... c a [F] dupdip G H4
... c a F a G H4
... d a G H4
... d a H4
... d a [F] dupdip G H4
... d a F a G H4
... d a G H4
... d a″ H4
... d a″ [F] dupdip G H4
... d a″ F a″ G H4
... d″ a″ G H4
... d″ a‴ H4
... d″ a‴ pop
... d″
Anamorphism
-----------
An anamorphism can be defined as a hylomorphism that uses ``[]`` for
``c`` and ``swons`` for ``F``. An anamorphic function builds a list of
values.
::
A == [P] [] [G] [swons] hylomorphism
``range`` et. al.
~~~~~~~~~~~~~~~~~
An example of an anamorphism is the ``range`` function which generates
the list of integers from 0 to *n* - 1 given *n*.
Each of the above variations can be used to make four slightly different
``range`` functions.
``range`` with ``H1``
^^^^^^^^^^^^^^^^^^^^^
::
H1 == [P] [pop c] [G] [dip F] genrec
== [0 <=] [pop []] [-- dup] [dip swons] genrec
.. code:: ipython2
define('range == [0 <=] [] [-- dup] [swons] hylomorphism')
.. code:: ipython2
J('5 range')
.. parsed-literal::
[4 3 2 1 0]
``range`` with ``H2``
^^^^^^^^^^^^^^^^^^^^^
::
H2 == c swap [P] [pop] [G [F] dip] primrec
== [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
.. code:: ipython2
define('range_reverse == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec')
.. code:: ipython2
J('5 range_reverse')
.. parsed-literal::
[0 1 2 3 4]
``range`` with ``H3``
^^^^^^^^^^^^^^^^^^^^^
::
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
== [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
.. code:: ipython2
define('ranger == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec')
.. code:: ipython2
J('5 ranger')
.. parsed-literal::
[5 4 3 2 1]
``range`` with ``H4``
^^^^^^^^^^^^^^^^^^^^^
::
H4 == c swap [P] [pop] [[F] dupdip G ] primrec
== [] swap [0 <=] [pop] [[swons] dupdip --] primrec
.. code:: ipython2
define('ranger_reverse == [] swap [0 <=] [pop] [[swons] dupdip --] primrec')
.. code:: ipython2
J('5 ranger_reverse')
.. parsed-literal::
[1 2 3 4 5]
Hopefully this illustrates the workings of the variations. For more
insight you can run the cells using the ``V()`` function instead of the
``J()`` function to get a trace of the Joy evaluation.
Catamorphism
------------
A catamorphism can be defined as a hylomorphism that uses
``[uncons swap]`` for ``[G]`` and ``[[] =]`` (or just ``[not]``) for the
predicate ``[P]``. A catamorphic function tears down a list term-by-term
and makes some new value.
::
C == [not] c [uncons swap] [F] hylomorphism
.. code:: ipython2
define('swuncons == uncons swap') # Awkward name.
An example of a catamorphism is the sum function.
::
sum == [not] 0 [swuncons] [+] hylomorphism
.. code:: ipython2
define('sum == [not] 0 [swuncons] [+] hylomorphism')
.. code:: ipython2
J('[5 4 3 2 1] sum')
.. parsed-literal::
15
The ``step`` combinator
~~~~~~~~~~~~~~~~~~~~~~~
The ``step`` combinator will usually be better to use than
``catamorphism``.
.. code:: ipython2
J('[step] help')
.. parsed-literal::
Run a quoted program on each item in a sequence.
::
... [] [Q] . step
-----------------------
... .
... [a] [Q] . step
------------------------
... a . Q
... [a b c] [Q] . step
----------------------------------------
... a . Q [b c] [Q] step
The step combinator executes the quotation on each member of the list
on top of the stack.
.. code:: ipython2
define('sum == 0 swap [+] step')
.. code:: ipython2
J('[5 4 3 2 1] sum')
.. parsed-literal::
15
Example: Factorial Function
---------------------------
For the Factorial function:
::
H4 == c swap [P] [pop] [[F] dupdip G] primrec
With:
::
c == 1
F == *
G == --
P == 1 <=
.. code:: ipython2
define('factorial == 1 swap [1 <=] [pop] [[*] dupdip --] primrec')
.. code:: ipython2
J('5 factorial')
.. parsed-literal::
120
Example: ``tails``
------------------
An example of a paramorphism for lists given in the `"Bananas..."
paper <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
is ``tails`` which returns the list of "tails" of a list.
::
[1 2 3] tails
--------------------
[[] [3] [2 3]]
We can build as we go, and we want ``F`` to run after ``G``, so we use
pattern ``H2``:
::
H2 == c swap [P] [pop] [G [F] dip] primrec
We would use:
::
c == []
F == swons
G == rest dup
P == not
.. code:: ipython2
define('tails == [] swap [not] [pop] [rest dup [swons] dip] primrec')
.. code:: ipython2
J('[1 2 3] tails')
.. parsed-literal::
[[] [3] [2 3]]
Conclusion: Patterns of Recursion
---------------------------------
Our story so far...
Hylo-, Ana-, Cata-
~~~~~~~~~~~~~~~~~~
::
H == [P ] [pop c ] [G ] [dip F ] genrec
A == [P ] [pop []] [G ] [dip swap cons] genrec
C == [not] [pop c ] [uncons swap] [dip F ] genrec
Para-, ?-, ?-
~~~~~~~~~~~~~
::
P == c swap [P ] [pop] [[F ] dupdip G ] primrec
? == [] swap [P ] [pop] [[swap cons] dupdip G ] primrec
? == c swap [not] [pop] [[F ] dupdip uncons swap] primrec
Appendix: Fun with Symbols
--------------------------
::
|[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)]
`"Bananas, Lenses, & Barbed
Wire" <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
::
(|...|) [(...)] [<...>]
I think they are having slightly too much fun with the symbols. However,
"Too much is always better than not enough."
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -9,13 +9,15 @@ These essays are adapted from Jupyter notebooks. I hope to have those hosted so
:maxdepth: 2
Developing
Quadratic
Replacing
Recursion_Combinators
Ordered_Binary_Trees
Treestep
Generator_Programs
Newton-Raphson
Quadratic
Zipper
Types
NoUpdates
Categorical