{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Partial Evalutation and Currying\n", "\n", "Often when programming we want to evaluate a function only halfway. Consider the following definition of cumulative sum\n", "\n", "```\n", "def cumsum(data):\n", " return accumulate(add, data)\n", "```\n", "\n", "or of `fib_many` which applies the Fibonacci function, `fib`, onto a list\n", "\n", "```\n", "def fib_many(data):\n", " return map(fib, data)\n", "```\n", "\n", "In each case we specialize a higher order function (`accumulate` or `map`) with a single specific argument, leaving the second argument open for future use. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Obligatory set of small functions\n", "\n", "from toolz import accumulate\n", "\n", "def add(x, y):\n", " return x + y\n", "\n", "def mul(x, y):\n", " return x * y\n", "\n", "def fib(n):\n", " a, b = 0, 1\n", " for i in range(n):\n", " a, b = b, a + b\n", " return b" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "# We commonly use a lambda expression and the equals sign\n", "cumsum = lambda data: accumulate(add, data)\n", "\n", "# This is perfectly equivalent to the function definition\n", "def cumsum(data):\n", " return accumulate(add, data)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "# Or we can use the `partial` function from functools\n", "# Partial inserts an argument into the first place\n", "from functools import partial\n", "cumsum = partial(accumulate, add)\n", "\n", "# Semantically like the following:\n", "# cumsum(whatever) = accumulate(add, whatever)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise\n", "\n", "Partially evaluate the `mul` function to create a new function `double`" ] }, { "cell_type": "code", "collapsed": false, "input": [ "double = ...\n", "assert double(5) = 10" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (, line 1)", "output_type": "pyerr", "traceback": [ "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m double = ...\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Curry\n", "\n", "Currying provides syntacic sugar for partial evaluation. \n", "\n", "Curry is a higher order function that changes the behavior of what a function does when it is called with an incomplete set of arguments\n", "\n", "Normally Python would raise a TypeError.\n", "\n", "Now it returns a partial" ] }, { "cell_type": "code", "collapsed": false, "input": [ "mul(2)" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "mul() takes exactly 2 arguments (1 given)", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmul\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mTypeError\u001b[0m: mul() takes exactly 2 arguments (1 given)" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "from toolz import curry\n", "mul = curry(mul)\n", "mul(2)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This allows for the idiomatic expression of functional statements" ] }, { "cell_type": "code", "collapsed": false, "input": [ "accumulate = curry(accumulate)\n", "map = curry(map)\n", "\n", "cumsum = accumulate(add)\n", "cumprod = accumulate(mul)\n", "\n", "fibMany = map(fib)\n", "\n", "fibMany(range(10))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Curried namespace\n", "\n", "Toolz contains a separate namespace for curried functions" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from toolz.curried import map" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the same as the following:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from toolz import map, curry\n", "map = curry(map)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 } ], "metadata": {} } ] }