{ "info": { "author": "johnklee", "author_email": "puremonkey2007@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "Overview and Table of Contents\n==========================\nThis package is used as utility to support more thorough FP (functional programming) functionalities in Python. For more, you can refer to [FPU.pptx](https://github.com/johnklee/fpu/blob/master/docs/FPU.pptx)\n\n* [**FP Introduction**: Functional programming has a long history. In a nutshell, its a style of programming where you focus on transforming data through the use of small expressions that ideally don\u2019t contain side effects.](#fpi)\n * [Feature of FP](#fpi_s1)\n * [Pro & Con of FP](#fpi_s2)\n * [Python's Functional Features](#fpi_s3)\n* [**Example API Usage**: To have a taste on how this utility to help you to program in FP](#examples)\n * [Gemstones](#exp_gemstones)\n* [**More about FP**: Appendix of other resources](#supplement)\n\n# FP Introduction \n([Source link](https://www.youtube.com/watch?v=Ta1bAMOMFOI)) Functional programming has a long history. In a nutshell, its a style of programming where you focus on transforming data through the use of small expressions that ideally don\u2019t contain side effects. In other words, when you call my_fun(1, 2), it will alway return the same result. This is achieved by immutable data typical of a functional language.\n* Lisp 1958\n* Renaissance: F#, Haskell, Erlang ...\n* Used in industry such as Trading, Algorithmic, and Telecommunication etc\n\n## Features of FP \n* First-class function/Higher order function\n* Pure functions without side effects\n* Immutable data structures\n* Preserve state in functions (Closure, Cury)\n* Recursion instead of loops/iteration\n\n### First-class function/Higher order function\nWith this feature, you can:\n* Use function as argument(s)\n* Function can return function\n* Enables functional composition\n\nLet's take a example to explain the usage of functional composition. Below is the imperative way to get the minimum value of all maximum values from values:\n```python\ndataSet = [{'values':[1, 2, 3]}, {'values':[4, 5]}]\n\n# Imperative\ndef min_max_imp(dataSet):\n max_list = []\n for d in dataSet:\n max_list.append(max(d['values']))\n\n return min(max_list)\n\nmin_max_imp(dataSet) # 3\n```\nAbove function `min_max_imp` actually comprises two sub steps:\n* Get the maximum of each values\n* Get the minimum of list collected by previous step\n\nThis implies you can compose above two steps (function) into one by leveraging exist functions:\n```python\n# FP\nfrom fpu.fp import *\nfrom functools import reduce, partial\n\n# compose2(f, g) = f(g())\nmin_max = compose2(\n partial(reduce, min), \\\n partial(map, lambda d: max(d['values']))\n )\nmin_max(dataSet)\n```\nWith composing feature, you can write less dump code and make use of exist function to generate new function!\n\n### Pure functions without side effects\nThe [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) can refer to many things. I suggest you to read below materials to know more:\n* [Get started with Functional Programming | otsconf 2015](https://www.youtube.com/watch?v=6f5dt923FmQ&t=96s)\n* [Functional Programming with Python](https://www.youtube.com/watch?v=Ta1bAMOMFOI)\n\n### Immutable data structures\nThere are many python packages support you to carry out this requirement. One of them is [**pyrsistent**](https://github.com/tobgu/pyrsistent). Below is a few usage of it to show `immutable data`:\n```python\nIn [2]: v1 = v(1, 2, 3) \n\nIn [3]: v2 = v1.append(4) # Any operation on v1 will return new vectory to reflect the modification\n\nIn [4]: v1 \nOut[4]: pvector([1, 2, 3]) # v1 stay immutable\n\nIn [5]: v2 \nOut[5]: pvector([1, 2, 3, 4]) # v2 reflect the change for appending 4\n\nIn [6]: v3 = v2.set(1, 5) \n\nIn [7]: v2 \nOut[7]: pvector([1, 2, 3, 4])\n\nIn [8]: v3\nOut[8]: pvector([1, 5, 3, 4])\n```\nAbove is a demonstration on data structure vector. There are more for [**PMap**](https://github.com/tobgu/pyrsistent#pmap), [**PSet**](https://github.com/tobgu/pyrsistent#pset), [**PRecord**](https://github.com/tobgu/pyrsistent#precord) and [**PClass**](https://github.com/tobgu/pyrsistent#pclass).\n\n### Preserve state in functions (Closure, Cury)\nA [Closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) which simply creates a scope that allows the function to access and manipulate the variables in enclosing scopes. Normally, you will follow below steps to create a Closure in Python:\n* We have to create a nested function (a function inside another function).\n* This nested function has to refer to a variable defined inside the enclosing function.\n* The enclosing function has to return the nested function\n\nBelow is a simple example to create closure:\n```python\nIn [10]: def addN(n): \n ...: def _add(v): \n ...: return v + n \n ...: return _add \n ...: \n\nIn [11]: addOne = addN(1) \n\nIn [12]: addOne(2) \nOut[12]: 3\n\nIn [13]: addOne(3) \nOut[13]: 4\n\nIn [14]: addTwo = addN(2) \n\nIn [15]: addTwo(2) \nOut[15]: 4\n\nIn [16]: addTwo(3) \nOut[16]: 5\n```\n[Currying](https://en.wikipedia.org/wiki/Currying) is like a kind of incremental binding of function arguments. It is the technique of breaking down the evaluation of a function that takes multiple arguments into evaluating a sequence of single-argument functions:\n* Concept by Haskell Curry\n* Translating a function that takes multiple arguments into a sequence of functions which all take 1 argument. e.g.: `add(a, b)` AND `add(a)(b)`\n* Improves reusability and composition\n* In some languages (Haskell, F#) functions are curried by default\n\nUnfortunately, Python doesn't support curring in default. Below is a workaround for you to do curring in Python3:\n```python\nfrom inspect import signature\n\ndef curry(x, argc=None):\n if argc is None:\n argc = len(signature(x).parameters)\n\n def p(*a):\n if len(a) == argc:\n return x(*a)\n def q(*b):\n return x(*(a + b))\n return curry(q, argc - len(a))\n return p\n\n@curry\ndef myfun(a,b,c):\n print(\"{}-{}-{}\".format(a,b,c))\n\nmyfun(1, 2, 3)\nmyfun(1, 2)(3)\nmyfun(1)(2)(3) \n```\n\n### Recursion instead of loops/iteration\nFP favors recursion over for-loop. However, the recursion will use precious resource as stack. You can use below sample code to retrieve the recursive limit:\n```python\nIn [17]: import sys \n\nIn [18]: sys.getrecursionlimit() \nOut[18]: 3000\n```\nThis package use class **TailCall** to store the function call in heap instead of stack. Below is one usage example:\n```python\nIn [1]: def fibRec(n, x=0, y=1): \n ...: if n == 0: \n ...: return x \n ...: else: \n ...: return fibRec(n-1, y, x + y) \n ...: \n\nIn [2]: fibRec(3) \nOut[2]: 2\n\nIn [3]: fibRec(3000) \n---------------------------------------------------------------------------\nRecursionError Traceback (most recent call last)\n in \n----> 1 fibRec(3000)\n\n in fibRec(n, x, y)\n 3 return x\n 4 else:\n----> 5 return fibRec(n-1, y, x + y)\n 6 \n\n... last 1 frames repeated, from the frame below ...\n\n in fibRec(n, x, y)\n 3 return x\n 4 else:\n----> 5 return fibRec(n-1, y, x + y)\n 6 \n\nRecursionError: maximum recursion depth exceeded in comparison\n```\nThe exception is raised owing to recursion limitation. We can get by this limition by adopting **TailCall**:\n```python\nIn [5]: from fpu.fp import * \n\nIn [6]: ret = TailCall.ret; sus = TailCall.sus\nIn [22]: def fib(n, x=0, y=1): \n ...: return ret(x) if n == 0 else sus(Supplier(fib, n-1, y, x + y)) \n ...: \n\nIn [23]: fib(3) \nOut[23]: \n\nIn [24]: fib(3).eval() \nOut[24]: 2\n\nIn [25]: fib(3000).eval() \nOut[25]: 410615886307971260333568378719267105220125108637369252408885430926905584274113403731330491660850044560830036835706942274588569362145476502674373045446852160486606292497360503469773453733196887405847255290082049086907512622059054542195889758031109222670849274793859539133318371244795543147611073276240066737934085191731810993201706776838934766764778739502174470268627820918553842225858306408301661862900358266857238210235802504351951472997919676524004784236376453347268364152648346245840573214241419937917242918602639810097866942392015404620153818671425739835074851396421139982713640679581178458198658692285968043243656709796000\n```\n\n## Pro & Con of FP \nHere we will be going to review what advantage/disadvantage FP will bring to you.\n\n### Advantages of FP\n* Absence of side effects can make your programs more robust\n* Programs tend to be more modular come and typically in smaller building blocks\n* Better testable - call with same parameters always returns same result\n* Focus on algorithms\n* Conceptional fit with parallel / concurrent programming\n* Live upates - Install new release while running\n\n### Disadvantages of FP\n* Solutions to the same problem can look very different than procedural/OO ones\n* Finding good developers can be hard\n* Not equally useful for all types of problems\n* Input/Output are side effects and need special treatment\n* Recursion is \"an order of magnitude more complex\" than loops/iterations\n* Immutable data structures may increase run times\n\n## Python's Functional Features - Overview \n* Pure functions (sort of)\n* Closures - hold state in functions\n* Functions as objects and decorators\n* Immutable data types (tuple, freezeSet)\n* Lazy evaluation - generators\n* List (dictionary, set) comprehensions\n* [functools](https://docs.python.org/3.5/library/functools.html), itertools, lambda, map, filter\n* Recursion - try to avoid, recursion limit has a reason\n\n# Example API Usage \nHere we are going to look at few examples from [HackerRank](https://www.hackerrank.com/) to know how FP can help you write code gracefully.\n\n## Gemstones \nThe [problem](https://www.hackerrank.com/challenges/gem-stones/problem) simply ask you to extract element exist in every rock. The imperative approach will look like:\n```python\narr = ['abcdde', 'baccd', 'eeabg']\n# Complete the gemstones function below.\ndef gemstones_imp(arr):\n set_list = []\n for s in arr:\n set_list.append(set(list(s)))\n\n # Imperative code\n uset = None\n for aset in set_list:\n if uset is None:\n uset = aset\n else:\n uset = uset & aset\n\n return len(uset)\n\nprint(\"Output of gemstones_imp={}\".format(gemstones_imp(arr)))\n``` \nThe FP (declarative approach) code will be neat and graceful:\n```python\nfrom fpu.flist import *\n\ndef gemstones_dec(arr):\n rlist = fl(arr)\n return len(\n rlist.map(lambda r: set(list(r))) \\\n .reduce(lambda a, b: a & b)\n )\n\nprint(\"Output of gemstones_imp={}\".format(gemstones_dec(arr)))\n```\n\n# Supplement \n* [FP In Python - Ch1. (Avoiding) Flow Control](http://puremonkey2010.blogspot.com/2018/05/fp-in-python-ch1-avoiding-flow-control.html)\n* [FP In Python - Ch2. Callables](http://puremonkey2010.blogspot.com/2018/05/fp-in-python-ch2-callables.html)\n* [FP In Python - Ch3. Lazy Evaluation](http://puremonkey2010.blogspot.com/2018/05/fp-in-python-ch3-lazy-evaluation.html)\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/johnklee/fpu", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "fpu", "package_url": "https://pypi.org/project/fpu/", "platform": "", "project_url": "https://pypi.org/project/fpu/", "project_urls": { "Homepage": "https://github.com/johnklee/fpu" }, "release_url": "https://pypi.org/project/fpu/0.2.5/", "requires_dist": null, "requires_python": "", "summary": "Functional Programming Utility", "version": "0.2.5" }, "last_serial": 5137075, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "e51366893fb39e2e1f322f243252c89e", "sha256": "bd4738376259164b4f27c8b757cdb54c6d0b89c73391904e800330909b1a0cf6" }, "downloads": -1, "filename": "fpu-0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "e51366893fb39e2e1f322f243252c89e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6758, "upload_time": "2019-04-13T06:02:06", "url": "https://files.pythonhosted.org/packages/5b/e5/5719e4f26d9689daa56aa576065c1c2f88cdac86301ae064f67137be95c4/fpu-0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d4afdaad5fc7b90447d9d92fabec6e08", "sha256": "fea8ff0c81f60b8c58916a8348c7024b5228cab2b9526c640c44929de7df411d" }, "downloads": -1, "filename": "fpu-0.1.tar.gz", "has_sig": false, "md5_digest": "d4afdaad5fc7b90447d9d92fabec6e08", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10235, "upload_time": "2019-04-13T06:02:09", "url": "https://files.pythonhosted.org/packages/71/1e/eb3509661ff7c25603b229eb68785562d339624805ac0e1892c13cda6805/fpu-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "45ec79d23ac7325b599f62391a573ad8", "sha256": "4cff676b019549f2d2a6d0837e2e720688bea2e042f01381ebd4e18bba6de18a" }, "downloads": -1, "filename": "fpu-0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "45ec79d23ac7325b599f62391a573ad8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6689, "upload_time": "2019-04-13T06:44:34", "url": "https://files.pythonhosted.org/packages/99/7a/9e6deb35b233072af2fb63cf9c071a7364db4fc817e5265fa959710327b5/fpu-0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a3a84c05759c6142513bab4441db870c", "sha256": "cdf42a100beed0c492a52b441d5191bbc579d32d834aa4c3996c0af1ed68a655" }, "downloads": -1, "filename": "fpu-0.2.tar.gz", "has_sig": false, "md5_digest": "a3a84c05759c6142513bab4441db870c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10252, "upload_time": "2019-04-13T06:48:36", "url": "https://files.pythonhosted.org/packages/97/95/1cec044177bad88c40bbe2f9852161a1f1c03d52e97a3e1d6bab9f2010f2/fpu-0.2.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "35d732aa16680b92baffe0b920748236", "sha256": "2d53060236ecff70f5111ce5064cf5a88f634143ade9c57100961783041e894a" }, "downloads": -1, "filename": "fpu-0.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "35d732aa16680b92baffe0b920748236", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11405, "upload_time": "2019-04-13T06:52:14", "url": "https://files.pythonhosted.org/packages/a2/5e/2eb7b039c63f375891494540bcc0bb2e549aa60d4aaa3f9e4e78ac1fa6ee/fpu-0.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "61182f9ea523f71d16d64ca10b261ca6", "sha256": "1042008241cf748897295799ed95824fea7d25890ae8dc012f203ef358e3be51" }, "downloads": -1, "filename": "fpu-0.2.1.tar.gz", "has_sig": false, "md5_digest": "61182f9ea523f71d16d64ca10b261ca6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12596, "upload_time": "2019-04-13T06:52:15", "url": "https://files.pythonhosted.org/packages/b2/cc/49e7dda522abed09d29df4c7eeb1f91296b3852f1bcf05fd8d54b0ccc605/fpu-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "f4d9199d7876ea1a12850c988c04cee2", "sha256": "18386a00efa5b4bfcf681090f3103b4a4ae3e26cc750aec547c220dd2e5d0607" }, "downloads": -1, "filename": "fpu-0.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "f4d9199d7876ea1a12850c988c04cee2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11560, "upload_time": "2019-04-13T07:02:38", "url": "https://files.pythonhosted.org/packages/90/58/793932ffc24ff4a92cb3a84cb85e5567e2bd9438ecd6dd07a91a8a06c427/fpu-0.2.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "57e39d0a97ac76fa2d8531b0e602d0ec", "sha256": "7cd5a82b5277eaa14920dc8df1957087122e1ce2456211764aa940d2b9ec5163" }, "downloads": -1, "filename": "fpu-0.2.2.tar.gz", "has_sig": false, "md5_digest": "57e39d0a97ac76fa2d8531b0e602d0ec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12811, "upload_time": "2019-04-13T07:02:39", "url": "https://files.pythonhosted.org/packages/37/0d/e326903b6df1ff6d5b428d6e3290576f2952d0f4036ae78bde14d195e5e0/fpu-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "f089a945b080b71ff08df249a428801c", "sha256": "a1bd144ccf5bee0a6413ad29b9b2085c8a5f04afed1b0a96065515d3c1de99ef" }, "downloads": -1, "filename": "fpu-0.2.3-py3-none-any.whl", "has_sig": false, "md5_digest": "f089a945b080b71ff08df249a428801c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11585, "upload_time": "2019-04-13T07:05:08", "url": "https://files.pythonhosted.org/packages/95/cb/8b5a9681d443e6ee30f1f4b97d9ef8f76a77b73d17e36c56cbe259696cd6/fpu-0.2.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "83b0cf6590f5226b314c2898c8181969", "sha256": "72f6cd614af986fde4e25f6f877c56d05a859222a794128e5c46f3479c66bf98" }, "downloads": -1, "filename": "fpu-0.2.3.tar.gz", "has_sig": false, "md5_digest": "83b0cf6590f5226b314c2898c8181969", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12859, "upload_time": "2019-04-13T07:05:10", "url": "https://files.pythonhosted.org/packages/ae/a5/8470ad6341aaccc3a4a652943f582ecdeaef8ee026ee8d2edde2c5910aae/fpu-0.2.3.tar.gz" } ], "0.2.5": [ { "comment_text": "", "digests": { "md5": "336402316fdaf17d075154944062acac", "sha256": "d7f9ecf8eae73f86360fd2d98d0d6543c1460e3de8432c1d6b5a669f081ba56b" }, "downloads": -1, "filename": "fpu-0.2.5-py3-none-any.whl", "has_sig": false, "md5_digest": "336402316fdaf17d075154944062acac", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11597, "upload_time": "2019-04-13T07:07:41", "url": "https://files.pythonhosted.org/packages/44/ad/a7ffd51f71476e46961b2b532a4e42cc08256a03595f9db4e64fddb3452c/fpu-0.2.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ff8b11bac4d8ca881a73c2609be3d6fe", "sha256": "50f2c520171ab016cee2985d06691863589eca88f12c04606c110ebd83411b5c" }, "downloads": -1, "filename": "fpu-0.2.5.tar.gz", "has_sig": false, "md5_digest": "ff8b11bac4d8ca881a73c2609be3d6fe", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12857, "upload_time": "2019-04-13T07:07:42", "url": "https://files.pythonhosted.org/packages/06/8d/9db38b13ae9744478e63dc3179e804eb66a746776c29e481f4f6078176cd/fpu-0.2.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "336402316fdaf17d075154944062acac", "sha256": "d7f9ecf8eae73f86360fd2d98d0d6543c1460e3de8432c1d6b5a669f081ba56b" }, "downloads": -1, "filename": "fpu-0.2.5-py3-none-any.whl", "has_sig": false, "md5_digest": "336402316fdaf17d075154944062acac", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11597, "upload_time": "2019-04-13T07:07:41", "url": "https://files.pythonhosted.org/packages/44/ad/a7ffd51f71476e46961b2b532a4e42cc08256a03595f9db4e64fddb3452c/fpu-0.2.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ff8b11bac4d8ca881a73c2609be3d6fe", "sha256": "50f2c520171ab016cee2985d06691863589eca88f12c04606c110ebd83411b5c" }, "downloads": -1, "filename": "fpu-0.2.5.tar.gz", "has_sig": false, "md5_digest": "ff8b11bac4d8ca881a73c2609be3d6fe", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12857, "upload_time": "2019-04-13T07:07:42", "url": "https://files.pythonhosted.org/packages/06/8d/9db38b13ae9744478e63dc3179e804eb66a746776c29e481f4f6078176cd/fpu-0.2.5.tar.gz" } ] }