{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Symbolic Evaluation with SymPy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sympy\n",
    "\n",
    "from joy.joy import run\n",
    "from joy.library import UnaryBuiltinWrapper\n",
    "from joy.utils.pretty_print import TracePrinter\n",
    "from joy.utils.stack import list_to_stack\n",
    "\n",
    "from notebook_preamble import D, J, V, define"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "sympy.init_printing()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [SypPy Symbols](http://docs.sympy.org/latest/modules/core.html#module-sympy.core.symbol)\n",
    "\n",
    "The SymPy package provides a powerful and elegant [\"thunk\"](https://en.wikipedia.org/wiki/Thunk) object that can take the place of a numeric value in calculations and \"record\" the operations performed on it.\n",
    "\n",
    "We can create some of these objects and put them on the Joy stack:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "stack = list_to_stack(sympy.symbols('c a b'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we evaluate the `quadratic` program\n",
    "\n",
    "    over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2\n",
    "\n",
    "The [SypPy Symbols](http://docs.sympy.org/latest/modules/core.html#module-sympy.core.symbol) will become the symbolic expression of the math operations.  Unfortunately, the library `sqrt` function doesn't work with the SymPy objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "can't convert expression to float\n",
      "                                            b a c . over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2\n",
      "                                          b a c a . [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2\n",
      "b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt pm] . dip 2 * [/] cons app2\n",
      "                                            b a c . [[neg] dupdip sqr 4] dipd * * - sqrt pm a 2 * [/] cons app2\n",
      "                       b a c [[neg] dupdip sqr 4] . dipd * * - sqrt pm a 2 * [/] cons app2\n",
      "                                                b . [neg] dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                          b [neg] . dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                                b . neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                               -b . b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                             -b b . sqr 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                             -b b . dup mul 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                           -b b b . mul 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                          -b b**2 . 4 a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                        -b b**2 4 . a c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                      -b b**2 4 a . c * * - sqrt pm a 2 * [/] cons app2\n",
      "                                    -b b**2 4 a c . * * - sqrt pm a 2 * [/] cons app2\n",
      "                                    -b b**2 4 a*c . * - sqrt pm a 2 * [/] cons app2\n",
      "                                    -b b**2 4*a*c . - sqrt pm a 2 * [/] cons app2\n",
      "                                 -b -4*a*c + b**2 . sqrt pm a 2 * [/] cons app2\n"
     ]
    }
   ],
   "source": [
    "viewer = TracePrinter()\n",
    "try:\n",
    "    run('over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2', stack, D, viewer.viewer)\n",
    "except Exception, e:\n",
    "    print e\n",
    "viewer.print_()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can pick out that first symbolic expression obect from the Joy stack:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "S, E = viewer.history[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAGUAAAAWCAYAAADZylKgAAAABHNCSVQICAgIfAhkiAAAA3NJREFUaIHt2GuIVVUUB/CfpWU1wlRYTSFDEUHQ5IxCURBMI9EDouhDH3rYNYSgQII+9ADh4oeIpJoi+iAUlUUFlZEETQ0ZvSw1TaJIU5kgMppeGFFWNn1Y+zKn47l35p6544jcP2wOZ+291v6fu/Z67EsbbbQB7sVm7MMo1uO8GWXUhiEsE47owTp8j5NmktSRhtsxhltL6nfgAK6Go1pEqh5uFmTHsHya95pJLErPT0vqzxO++Lk1dOpjAX7Fb458p2zDH5hdUv+lZOPoljEqwCwMYzdWO7ydUhH8+kvqH4u/8ElJ/dXYi7NrgulKXyswIIrZ75NYfwOex04RWb+I7mRZA51+ccK+xX7xYUO4tizpkujBHJG6evEKfhLf/ZbGXdVDWIol2FUTTodTzsUDeBTvTWL9PDyDs/A+HserOBNP4e4CnUFsEI4fxsPpfTEunhr9prE4PbvxoYi6J7EVl+EddBboPYabcCm+nE6Cs7EFO3BcklU1Tl8dOK1A3iWi5quc/P5k72WcUGCru0nOFVNLX2uS/o/oy809m+buy8mfEHeUAfHttdFRkkNDrBKt3UUZWVX5mrIDP2Te+5L9zTimHMWDUDE1p2xRvx2+MM29kJOP1RnVog1GGigUjecyuhfgHzyYs1nV2CknYiU+FrXkQG6PrZm1a5NsSR1bE2GkwbcUjacnsDdH1LMRxZ1Xd7KzrhmSeUO78WcT+t9l7KwVhXplE/rni2J4KjbhRdGr/y1qylJsz6y/XDhuQxN7ZDHo4Pzei2tEXRvJzX02gb0eEbHrxYHMo5ZKv2mKZYvQafKnbzCjt10cgv4Cm6vS+jvS+9z0vq3F3CvKp6/lSfeuOvOPpPmrmjFa9rKTx37RcRRhkagFH4gasTHJF4hIGcK7OZ1O4+1w7ZY8Kz1PmTrdlqHWeRX9Z9WF2/C1yAaHFaqKa8r8JN8pcnMNJwtHjYk0Njcz93mSX1+wzznK3YgrykfKpqS7x/87wQ68LerjFc0abVWklMGo6OEHxG14WJyuK0XN+Ff079kadw9eF7XnFnwhoqpXRF7XIeJO/HY9Ip0eL+rPa+KGfx1Ox5148xBymjSq6ndf80UvPyruJB+J4t5n/BKWxyV4w3hDsFekhxtL8qsoFykLk94anCEuvPvSGCphr4022mijjTZajv8AHtTyPwIcKPQAAAAASUVORK5CYII=\n",
      "text/latex": [
       "$$- 4 a c + b^{2}$$"
      ],
      "text/plain": [
       "          2\n",
       "-4⋅a⋅c + b "
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "q = S[0]\n",
    "q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Python `math.sqrt()` function causes the \"can't convert expression to float\" exception but `sympy.sqrt()` does not:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHoAAAAdCAYAAABlqHgGAAAABHNCSVQICAgIfAhkiAAABGhJREFUaIHt2muIVVUUwPGfpqU1ghVWFpMUJJVZPnpgUExK9AAthPrQQ8cQgoIIMnqANPghelBalIRgL3tCmSVBlmVU9lDTLHpoD6aIpKwMQ0pKpw/rDF6v597uOffMVeT84XDn7L3X2uucvfdaa+8zlJSU7D/0yyk3FFuKNKRk32Q6LtvbRpT0Pc9gyN42oqRx+ueQOTC5/izYlpI+JM9AT8TbBdtRsg/yMI7d20aUZCPPim7HD0UbUpKJ27AaW7EZS3FKPYGsAz0Wn+QyraRIOjAfZ4tQ+i+W47CiOrgDZxSlrARchx5c04SONuzA5FoNsq7o8VjThEFZuVq8hB7MbGG/rWRc8vtxEzqGiLH8vXlzOAaPFKGoQdrxh9jG7c8DvQ5/YUATOp5P9BxQq0Haiq61yieLoN8K+uEx/Ka1kysPnWIiduSQPQij8KmIs3m4F+eKk8odtRpVD+pkfJlSDpPwZk5jsnKDSDJmYFsD7a/A09goPMAWkZXOqCPTIVbCj9iOTViGS/ManYPRGCjc9hi8KCb3NrzufzJp3IdpYmy+qdewckBPFw9/gsjmKjlEzJa/G7G+SU7CXXgA7zTQfgiewPF4Fw9hMY7Do7glRWYeVojJtBz3J/fj7fnsfcn45HcEVgrPsBBrcT7eEh+Q0ngQV+E8fJGn85W4p6psquaywkYZIJK9DRiclHWpH6PbcFRK+XCxur+qKr8z0feCmMDVukZktLlTfte9IJH9VWxdK3kyqbs9RW6+2ENPFM/ee7Vl6fxm8aIrWYgjsijJyRzhOSZUlHXJn4xtwC8V92MT/avFeX0RdMo/0GvU3lqdldQ9m1LXU+PqytL5yEToxOS+P5Y0INddx4C066kq+TNFQlLtTbrUH+hDMRsfiti8o6qftRVtFyVlkxp4njS6ZXvGx+voGihyg27pGfeIRMdLOW3djbQONoqVMEW4vQn4qAFd38oWw3+qsmNR0vfsDDpOFUnLkViF58Re8h8Ro6dhfUX7C8RkWJGhj0rm2TNmjsElIk/orqqrd4o4WniVpdIz7t4Q8n1mKzNwt4jVvX+P6svOxMtrdJXMq5BbLyZXR4rOOUn765P7Qcn9uoJt75TPdc9M5G6qUT83qb84r2GV1Nqkv4xZIi6fjM+L6KwO20UekMY4EVvfE57mg6S8XazoZfb8bDrUrq1V74lT779NtSLXaITejDvtfHo4rsXXwmP1Gf3xs8hQ5/ZlRw3QJT1GD0vKN4p418vhYvB7hAsfVFH3WVJ+eUo/I9U5WapDp3wrelUi953ds/82vCFyjQtz2JNKrRW9U8SOWUV2VjCbxT5zosghlouVcJGIwTvF/rIyb7gVr4hYPl14qqEizrYn8q1ggIjR63CwiOVLxEnZVByNG/FaK4yZIhKXZs5gi6BL7ax7mNhvbhZ75vdFAjbWrsOHas7Bq3YlbZuEe7wyp32dsq/o0xKZBeIbwmKxL94qvFEWXU0zWHzgLikpKSkpKWk9/wEOzhV2ng+cdgAAAABJRU5ErkJggg==\n",
      "text/latex": [
       "$$\\sqrt{- 4 a c + b^{2}}$$"
      ],
      "text/plain": [
       "   _____________\n",
       "  ╱           2 \n",
       "╲╱  -4⋅a⋅c + b  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sympy.sqrt(q)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use `sympy.sqrt`\n",
    "This is easy to fix."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "D['sqrt'] = UnaryBuiltinWrapper(sympy.sqrt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now it works just fine."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "(root1, (root2, _)) = run('over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2', stack, D)[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/latex": [
       "$$\\frac{1}{2 a} \\left(- b - \\sqrt{- 4 a c + b^{2}}\\right)$$"
      ],
      "text/plain": [
       "        _____________\n",
       "       ╱           2 \n",
       "-b - ╲╱  -4⋅a⋅c + b  \n",
       "─────────────────────\n",
       "         2⋅a         "
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "root1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/latex": [
       "$$\\frac{1}{2 a} \\left(- b + \\sqrt{- 4 a c + b^{2}}\\right)$$"
      ],
      "text/plain": [
       "        _____________\n",
       "       ╱           2 \n",
       "-b + ╲╱  -4⋅a⋅c + b  \n",
       "─────────────────────\n",
       "         2⋅a         "
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "root2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At some point I will probably make an optional library of Joy wrappers for SymPy functions, and either load it automatically if SymPy installation is available or have a CLI switch or something.  There's a huge amount of incredibly useful stuff and I don't see why Joy shouldn't expose another interface for using it.  (As an example, the symbolic expressions can be \"lambdafied\" into very fast versions, i.e. a function that takes `a`, `b`, and `c` and computes the value of the root using just low-level fast code, bypassing Joy and Python.  Also, Numpy, &c.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Partial Evaluation\n",
    "\n",
    "[Futamura projections](https://en.wikipedia.org/wiki/Futamura_projection)\n",
    "\n",
    "Starting with the example from [Partial Computation of Programs](http://hdl.handle.net/2433/103401)\n",
    "by [Yoshihiko Futamura](http://fi.ftmr.info/) of a function to compute `u` to the `k`th power:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(u, k):\n",
    "    z = 1\n",
    "    while k != 0:\n",
    "        if odd(k):\n",
    "            z = z * u\n",
    "        k = k / 2\n",
    "        u = u * u\n",
    "    return z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Partial evaluation with `k = 5`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F5(u):\n",
    "    z = 1 * u\n",
    "    u = u * u\n",
    "    u = u * u\n",
    "    z = z * u\n",
    "    return z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Translate `F(u, k)` to Joy\n",
    "\n",
    "    u k 1                    # z = 1\n",
    "        [pop] [Fw] while     # the while statement\n",
    "        popopd               # discard u k, \"return\" z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What's Fw?\n",
    "\n",
    "\n",
    "    u k z [pop odd] [Ft] [] ifte   # the if statement\n",
    "          [2 //] dip               # k = k / 2  floordiv\n",
    "          [sqr] dipd               # u = u * u\n",
    "\n",
    "          [[sqr] dip 2 //] dip     # We can merge last two lines."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Helper function Ft (to compute z = z * u).\n",
    "\n",
    "\n",
    "       u k z Ft\n",
    "    ---------------\n",
    "       u k u*z\n",
    "\n",
    "\n",
    "    Ft == [over] dip *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Putting it together:\n",
    "\n",
    "    Ft == [over] dip *\n",
    "    Fb == [[sqr] dip 2 //] dip\n",
    "    Fw == [pop odd] [Ft] [] ifte Fb\n",
    "     F == 1 [pop] [Fw] while popopd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('odd == 2 %')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('Ft == [over] dip *')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('Fb == [[sqr] dip 2 //] dip')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('Fw == [pop odd] [Ft] [] ifte Fb')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('F == 1 [pop] [Fw] while popopd')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try it out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "32\n"
     ]
    }
   ],
   "source": [
    "J('2 5 F')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to elide the tests let's define special versions of `while` and `ifte`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "from joy.joy import joy\n",
    "from joy.library import FunctionWrapper\n",
    "from joy.parser import Symbol\n",
    "from joy.utils.stack import concat\n",
    "\n",
    "\n",
    "S_while = Symbol('while')\n",
    "\n",
    "\n",
    "@FunctionWrapper\n",
    "def while_(S, expression, dictionary):\n",
    "    '''[if] [body] while'''\n",
    "    (body, (if_, stack)) = S\n",
    "    if joy(stack, if_, dictionary)[0][0]:\n",
    "        expression = concat(body, (if_, (body, (S_while, expression))))\n",
    "    return stack, expression, dictionary\n",
    "\n",
    "\n",
    "@FunctionWrapper\n",
    "def ifte(stack, expression, dictionary):\n",
    "    '''[if] [then] [else] ifte'''\n",
    "    (else_, (then, (if_, stack))) = stack\n",
    "    if_res = joy(stack, if_, dictionary)[0][0]\n",
    "    quote = then if if_res else else_\n",
    "    expression = concat(quote, expression)\n",
    "    return (stack, expression, dictionary)\n",
    "\n",
    "\n",
    "D['ifte'] = ifte\n",
    "D['while'] = while_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And with a SymPy symbol for the `u` argument:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                         u 5 . F\n",
      "                         u 5 . 1 [pop] [Fw] while popopd\n",
      "                       u 5 1 . [pop] [Fw] while popopd\n",
      "                 u 5 1 [pop] . [Fw] while popopd\n",
      "            u 5 1 [pop] [Fw] . while popopd\n",
      "                       u 5 1 . Fw [pop] [Fw] while popopd\n",
      "                       u 5 1 . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
      "             u 5 1 [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
      "        u 5 1 [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd\n",
      "     u 5 1 [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd\n",
      "                       u 5 1 . Ft Fb [pop] [Fw] while popopd\n",
      "                       u 5 1 . [over] dip * Fb [pop] [Fw] while popopd\n",
      "                u 5 1 [over] . dip * Fb [pop] [Fw] while popopd\n",
      "                         u 5 . over 1 * Fb [pop] [Fw] while popopd\n",
      "                       u 5 u . 1 * Fb [pop] [Fw] while popopd\n",
      "                     u 5 u 1 . * Fb [pop] [Fw] while popopd\n",
      "                       u 5 u . Fb [pop] [Fw] while popopd\n",
      "                       u 5 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd\n",
      "      u 5 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd\n",
      "                         u 5 . [sqr] dip 2 // u [pop] [Fw] while popopd\n",
      "                   u 5 [sqr] . dip 2 // u [pop] [Fw] while popopd\n",
      "                           u . sqr 5 2 // u [pop] [Fw] while popopd\n",
      "                           u . dup mul 5 2 // u [pop] [Fw] while popopd\n",
      "                         u u . mul 5 2 // u [pop] [Fw] while popopd\n",
      "                        u**2 . 5 2 // u [pop] [Fw] while popopd\n",
      "                      u**2 5 . 2 // u [pop] [Fw] while popopd\n",
      "                    u**2 5 2 . // u [pop] [Fw] while popopd\n",
      "                      u**2 2 . u [pop] [Fw] while popopd\n",
      "                    u**2 2 u . [pop] [Fw] while popopd\n",
      "              u**2 2 u [pop] . [Fw] while popopd\n",
      "         u**2 2 u [pop] [Fw] . while popopd\n",
      "                    u**2 2 u . Fw [pop] [Fw] while popopd\n",
      "                    u**2 2 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
      "          u**2 2 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
      "     u**2 2 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd\n",
      "  u**2 2 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd\n",
      "                    u**2 2 u . Fb [pop] [Fw] while popopd\n",
      "                    u**2 2 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd\n",
      "   u**2 2 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd\n",
      "                      u**2 2 . [sqr] dip 2 // u [pop] [Fw] while popopd\n",
      "                u**2 2 [sqr] . dip 2 // u [pop] [Fw] while popopd\n",
      "                        u**2 . sqr 2 2 // u [pop] [Fw] while popopd\n",
      "                        u**2 . dup mul 2 2 // u [pop] [Fw] while popopd\n",
      "                   u**2 u**2 . mul 2 2 // u [pop] [Fw] while popopd\n",
      "                        u**4 . 2 2 // u [pop] [Fw] while popopd\n",
      "                      u**4 2 . 2 // u [pop] [Fw] while popopd\n",
      "                    u**4 2 2 . // u [pop] [Fw] while popopd\n",
      "                      u**4 1 . u [pop] [Fw] while popopd\n",
      "                    u**4 1 u . [pop] [Fw] while popopd\n",
      "              u**4 1 u [pop] . [Fw] while popopd\n",
      "         u**4 1 u [pop] [Fw] . while popopd\n",
      "                    u**4 1 u . Fw [pop] [Fw] while popopd\n",
      "                    u**4 1 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
      "          u**4 1 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
      "     u**4 1 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd\n",
      "  u**4 1 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd\n",
      "                    u**4 1 u . Ft Fb [pop] [Fw] while popopd\n",
      "                    u**4 1 u . [over] dip * Fb [pop] [Fw] while popopd\n",
      "             u**4 1 u [over] . dip * Fb [pop] [Fw] while popopd\n",
      "                      u**4 1 . over u * Fb [pop] [Fw] while popopd\n",
      "                 u**4 1 u**4 . u * Fb [pop] [Fw] while popopd\n",
      "               u**4 1 u**4 u . * Fb [pop] [Fw] while popopd\n",
      "                 u**4 1 u**5 . Fb [pop] [Fw] while popopd\n",
      "                 u**4 1 u**5 . [[sqr] dip 2 //] dip [pop] [Fw] while popopd\n",
      "u**4 1 u**5 [[sqr] dip 2 //] . dip [pop] [Fw] while popopd\n",
      "                      u**4 1 . [sqr] dip 2 // u**5 [pop] [Fw] while popopd\n",
      "                u**4 1 [sqr] . dip 2 // u**5 [pop] [Fw] while popopd\n",
      "                        u**4 . sqr 1 2 // u**5 [pop] [Fw] while popopd\n",
      "                        u**4 . dup mul 1 2 // u**5 [pop] [Fw] while popopd\n",
      "                   u**4 u**4 . mul 1 2 // u**5 [pop] [Fw] while popopd\n",
      "                        u**8 . 1 2 // u**5 [pop] [Fw] while popopd\n",
      "                      u**8 1 . 2 // u**5 [pop] [Fw] while popopd\n",
      "                    u**8 1 2 . // u**5 [pop] [Fw] while popopd\n",
      "                      u**8 0 . u**5 [pop] [Fw] while popopd\n",
      "                 u**8 0 u**5 . [pop] [Fw] while popopd\n",
      "           u**8 0 u**5 [pop] . [Fw] while popopd\n",
      "      u**8 0 u**5 [pop] [Fw] . while popopd\n",
      "                 u**8 0 u**5 . popopd\n",
      "                        u**5 . \n"
     ]
    }
   ],
   "source": [
    "stack = list_to_stack([5, sympy.symbols('u')])\n",
    "viewer = TracePrinter()\n",
    "try:\n",
    "    (result, _) = run('F', stack, D, viewer.viewer)[0]\n",
    "except Exception, e:\n",
    "    print e\n",
    "viewer.print_()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAWCAYAAADafVyIAAAABHNCSVQICAgIfAhkiAAAAUpJREFUSInt1DtLXUEUBeBPESLXFGITsDEqEdHKaLBMQBvFRsh/SG9nKouAjY0/IsQm2lgoNrENeaEgaKGVjyBaBLQQNRYzSYbjOVduzoWkcMGwmb1n1pr9YPjPMYOfmXWUHmiqg8g2XiT7q3oLXMq8OkVjHQS6sI89vMPjNNhQknwMFezgEabRh36cluTORQu+Y+qXI1ui18IkTOZc7oixxSoCZ9jCkyKBp9F+zrk8FO3XKgLN6MVh0YE9HBfEZoUMJhLfHJ6jE8NYxg8h21toiwQrBQJrMd6e+BZwgAthkt4LTc7FaCR4UxA/ERpYE9IeDEabV/8uIcMvZQQGos0jGY+2WoPvxDehllk8wKZQvpdlBD5Gkp7E14K3/vyU3bWSpp/dKp5hHUt4iBFsCHNdwe5fPPw3mjEvjN05PuEVWnGND2XI7/HvcAOaJEK/e6V+UwAAAABJRU5ErkJggg==\n",
      "text/latex": [
       "$$u^{5}$$"
      ],
      "text/plain": [
       " 5\n",
       "u "
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's try partial evaluation by hand and use a \"stronger\" thunk.\n",
    "\n",
    "Caret underscoring indicates terms that form thunks.  When an arg is unavailable for a computation we just postpone it until the arg becomes available and in the meantime treat the pending computation as one unit.\n",
    "\n",
    "                             u 5 . F\n",
    "                             u 5 . 1 [pop] [Fw] while popopd\n",
    "                           u 5 1 . [pop] [Fw] while popopd\n",
    "                     u 5 1 [pop] . [Fw] while popopd\n",
    "                u 5 1 [pop] [Fw] . while popopd\n",
    "                           u 5 1 . Fw [pop] [Fw] while popopd\n",
    "                           u 5 1 . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
    "                 u 5 1 [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
    "            u 5 1 [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd\n",
    "         u 5 1 [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd\n",
    "                           u 5 1 . Ft Fb [pop] [Fw] while popopd\n",
    "                           u 5 1 . [over] dip * Fb [pop] [Fw] while popopd\n",
    "                    u 5 1 [over] . dip * Fb [pop] [Fw] while popopd\n",
    "                             u 5 . over 1 * Fb [pop] [Fw] while popopd\n",
    "                           u 5 u . 1 * Fb [pop] [Fw] while popopd\n",
    "                         u 5 u 1 . * Fb [pop] [Fw] while popopd\n",
    "                           u 5 u . Fb [pop] [Fw] while popopd\n",
    "                           u 5 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd\n",
    "          u 5 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd\n",
    "                             u 5 . [sqr] dip 2 // u [pop] [Fw] while popopd\n",
    "                       u 5 [sqr] . dip 2 // u [pop] [Fw] while popopd\n",
    "                               u . sqr 5 2 // u [pop] [Fw] while popopd\n",
    "                               u . dup mul 5 2 // u [pop] [Fw] while popopd\n",
    "                         u dup * . 5 2 // u [pop] [Fw] while popopd\n",
    "                         ^^^^^^^"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "             u dup * 2 u [pop] [Fw] . while popopd\n",
    "                        u dup * 2 u . Fw [pop] [Fw] while popopd\n",
    "                        u dup * 2 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
    "              u dup * 2 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
    "         u dup * 2 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd\n",
    "      u dup * 2 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd\n",
    "                        u dup * 2 u . Fb [pop] [Fw] while popopd\n",
    "                        u dup * 2 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd\n",
    "       u dup * 2 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd\n",
    "                          u dup * 2 . [sqr] dip 2 // u [pop] [Fw] while popopd\n",
    "                    u dup * 2 [sqr] . dip 2 // u [pop] [Fw] while popopd\n",
    "                            u dup * . sqr 2 2 // u [pop] [Fw] while popopd\n",
    "                            u dup * . dup mul 2 2 // u [pop] [Fw] while popopd\n",
    "                      u dup * dup * . 2 2 // u [pop] [Fw] while popopd\n",
    "                      ^^^^^^^^^^^^^\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "w/ `K == u dup * dup *`\n",
    "\n",
    "                 K 1 u [pop] [Fw] . while popopd\n",
    "                            K 1 u . Fw [pop] [Fw] while popopd\n",
    "                            K 1 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
    "                  K 1 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd\n",
    "             K 1 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd\n",
    "          K 1 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd\n",
    "                            K 1 u . Ft Fb [pop] [Fw] while popopd\n",
    "                            K 1 u . [over] dip * Fb [pop] [Fw] while popopd\n",
    "                     K 1 u [over] . dip * Fb [pop] [Fw] while popopd\n",
    "                              K 1 . over u * Fb [pop] [Fw] while popopd\n",
    "                            K 1 K . u * Fb [pop] [Fw] while popopd\n",
    "                          K 1 K u . * Fb [pop] [Fw] while popopd\n",
    "                        K 1 K u * . Fb [pop] [Fw] while popopd\n",
    "                            ^^^^^"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "w/ `L == K u *`\n",
    "\n",
    "                         K 1 L . Fb [pop] [Fw] while popopd\n",
    "                         K 1 L . [[sqr] dip 2 //] dip [pop] [Fw] while popopd\n",
    "        K 1 L [[sqr] dip 2 //] . dip [pop] [Fw] while popopd\n",
    "                           K 1 . [sqr] dip 2 // L [pop] [Fw] while popopd\n",
    "                     K 1 [sqr] . dip 2 // L [pop] [Fw] while popopd\n",
    "                             K . sqr 1 2 // L [pop] [Fw] while popopd\n",
    "                             K . dup mul 1 2 // L [pop] [Fw] while popopd\n",
    "                           K K . mul 1 2 // L [pop] [Fw] while popopd\n",
    "                         K K * . 1 2 // L [pop] [Fw] while popopd\n",
    "                         ^^^^^\n",
    "                         K K * . 1 2 // L [pop] [Fw] while popopd\n",
    "                       K K * 1 . 2 // L [pop] [Fw] while popopd\n",
    "                     K K * 1 2 . // L [pop] [Fw] while popopd\n",
    "                       K K * 0 . L [pop] [Fw] while popopd\n",
    "                     K K * 0 L . [pop] [Fw] while popopd\n",
    "               K K * 0 L [pop] . [Fw] while popopd\n",
    "          K K * 0 L [pop] [Fw] . while popopd\n",
    "          ^^^^^\n",
    "                     K K * 0 L . popopd\n",
    "                             L . "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So:\n",
    "\n",
    "    K == u dup * dup *\n",
    "    L == K u *\n",
    "\n",
    "Our result \"thunk\" would be:\n",
    "    \n",
    "    u dup * dup * u *\n",
    "\n",
    "Mechanically, you could do:\n",
    "\n",
    "    u      dup * dup *  u   *\n",
    "    u u   [dup * dup *] dip *\n",
    "    u dup [dup * dup *] dip *\n",
    "\n",
    "\n",
    "    F5 == dup [dup * dup *] dip *\n",
    "\n",
    "But we can swap the two arguments to the final `*` to get all mentions of `u` to the left:\n",
    "\n",
    "\n",
    "    u u dup * dup * *\n",
    "\n",
    "\n",
    "Then de-duplicate \"u\":\n",
    "\n",
    "\n",
    "    u dup dup * dup * *\n",
    "\n",
    "\n",
    "To arrive at a startlingly elegant form for F5:\n",
    "\n",
    "\n",
    "    F5 == dup dup * dup * *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "          u . dup dup * dup * *\n",
      "        u u . dup * dup * *\n",
      "      u u u . * dup * *\n",
      "     u u**2 . dup * *\n",
      "u u**2 u**2 . * *\n",
      "     u u**4 . *\n",
      "       u**5 . \n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAWCAYAAADafVyIAAAABHNCSVQICAgIfAhkiAAAAUpJREFUSInt1DtLXUEUBeBPESLXFGITsDEqEdHKaLBMQBvFRsh/SG9nKouAjY0/IsQm2lgoNrENeaEgaKGVjyBaBLQQNRYzSYbjOVduzoWkcMGwmb1n1pr9YPjPMYOfmXWUHmiqg8g2XiT7q3oLXMq8OkVjHQS6sI89vMPjNNhQknwMFezgEabRh36cluTORQu+Y+qXI1ui18IkTOZc7oixxSoCZ9jCkyKBp9F+zrk8FO3XKgLN6MVh0YE9HBfEZoUMJhLfHJ6jE8NYxg8h21toiwQrBQJrMd6e+BZwgAthkt4LTc7FaCR4UxA/ERpYE9IeDEabV/8uIcMvZQQGos0jGY+2WoPvxDehllk8wKZQvpdlBD5Gkp7E14K3/vyU3bWSpp/dKp5hHUt4iBFsCHNdwe5fPPw3mjEvjN05PuEVWnGND2XI7/HvcAOaJEK/e6V+UwAAAABJRU5ErkJggg==\n",
      "text/latex": [
       "$$u^{5}$$"
      ],
      "text/plain": [
       " 5\n",
       "u "
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "stack = list_to_stack([sympy.symbols('u')])\n",
    "viewer = TracePrinter()\n",
    "try:\n",
    "    (result, _) = run('dup dup * dup * *', stack, D, viewer.viewer)[0]\n",
    "except Exception, e:\n",
    "    print e\n",
    "viewer.print_()\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I'm not sure how to implement these kinds of thunks.  I think you have to have support in the interpreter, or you have to modify all of the functions like `dup` to check for thunks in their inputs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Working on the compiler, from this:\n",
    "\n",
    "    dup dup * dup * *\n",
    "\n",
    "We can already generate:\n",
    "\n",
    "    ---------------------------------\n",
    "    (a0, stack) = stack\n",
    "    a1 = mul(a0, a0)\n",
    "    a2 = mul(a1, a1)\n",
    "    a3 = mul(a2, a0)\n",
    "    stack = (a3, stack)\n",
    "    ---------------------------------\n",
    "\n",
    "This is pretty old stuff...  (E.g. from 1999, M. Anton Ertl [Compilation of Stack-Based Languages](http://www.complang.tuwien.ac.at/projects/rafts.html) he goes a lot further for Forth.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## \"A Transformation Based Approach to Semantics-Directed Code Generation\"\n",
    "\n",
    "by Arthur Nunes-Harwitt\n",
    "\n",
    "https://dl.acm.org/citation.cfm?doid=2635648.2635652\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "def m(x, y): return x * y\n",
    "\n",
    "print m(2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "def m(x): return lambda y: x * y\n",
    "\n",
    "print m(2)(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lambda y: 2 * y\n",
      "6\n"
     ]
    }
   ],
   "source": [
    "def m(x): return \"lambda y: %(x)s * y\" % locals()\n",
    "\n",
    "print m(2)\n",
    "print eval(m(2))(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In Joy:\n",
    "\n",
    "    m == [*] cons\n",
    "\n",
    "    3 2 m i\n",
    "    3 2 [*] cons i\n",
    "    3 [2 *] i\n",
    "    3 2 *\n",
    "    6\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lambda b: 1 if 3 == 0 else b * p(3 - 1, b)\n"
     ]
    }
   ],
   "source": [
    "def p(n, b): # original\n",
    "    return 1 if n == 0 else b * p(n - 1, b)\n",
    "\n",
    "\n",
    "def p(n): # curried\n",
    "    return lambda b: 1 if n == 0 else b * p(n - 1, b)\n",
    "\n",
    "\n",
    "def p(n): # quoted\n",
    "    return \"lambda b: 1 if %(n)s == 0 else b * p(%(n)s - 1, b)\"  % locals()\n",
    "\n",
    "\n",
    "print p(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Original\n",
    "\n",
    "    p == [0 =] [popop 1] [-- over] [dip *] genrec\n",
    "\n",
    "    b n p\n",
    "    b n [0 =] [popop 1] [-- over [p] dip *]\n",
    "\n",
    "    b n -- over [p] dip *\n",
    "    b n-1  over [p] dip *\n",
    "    b n-1 b [p] dip *\n",
    "    b n-1 p b *\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "curried, quoted\n",
    "\n",
    "                        n p\n",
    "    ---------------------------------------------\n",
    "       [[n 0 =] [pop 1] [dup n --] [*] genrec]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lambda b: b * p(3 - 1, b)\n"
     ]
    }
   ],
   "source": [
    "def p(n): # lambda lowered\n",
    "    return (\n",
    "        lambda b: 1\n",
    "        if n == 0 else\n",
    "        lambda b: b * p(n - 1, b)\n",
    "        )\n",
    "\n",
    "\n",
    "def p(n): # lambda lowered quoted\n",
    "    return (\n",
    "        \"lambda b: 1\"\n",
    "        if n == 0 else\n",
    "        \"lambda b: b * p(%(n)s - 1, b)\"  % locals()\n",
    "        )\n",
    "\n",
    "print p(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "    p == [0 =] [[pop 1]] [ [-- [dup] dip p *] cons ]ifte\n",
    "\n",
    "\n",
    "    3 p\n",
    "    3 [-- [dup] dip p *] cons\n",
    "    [3 -- [dup] dip p *]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n"
     ]
    }
   ],
   "source": [
    "def p(n): # expression lifted\n",
    "    if n == 0:\n",
    "        return lambda b: 1\n",
    "    f = p(n - 1)\n",
    "    return lambda b: b * f(b)\n",
    "\n",
    "\n",
    "print p(3)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lambda b: b * (lambda b: b * (lambda b: b * (lambda b: 1)(b))(b))(b)\n",
      "8\n"
     ]
    }
   ],
   "source": [
    "def p(n): # quoted\n",
    "    if n == 0:\n",
    "        return \"lambda b: 1\"\n",
    "    f = p(n - 1)\n",
    "    return \"lambda b: b * (%(f)s)(b)\"  % locals()\n",
    "\n",
    "print p(3)\n",
    "print eval(p(3))(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "    p == [0 =] [pop [pop 1]] [-- p [dupdip *] cons] ifte\n",
    "\n",
    "\n",
    "    3 p\n",
    "    3 -- p [dupdip *] cons\n",
    "    2    p [dupdip *] cons\n",
    "    2 -- p [dupdip *] cons [dupdip *] cons\n",
    "    1    p [dupdip *] cons [dupdip *] cons\n",
    "    1 -- p [dupdip *] cons [dupdip *] cons [dupdip *] cons\n",
    "    0    p [dupdip *] cons [dupdip *] cons [dupdip *] cons\n",
    "    0 pop [pop 1] [dupdip *] cons [dupdip *] cons [dupdip *] cons\n",
    "    [pop 1] [dupdip *] cons [dupdip *] cons [dupdip *] cons\n",
    "    ...\n",
    "    [[[[pop 1] dupdip *] dupdip *] dupdip *]\n",
    "\n",
    "\n",
    "    2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i\n",
    "    2  [[[pop 1] dupdip *] dupdip *] dupdip *\n",
    "    2   [[pop 1] dupdip *] dupdip *  2 *\n",
    "    2    [pop 1] dupdip *  2      *  2 *\n",
    "    2     pop 1  2      *  2      *  2 *\n",
    "              1  2      *  2      *  2 *\n",
    "\n",
    "\n",
    "\n",
    "    p == [0 =] [pop [pop 1]] [--  p    [dupdip *] cons] ifte\n",
    "    p == [0 =] [pop [pop 1]] [-- [p] i [dupdip *] cons] ifte\n",
    "    p == [0 =] [pop [pop 1]] [--]   [i [dupdip *] cons] genrec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[[[pop 1] dupdip *] dupdip *] dupdip *]\n"
     ]
    }
   ],
   "source": [
    "J('3 p')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                                           . 2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i\n",
      "                                         2 . [[[[pop 1] dupdip *] dupdip *] dupdip *] i\n",
      "2 [[[[pop 1] dupdip *] dupdip *] dupdip *] . i\n",
      "                                         2 . [[[pop 1] dupdip *] dupdip *] dupdip *\n",
      "           2 [[[pop 1] dupdip *] dupdip *] . dupdip *\n",
      "                                         2 . [[pop 1] dupdip *] dupdip * 2 *\n",
      "                      2 [[pop 1] dupdip *] . dupdip * 2 *\n",
      "                                         2 . [pop 1] dupdip * 2 * 2 *\n",
      "                                 2 [pop 1] . dupdip * 2 * 2 *\n",
      "                                         2 . pop 1 2 * 2 * 2 *\n",
      "                                           . 1 2 * 2 * 2 *\n",
      "                                         1 . 2 * 2 * 2 *\n",
      "                                       1 2 . * 2 * 2 *\n",
      "                                         2 . 2 * 2 *\n",
      "                                       2 2 . * 2 *\n",
      "                                         4 . 2 *\n",
      "                                       4 2 . *\n",
      "                                         8 . \n"
     ]
    }
   ],
   "source": [
    "V('2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stack = list_to_stack([sympy.symbols('u')])\n",
    "(result, s) = run('p i', stack, D)[0]\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "From this:\n",
    "\n",
    "    p == [0 =] [pop pop 1] [-- over] [dip *] genrec\n",
    "\n",
    "To this:\n",
    "\n",
    "    p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try it with `F()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def odd(n): return n % 2\n",
    "\n",
    "\n",
    "def F(u, k):\n",
    "    z = 1\n",
    "    while k != 0:\n",
    "        if odd(k):\n",
    "            z = z * u\n",
    "        k = k / 2\n",
    "        u = u * u\n",
    "    return z\n",
    "\n",
    "F(2, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    def _F(u, k=k):\n",
    "        z = 1\n",
    "        while k != 0:\n",
    "            if odd(k):\n",
    "                z = z * u\n",
    "            k = k / 2\n",
    "            u = u * u\n",
    "        return z\n",
    "    return _F\n",
    "    \n",
    "F(5)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    def _F(u, k=k):\n",
    "        if k == 0:\n",
    "            z = 1\n",
    "        else:\n",
    "            z = F(k / 2)(u)\n",
    "            z *= z\n",
    "            if odd(k):\n",
    "                z = z * u\n",
    "        return z\n",
    "    return _F\n",
    "    \n",
    "F(5)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    if k == 0:\n",
    "        z = lambda u: 1\n",
    "    else:\n",
    "        f = F(k / 2)\n",
    "        def z(u):\n",
    "            uu = f(u)\n",
    "            uu *= uu\n",
    "            return uu * u if odd(k) else uu\n",
    "    return z\n",
    "    \n",
    "F(5)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    if k == 0:\n",
    "        z = lambda u: 1\n",
    "    else:\n",
    "        f = F(k / 2)\n",
    "        if odd(k):\n",
    "            z = lambda u: (lambda fu, u: fu * fu * u)(f(u), u)\n",
    "        else:\n",
    "            z = lambda u: (lambda fu, u: fu * fu)(f(u), u)\n",
    "    return z\n",
    "    \n",
    "F(5)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    if k == 0:\n",
    "        z = \"lambda u: 1\"\n",
    "    else:\n",
    "        f = F(k / 2)\n",
    "        if odd(k):\n",
    "            z = \"lambda u: (lambda fu, u: fu * fu * u)((%(f)s)(u), u)\" % locals()\n",
    "        else:\n",
    "            z = \"lambda u: (lambda fu, u: fu * fu)((%(f)s)(u), u)\" % locals()\n",
    "    return z\n",
    "    \n",
    "source = F(5)\n",
    "print source\n",
    "eval(source)(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hmm..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for n in range(4):\n",
    "    print F(n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    if k == 0:\n",
    "        z = \"lambda u: 1\"\n",
    "    elif k == 1:\n",
    "        z = \"lambda u: u\"\n",
    "    else:\n",
    "        f = F(k / 2)\n",
    "        if odd(k):\n",
    "            z = \"lambda u: (lambda fu, u: fu * fu * u)((%(f)s)(u), u)\" % locals()\n",
    "        else:\n",
    "            z = \"lambda u: (lambda fu, u: fu * fu)((%(f)s)(u), u)\" % locals()\n",
    "    return z\n",
    "    \n",
    "source = F(5)\n",
    "print source\n",
    "eval(source)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for n in range(4):\n",
    "    print F(n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    if k == 0:\n",
    "        z = \"lambda u: 1\"\n",
    "    elif k == 1:\n",
    "        z = \"lambda u: u\"\n",
    "    else:\n",
    "        m = k / 2\n",
    "        if odd(k):\n",
    "            if m == 0:\n",
    "                z = \"lambda u: 1\"\n",
    "            elif m == 1:\n",
    "                z = \"lambda u: u * u * u\"\n",
    "            else:\n",
    "                z = \"lambda u: (lambda fu, u: fu * fu * u)((%s)(u), u)\" % F(m)\n",
    "        else:\n",
    "            if m == 0:\n",
    "                z = \"lambda u: 1\"\n",
    "            elif m == 1:\n",
    "                z = \"lambda u: u * u\"\n",
    "            else:\n",
    "                z = \"lambda u: (lambda u: u * u)((%s)(u))\" % F(m)\n",
    "    return z\n",
    "    \n",
    "source = F(5)\n",
    "print source\n",
    "eval(source)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(k):\n",
    "    if k == 0:\n",
    "        z = \"lambda u: 1\"\n",
    "    elif k == 1:\n",
    "        z = \"lambda u: u\"\n",
    "    else:\n",
    "        m = k / 2\n",
    "        if m == 0:\n",
    "            z = \"lambda u: 1\"\n",
    "        \n",
    "        elif odd(k):\n",
    "            if m == 1:\n",
    "                z = \"lambda u: u * u * u\"\n",
    "            else:\n",
    "                z = \"lambda u: (lambda fu, u: fu * fu * u)((%s)(u), u)\" % F(m)\n",
    "        else:\n",
    "            if m == 1:\n",
    "                z = \"lambda u: u * u\"\n",
    "            else:\n",
    "                z = \"lambda u: (lambda u: u * u)((%s)(u))\" % F(m)\n",
    "    return z\n",
    "    \n",
    "source = F(5)\n",
    "print source\n",
    "eval(source)(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "for n in range(7):\n",
    "    source = F(n)\n",
    "    print n, '%2i' % eval(source)(2), source"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So that gets pretty good, eh?\n",
    "\n",
    "\n",
    "But looking back at the definition in Joy, it doesn't seem easy to directly apply this technique to Joy code:\n",
    "\n",
    "    Ft == [over] dip *\n",
    "    Fb == [[sqr] dip 2 //] dip\n",
    "    Fw == [pop odd] [Ft] [] ifte Fb\n",
    "     F == 1 [pop] [Fw] while popopd\n",
    "\n",
    "\n",
    "But a direct translation of the Python code..?\n",
    "\n",
    "\n",
    "    F == [\n",
    "      [[0 =] [pop 1]]\n",
    "      [[1 =] []]\n",
    "      [_F.0]\n",
    "      ] cond\n",
    "\n",
    "    _F.0 == dup 2 // [\n",
    "      [[0 =]     [pop 1]]\n",
    "      [[pop odd] _F.1]\n",
    "      [_F.2]\n",
    "      ] cond\n",
    "\n",
    "    _F.1 == [1 =] [pop [dup dup * *]] [popd F [dupdip over * *] cons] ifte\n",
    "    _F.2 == [1 =] [pop [dup *]]       [popd F [i dup *]         cons] ifte\n",
    "\n",
    "\n",
    "Try it:\n",
    "\n",
    "    5 F\n",
    "    5 [ [[0 =] [pop 1]] [[1 =] []] [_F.0] ] cond\n",
    "    5 _F.0\n",
    "    5 dup 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond\n",
    "    5 5 2 //   [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond\n",
    "\n",
    "    5 2 [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond\n",
    "    5 2 _F.1\n",
    "    5 2 [1 =] [popop [dup dup * *]] [popd F [dupdip over * *] cons] ifte\n",
    "    5 2                              popd F [dupdip over * *] cons\n",
    "      2                                   F [dupdip over * *] cons\n",
    "\n",
    "    2 F [dupdip over * *] cons\n",
    "\n",
    "    2 F\n",
    "    2 [ [[0 =] [pop 1]] [[1 =] []] [_F.0] ] cond\n",
    "    2 _F.0\n",
    "    2 dup 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond\n",
    "    2 2 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond\n",
    "    2 1 [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond\n",
    "    2 1 _F.2\n",
    "    2 1 [1 =] [popop [dup *]] [popd F [i dup *] cons] ifte\n",
    "    2 1 popop [dup *]\n",
    "    [dup *]\n",
    "\n",
    "\n",
    "    2 F     [dupdip over * *] cons\n",
    "    [dup *] [dupdip over * *] cons\n",
    "    [[dup *] dupdip over * *]\n",
    "\n",
    "And here it is in action:\n",
    "\n",
    "    2 [[dup *] dupdip over * *] i\n",
    "    2  [dup *] dupdip over * *\n",
    "    2   dup *  2      over * *\n",
    "    2   2   *  2      over * *\n",
    "    4          2      over * *\n",
    "    4          2      4    * *\n",
    "    4          8             *\n",
    "    32\n",
    "\n",
    "\n",
    "\n",
    "So, it works, but in this case the results of the partial evaluation are more elegant.\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try it with `hylomorphism()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c, F, P, G):\n",
    "    '''Return a hylomorphism function H.'''\n",
    "\n",
    "    def H(a):\n",
    "        if P(a):\n",
    "            result = c\n",
    "        else:\n",
    "            b, aa = G(a)\n",
    "            result = F(b, H(aa))\n",
    "        return result\n",
    "\n",
    "    return H"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### First, curry\n",
    "\n",
    "With abuse of syntax:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    return lambda F: lambda P: lambda G: lambda a: (\n",
    "        if P(a):\n",
    "            result = c\n",
    "        else:\n",
    "            b, aa = G(a)\n",
    "            result = F(b)(H(aa))\n",
    "        return result\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### lambda lowering\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    def r(a):\n",
    "        def rr(P):\n",
    "            if P(a):\n",
    "                return lambda F: lambda G: c\n",
    "            return lambda F: lambda G: (\n",
    "                    b, aa = G(a)\n",
    "                    return F(b)(H(aa))\n",
    "                )\n",
    "        return rr\n",
    "    return r\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### expression lifting\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    def r(a):\n",
    "        def rr(P):\n",
    "            def rrr(G):\n",
    "                if P(a):\n",
    "                    return lambda F: c\n",
    "                b, aa = G(a)\n",
    "                H = hylomorphism(c)(aa)(P)(G)\n",
    "                return lambda F: F(b)(H(F))\n",
    "            return rrr\n",
    "        return rr\n",
    "    return r\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### quoted\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    def r(a):\n",
    "        def rr(P):\n",
    "            def rrr(G):\n",
    "                if P(a):\n",
    "                    return \"lambda F: %s\" % (c,)\n",
    "                b, aa = G(a)\n",
    "                H = hylomorphism(c)(aa)(P)(G)\n",
    "                return \"lambda F: F(%(b)s)((%(H)s)(F))\" % locals()\n",
    "            return rrr\n",
    "        return rr\n",
    "    return r\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hylomorphism(0)(3)(lambda n: n == 0)(lambda n: (n-1, n-1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def F(a):\n",
    "    def _F(b):\n",
    "        print a, b\n",
    "        return a + b\n",
    "    return _F\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "F(2)(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(hylomorphism(0)(5)(lambda n: n == 0)(lambda n: (n-1, n-1)))(F)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(hylomorphism([])(5)(lambda n: n == 0)(lambda n: (n-1, n-1)))(lambda a: lambda b: [a] + b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hylomorphism(0)([1, 2, 3])(lambda n: not n)(lambda n: (n[0], n[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hylomorphism([])([1, 2, 3])(lambda n: not n)(lambda n: (n[1:], n[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(hylomorphism([])([1, 2, 3])(lambda n: not n)(lambda n: (n[1:], n[1:])))(lambda a: lambda b: [a] + b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    return lambda a: lambda P: (\n",
    "        if P(a):\n",
    "            result = lambda F: lambda G: c\n",
    "        else:\n",
    "            result = lambda F: lambda G: (\n",
    "                b, aa = G(a)\n",
    "                return F(b)(H(aa))\n",
    "            )\n",
    "        return result\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    return lambda a: (\n",
    "        lambda F: lambda P: lambda G: c\n",
    "        if P(a) else\n",
    "        lambda F: lambda P: lambda G: (\n",
    "            b, aa = G(a)\n",
    "            return F(b)(H(aa))\n",
    "        )\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hylomorphism(c):\n",
    "    return lambda a: lambda G: (\n",
    "        lambda F: lambda P: c\n",
    "        if P(a) else\n",
    "        b, aa = G(a)\n",
    "        lambda F: lambda P: F(b)(H(aa))\n",
    "    )\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
