Implementation of missing features to enjoy functional programming in Python
Project description
Fn.py: enjoy FP in Python
=========================
Despite the fact that Python is not pure-functional programming
language, it's multi-paradigm PL and it gives you enough freedom to take
credits from functional programming approach. There are theoretical and
practical advantages to the functional style:
- Formal provability
- Modularity
- Composability
- Ease of debugging and testing
``Fn.py`` library provides you with missing "batteries" to get maximum
from functional approach even in mostly-imperative program.
More about functional approach from my Pycon UA 2012 talks: `Functional
Programming with
Python <http://ua.pycon.org/static/talks/kachayev/#/>`_.
Scala-style lambdas definition
------------------------------
.. code-block:: python
from fn import _
from fn.iters import zipwith
from itertools import repeat
assert list(map(_ * 2, range(5))) == [0,2,4,6,8]
assert list(filter(_ < 10, [9,10,11])) == [9]
assert list(zipwith(_ + _)([0,1,2], repeat(10))) == [10,11,12]
More examples of using ``_`` you can find in `test
cases <https://github.com/kachayev/fn.py/blob/master/tests.py>`_
declaration (attributes resolving, method calling, slicing).
Streams and infinite sequences declaration
------------------------------------------
Lazy-evaluated scala-style streams. Basic idea: evaluate each new
element "on demand" and share calculated elements between all created
iterators. ``Stream`` object supports ``<<`` operator that means pushing
new elements when it's necessary.
Simplest cases:
.. code-block:: python
from fn import Stream
s = Stream() << [1,2,3,4,5]
assert list(s) == [1,2,3,4,5]
assert s[1] == 2
assert s[0:2] == [1,2]
s = Stream() << range(6) << [6,7]
assert list(s) == [0,1,2,3,4,5,6,7]
def gen():
yield 1
yield 2
yield 3
s = Stream() << gen << (4,5)
assert list(s) == [1,2,3,4,5]
Lazy-evaluated stream is useful for infinite sequences, i.e. fibonacci
sequence can be calculated as:
.. code-block:: python
from fn import Stream
from fn.iters import take, drop, map
from operator import add
f = Stream()
fib = f << [0, 1] << map(add, f, drop(1, f))
assert list(take(10, fib)) == [0,1,1,2,3,5,8,13,21,34]
assert fib[20] == 6765
assert fib[30:35] == [832040,1346269,2178309,3524578,5702887]
High-level operations with functions
------------------------------------
``fn.F`` is a useful function wrapper to provide easy-to-use partial
application and functions composition.
.. code-block:: python
from fn import F, _
from operator import add, mul
# F(f, *args) means partial application
# same as functools.partial but returns fn.F instance
assert F(add, 1)(10) == 11
# F << F means functions composition,
# so (F(f) << g)(x) == f(g(x))
f = F(add, 1) << F(mul, 100)
assert list(map(f, [0, 1, 2])) == [1, 101, 201]
assert list(map(F() << str << (_ ** 2) << (_ + 1), range(3))) == ["1", "4", "9"]
You can find more examples for compositions usage in ``fn._``
implementation `source
code <https://github.com/kachayev/fn.py/blob/master/fn/underscore.py>`_.
``fn.op.apply`` executes given function with given positional arguments
in list (or any other iterable). ``fn.op.flip`` returns you function
that will reverse arguments order before apply.
.. code-block:: python
from fn.op import apply, flip
from operator import add, sub
assert apply(add, [1, 2]) == 3
assert flip(sub)(20,10) == -10
assert list(map(apply, [add, mul], [(1,2), (10,20)])) == [3, 200]
Itertools recipes
-----------------
``fn.iters`` module consists from two parts. First one is "unification"
of lazy functionality for few functions to work the same way in Python
2+/3+:
- ``map`` (returns ``itertools.imap`` in Python 2+)
- ``filter`` (returns ``itertools.ifilter`` in Python 2+)
- ``reduce`` (returns ``functools.reduce`` in Python 3+)
- ``zip`` (returns ``itertools.izip`` in Python 2+)
- ``range`` (returns ``xrange`` in Python 2+)
- ``filterfalse`` (returns ``itertools.ifilterfalse`` in Python 2+)
- ``zip_longest`` (returns ``itertools.izip_longest`` in Python 2+)
Second part of module is high-level recipes to work with iterators. Most
of them taken from `Python
docs <http://docs.python.org/2.7/library/itertools.html#itertools.product>`_
and adopted to work both with Python 2+/3+. Such recipes as ``drop``,
``takelast``, ``droplast``, ``splitat``, ``splitby`` I have already
submitted as `docs patch <http://bugs.python.org/issue16774>`_ which is
review status just now.
- ``take``, ``drop``
- ``takelast``, ``droplast``
- ``head``, ``tail``
- ``consume``
- ``nth``
- ``padnone``, ``ncycles``
- ``repeatfunc``
- ``grouper``, ``powerset``, ``pairwise``
- ``roundrobin``
- ``partition``, ``splitat``, ``splitby``
- ``flatten``
- ``zipwith``
- ``iter_except``
More information about use cases you can find in docstrings for each
function in `source
code <https://github.com/kachayev/fn.py/blob/master/fn/iters.py>`_ and
in `test
cases <https://github.com/kachayev/fn.py/blob/master/tests.py>`_.
Functional style for error-handling
-----------------------------------
- Maybe
- Either
**TODO: Implementation, code samples**
Trampolines decorator
---------------------
Workaround for dealing with TCO without heavy stack utilization.
**TODO: Implementation, code samples and documented theory.**
Installation
------------
To install ``fn.py``, simply:
.. code-block:: console
$ pip install fn
Or, if you absolutely must:
.. code-block:: console
$ easy_install fn
You can also build library from source
.. code-block:: console
$ git clone https://github.com/kachayev/fn.py.git
$ cd fn.py
$ python setup.py install
Work in progress (!)
--------------------
"Roadmap":
- Error handling (``Maybe``, ``Either`` from Haskell, ``Option`` from
Scala etc)
- Trampolines decorator
- Add to ``fn.iters`` module ``foldl``, ``foldr``, ``findelem``,
``findindex``
- C-accelerator for most modules
Ideas to think about:
- "Pipeline" notation for composition (back-order):
``F() >> list >> partial(map, int)``
- Curried function builder to simplify
``lambda arg1: lambda arg2: ...``
- Scala-style for-yield loop to simplify long map/filter blocks
Contribute
----------
1. Check for open issues or open a fresh issue to start a discussion
around a feature idea or a bug.
2. Fork the repository on Github to start making your changes to the
master branch (or branch off of it).
3. Write a test which shows that the bug was fixed or that the feature
works as expected.
History
=======
18.01.2012
----------
- Added 22 itertools recipes to ``fn.iters``
17.01.2012
----------
- Unit tests coverage for ``fn.stream.Stream``
- ``_StreamIterator`` works fine both in Python 2/3
16.01.2012
----------
- Finished underscore module functionality
- Test cases for all implemented modules/functions
- Update in Readme file with several fixes
- Get rid of F.flip classmethod in pref. for simple building blocks
- Optimized version for fn.op.flip operator
14.01.2012
----------
- Simplest ``Stream`` implementation
- Code samples for streams, labdas (``_``) and functions compositions
- Plan, contribute section in readme file
13.01.2012
----------
- Full list of ideas on paper
- Repository is created
- Initial commit
=========================
Despite the fact that Python is not pure-functional programming
language, it's multi-paradigm PL and it gives you enough freedom to take
credits from functional programming approach. There are theoretical and
practical advantages to the functional style:
- Formal provability
- Modularity
- Composability
- Ease of debugging and testing
``Fn.py`` library provides you with missing "batteries" to get maximum
from functional approach even in mostly-imperative program.
More about functional approach from my Pycon UA 2012 talks: `Functional
Programming with
Python <http://ua.pycon.org/static/talks/kachayev/#/>`_.
Scala-style lambdas definition
------------------------------
.. code-block:: python
from fn import _
from fn.iters import zipwith
from itertools import repeat
assert list(map(_ * 2, range(5))) == [0,2,4,6,8]
assert list(filter(_ < 10, [9,10,11])) == [9]
assert list(zipwith(_ + _)([0,1,2], repeat(10))) == [10,11,12]
More examples of using ``_`` you can find in `test
cases <https://github.com/kachayev/fn.py/blob/master/tests.py>`_
declaration (attributes resolving, method calling, slicing).
Streams and infinite sequences declaration
------------------------------------------
Lazy-evaluated scala-style streams. Basic idea: evaluate each new
element "on demand" and share calculated elements between all created
iterators. ``Stream`` object supports ``<<`` operator that means pushing
new elements when it's necessary.
Simplest cases:
.. code-block:: python
from fn import Stream
s = Stream() << [1,2,3,4,5]
assert list(s) == [1,2,3,4,5]
assert s[1] == 2
assert s[0:2] == [1,2]
s = Stream() << range(6) << [6,7]
assert list(s) == [0,1,2,3,4,5,6,7]
def gen():
yield 1
yield 2
yield 3
s = Stream() << gen << (4,5)
assert list(s) == [1,2,3,4,5]
Lazy-evaluated stream is useful for infinite sequences, i.e. fibonacci
sequence can be calculated as:
.. code-block:: python
from fn import Stream
from fn.iters import take, drop, map
from operator import add
f = Stream()
fib = f << [0, 1] << map(add, f, drop(1, f))
assert list(take(10, fib)) == [0,1,1,2,3,5,8,13,21,34]
assert fib[20] == 6765
assert fib[30:35] == [832040,1346269,2178309,3524578,5702887]
High-level operations with functions
------------------------------------
``fn.F`` is a useful function wrapper to provide easy-to-use partial
application and functions composition.
.. code-block:: python
from fn import F, _
from operator import add, mul
# F(f, *args) means partial application
# same as functools.partial but returns fn.F instance
assert F(add, 1)(10) == 11
# F << F means functions composition,
# so (F(f) << g)(x) == f(g(x))
f = F(add, 1) << F(mul, 100)
assert list(map(f, [0, 1, 2])) == [1, 101, 201]
assert list(map(F() << str << (_ ** 2) << (_ + 1), range(3))) == ["1", "4", "9"]
You can find more examples for compositions usage in ``fn._``
implementation `source
code <https://github.com/kachayev/fn.py/blob/master/fn/underscore.py>`_.
``fn.op.apply`` executes given function with given positional arguments
in list (or any other iterable). ``fn.op.flip`` returns you function
that will reverse arguments order before apply.
.. code-block:: python
from fn.op import apply, flip
from operator import add, sub
assert apply(add, [1, 2]) == 3
assert flip(sub)(20,10) == -10
assert list(map(apply, [add, mul], [(1,2), (10,20)])) == [3, 200]
Itertools recipes
-----------------
``fn.iters`` module consists from two parts. First one is "unification"
of lazy functionality for few functions to work the same way in Python
2+/3+:
- ``map`` (returns ``itertools.imap`` in Python 2+)
- ``filter`` (returns ``itertools.ifilter`` in Python 2+)
- ``reduce`` (returns ``functools.reduce`` in Python 3+)
- ``zip`` (returns ``itertools.izip`` in Python 2+)
- ``range`` (returns ``xrange`` in Python 2+)
- ``filterfalse`` (returns ``itertools.ifilterfalse`` in Python 2+)
- ``zip_longest`` (returns ``itertools.izip_longest`` in Python 2+)
Second part of module is high-level recipes to work with iterators. Most
of them taken from `Python
docs <http://docs.python.org/2.7/library/itertools.html#itertools.product>`_
and adopted to work both with Python 2+/3+. Such recipes as ``drop``,
``takelast``, ``droplast``, ``splitat``, ``splitby`` I have already
submitted as `docs patch <http://bugs.python.org/issue16774>`_ which is
review status just now.
- ``take``, ``drop``
- ``takelast``, ``droplast``
- ``head``, ``tail``
- ``consume``
- ``nth``
- ``padnone``, ``ncycles``
- ``repeatfunc``
- ``grouper``, ``powerset``, ``pairwise``
- ``roundrobin``
- ``partition``, ``splitat``, ``splitby``
- ``flatten``
- ``zipwith``
- ``iter_except``
More information about use cases you can find in docstrings for each
function in `source
code <https://github.com/kachayev/fn.py/blob/master/fn/iters.py>`_ and
in `test
cases <https://github.com/kachayev/fn.py/blob/master/tests.py>`_.
Functional style for error-handling
-----------------------------------
- Maybe
- Either
**TODO: Implementation, code samples**
Trampolines decorator
---------------------
Workaround for dealing with TCO without heavy stack utilization.
**TODO: Implementation, code samples and documented theory.**
Installation
------------
To install ``fn.py``, simply:
.. code-block:: console
$ pip install fn
Or, if you absolutely must:
.. code-block:: console
$ easy_install fn
You can also build library from source
.. code-block:: console
$ git clone https://github.com/kachayev/fn.py.git
$ cd fn.py
$ python setup.py install
Work in progress (!)
--------------------
"Roadmap":
- Error handling (``Maybe``, ``Either`` from Haskell, ``Option`` from
Scala etc)
- Trampolines decorator
- Add to ``fn.iters`` module ``foldl``, ``foldr``, ``findelem``,
``findindex``
- C-accelerator for most modules
Ideas to think about:
- "Pipeline" notation for composition (back-order):
``F() >> list >> partial(map, int)``
- Curried function builder to simplify
``lambda arg1: lambda arg2: ...``
- Scala-style for-yield loop to simplify long map/filter blocks
Contribute
----------
1. Check for open issues or open a fresh issue to start a discussion
around a feature idea or a bug.
2. Fork the repository on Github to start making your changes to the
master branch (or branch off of it).
3. Write a test which shows that the bug was fixed or that the feature
works as expected.
History
=======
18.01.2012
----------
- Added 22 itertools recipes to ``fn.iters``
17.01.2012
----------
- Unit tests coverage for ``fn.stream.Stream``
- ``_StreamIterator`` works fine both in Python 2/3
16.01.2012
----------
- Finished underscore module functionality
- Test cases for all implemented modules/functions
- Update in Readme file with several fixes
- Get rid of F.flip classmethod in pref. for simple building blocks
- Optimized version for fn.op.flip operator
14.01.2012
----------
- Simplest ``Stream`` implementation
- Code samples for streams, labdas (``_``) and functions compositions
- Plan, contribute section in readme file
13.01.2012
----------
- Full list of ideas on paper
- Repository is created
- Initial commit
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
fn-0.1.13.tar.gz
(11.8 kB
view hashes)