Metadata-Version: 2.1
Name: fpu
Version: 0.2.51
Summary: Functional Programming Utility
Home-page: https://github.com/johnklee/fpu
Author: johnklee
Author-email: puremonkey2007@gmail.com
License: MIT
Description: [![Build Status](https://travis-ci.com/johnklee/fpu.svg?branch=master)](https://travis-ci.com/johnklee/fpu)<br/>
        Overview and Table of Contents
        ==========================
        This 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)
        
        * [**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’t contain side effects.](#fpi)
            * [Feature of FP](#fpi_s1)
            * [Pro & Con of FP](#fpi_s2)
            * [Python's Functional Features](#fpi_s3)
        * [**Example API Usage**: To have a taste on how this utility to help you to program in FP](#examples)
            * [Gemstones](#exp_gemstones)
        * [**More about FP**: Appendix of other resources](#supplement)
        
        # FP Introduction <a name='fpi'></a>
        ([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’t 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.
        * Lisp 1958
        * Renaissance: F#, Haskell, Erlang ...
        * Used in industry such as Trading, Algorithmic, and Telecommunication etc
        
        ## Features of FP <a name='fpi_s1'></a>
        * First-class function/Higher order function
        * Pure functions without side effects
        * Immutable data structures
        * Preserve state in functions (Closure, Cury)
        * Recursion instead of loops/iteration
        
        ### First-class function/Higher order function
        With this feature, you can:
        * Use function as argument(s)
        * Function can return function
        * Enables functional composition
        
        Let'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:
        ```python
        dataSet = [{'values':[1, 2, 3]}, {'values':[4, 5]}]
        
        # Imperative
        def min_max_imp(dataSet):
            max_list = []
            for d in dataSet:
                max_list.append(max(d['values']))
                
            return min(max_list)
        
        min_max_imp(dataSet) # 3
        ```
        Above function `min_max_imp` actually comprises two sub steps:
        * Get the maximum of each values
        * Get the minimum of list collected by previous step
        
        This implies you can compose above two steps (function) into one by leveraging exist functions:
        ```python
        # FP
        from fpu.fp import *
        from functools import reduce, partial
        
        # compose2(f, g) = f(g())
        min_max = compose2(
                            partial(reduce, min), \
                            partial(map, lambda d: max(d['values']))
                          )
        min_max(dataSet)
        ```
        With composing feature, you can write less dump code and make use of exist function to generate new function!
        
        ### Pure functions without side effects
        The [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:
        * [Get started with Functional Programming | otsconf 2015](https://www.youtube.com/watch?v=6f5dt923FmQ&t=96s)
        * [Functional Programming with Python](https://www.youtube.com/watch?v=Ta1bAMOMFOI)
        
        ### Immutable data structures
        There 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`:
        ```python
        In [2]: v1 = v(1, 2, 3)                                                         
        
        In [3]: v2 = v1.append(4) # Any operation on v1 will return new vectory to reflect the modification
        
        In [4]: v1                                                                      
        Out[4]: pvector([1, 2, 3])  # v1 stay immutable
        
        In [5]: v2                                 
        Out[5]: pvector([1, 2, 3, 4])  # v2 reflect the change for appending 4
        
        In [6]: v3 = v2.set(1, 5)          
        
        In [7]: v2                                                                      
        Out[7]: pvector([1, 2, 3, 4])
        
        In [8]: v3
        Out[8]: pvector([1, 5, 3, 4])
        ```
        Above 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).
        
        ### Preserve state in functions (Closure, Cury)
        A [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:
        * We have to create a nested function (a function inside another function).
        * This nested function has to refer to a variable defined inside the enclosing function.
        * The enclosing function has to return the nested function
        
        Below is a simple example to create closure:
        ```python
        In [10]: def addN(n): 
            ...:     def _add(v): 
            ...:         return v + n 
            ...:     return _add 
            ...:                                                                                                                                                                                                           
        
        In [11]: addOne = addN(1)                                                                                                                                                                                          
        
        In [12]: addOne(2)                                                                                                                                                                                                 
        Out[12]: 3
        
        In [13]: addOne(3)                                                                                                                                                                                                 
        Out[13]: 4
        
        In [14]: addTwo = addN(2)                                                                                                                                                                                          
        
        In [15]: addTwo(2)                                                                                                                                                                                                 
        Out[15]: 4
        
        In [16]: addTwo(3)                                                                                                                                                                                                 
        Out[16]: 5
        ```
        [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:
        * Concept by Haskell Curry
        * 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)`
        * Improves reusability and composition
        * In some languages (Haskell, F#) functions are curried by default
        
        Unfortunately, Python doesn't support curring in default. Below is a workaround for you to do curring in Python3:
        ```python
        from inspect import signature
        
        def curry(x, argc=None):
            if argc is None:
                argc = len(signature(x).parameters)
                
            def p(*a):
                if len(a) == argc:
                    return x(*a)
                def q(*b):
                    return x(*(a + b))
                return curry(q, argc - len(a))
            return p
        
        @curry
        def myfun(a,b,c):
            print("{}-{}-{}".format(a,b,c))
            
        myfun(1, 2, 3)
        myfun(1, 2)(3)
        myfun(1)(2)(3) 
        ```
        
        ### Recursion instead of loops/iteration
        FP 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:
        ```python
        In [17]: import sys                                                                                                                                                                                                
        
        In [18]: sys.getrecursionlimit()                                                                                                                                                                                   
        Out[18]: 3000
        ```
        This package use class **TailCall** to store the function call in heap instead of stack. Below is one usage example:
        ```python
        In [1]: def fibRec(n, x=0, y=1): 
           ...:     if n == 0: 
           ...:         return x 
           ...:     else: 
           ...:         return fibRec(n-1, y, x + y) 
           ...:                                                                         
        
        In [2]: fibRec(3)                                                               
        Out[2]: 2
        
        In [3]: fibRec(3000)                                                            
        ---------------------------------------------------------------------------
        RecursionError                            Traceback (most recent call last)
        <ipython-input-3-035cf1755b78> in <module>
        ----> 1 fibRec(3000)
        
        <ipython-input-1-f509e891ef84> in fibRec(n, x, y)
              3         return x
              4     else:
        ----> 5         return fibRec(n-1, y, x + y)
              6 
        
        ... last 1 frames repeated, from the frame below ...
        
        <ipython-input-1-f509e891ef84> in fibRec(n, x, y)
              3         return x
              4     else:
        ----> 5         return fibRec(n-1, y, x + y)
              6 
        
        RecursionError: maximum recursion depth exceeded in comparison
        ```
        The exception is raised owing to recursion limitation. We can get by this limition by adopting **TailCall**:
        ```python
        In [5]: from fpu.fp import *                                                                                        
        
        In [6]: ret = TailCall.ret; sus = TailCall.sus
        In [22]: def fib(n, x=0, y=1):     
            ...:     return ret(x) if n == 0 else sus(Supplier(fib, n-1, y, x + y)) 
            ...:                                                                                                                                                                                                           
        
        In [23]: fib(3)                                                                                                                                                                                                    
        Out[23]: <fpu.fp.Suspend at 0x7f2be96be710>
        
        In [24]: fib(3).eval()                                                                                                                                                                                             
        Out[24]: 2
        
        In [25]: fib(3000).eval()                                                                                                                                                                                          
        Out[25]: 410615886307971260333568378719267105220125108637369252408885430926905584274113403731330491660850044560830036835706942274588569362145476502674373045446852160486606292497360503469773453733196887405847255290082049086907512622059054542195889758031109222670849274793859539133318371244795543147611073276240066737934085191731810993201706776838934766764778739502174470268627820918553842225858306408301661862900358266857238210235802504351951472997919676524004784236376453347268364152648346245840573214241419937917242918602639810097866942392015404620153818671425739835074851396421139982713640679581178458198658692285968043243656709796000
        ```
        
        ## Pro & Con of FP <a name='fpi_s2'></a>
        Here we will be going to review what advantage/disadvantage FP will bring to you.
        
        ### Advantages of FP
        * Absence of side effects can make your programs more robust
        * Programs tend to be more modular come and typically in smaller building blocks
        * Better testable - call with same parameters always returns same result
        * Focus on algorithms
        * Conceptional fit with parallel / concurrent programming
        * Live upates - Install new release while running
        
        ### Disadvantages of FP
        * Solutions to the same problem can look very different than procedural/OO ones
        * Finding good developers can be hard
        * Not equally useful for all types of problems
        * Input/Output are side effects and need special treatment
        * Recursion is "an order of magnitude more complex" than loops/iterations
        * Immutable data structures may increase run times
        
        ## Python's Functional Features - Overview <a name='fpi_s3'></a>
        * Pure functions (sort of)
        * Closures - hold state in functions
        * Functions as objects and decorators
        * Immutable data types (tuple, freezeSet)
        * Lazy evaluation - generators
        * List (dictionary, set) comprehensions
        * [functools](https://docs.python.org/3.5/library/functools.html), itertools, lambda, map, filter
        * Recursion - try to avoid, recursion limit has a reason
        
        # Example API Usage <a name='examples'></a>
        Here we are going to look at few examples from [HackerRank](https://www.hackerrank.com/) to know how FP can help you write code gracefully.
        
        ## Gemstones <a name='exp_gemstones'>
        The [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:
        ```python
        arr = ['abcdde', 'baccd', 'eeabg']
        # Complete the gemstones function below.
        def gemstones_imp(arr):
            set_list = []
            for s in arr:
                set_list.append(set(list(s)))
        
            # Imperative code
            uset = None
            for aset in set_list:
                if uset is None:
                    uset = aset
                else:
                    uset = uset & aset
        
            return len(uset)
        
        print("Output of gemstones_imp={}".format(gemstones_imp(arr)))
        ``` 
        The FP (declarative approach) code will be neat and graceful:
        ```python
        from fpu.flist import *
        
        def gemstones_dec(arr):
            rlist = fl(arr)
            return len(
                        rlist.map(lambda r: set(list(r))) \
                             .reduce(lambda a, b: a & b)
                      )
        
        print("Output of gemstones_imp={}".format(gemstones_dec(arr)))
        ```
        
        # Supplement <a name='supplement'></a>
        * [FP In Python - Ch1. (Avoiding) Flow Control](http://puremonkey2010.blogspot.com/2018/05/fp-in-python-ch1-avoiding-flow-control.html)
        * [FP In Python - Ch2. Callables](http://puremonkey2010.blogspot.com/2018/05/fp-in-python-ch2-callables.html)
        * [FP In Python - Ch3. Lazy Evaluation](http://puremonkey2010.blogspot.com/2018/05/fp-in-python-ch3-lazy-evaluation.html)
        
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: Development Status :: 3 - Alpha
Classifier: Natural Language :: English
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Description-Content-Type: text/markdown
