PyMonad


NamePyMonad JSON
Version 2.4.0 PyPI version JSON
download
home_pagehttps://github.com/jasondelaat/pymonad
SummaryData structures and utilities for monadic style functional programming.
upload_time2021-05-14 16:44:14
maintainer
docs_urlNone
authorJason DeLaat
requires_python>=3.7
licenseBSD-3-Clause
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
.. contents::

PyMonad implements data structures typically available in pure
functional or functional first programming languages like Haskell and
F#. Included are Monad and Monoid data types with several common
monads included - such as Maybe and State - as well as some useful
tools such as the @curry decorator for defining curried
functions. PyMonad 2.0.x represents and almost complete re-write of
the library with a simpler, more consistent interface as well as type
annotations to help ensure correct usage.

1 Getting Started
-----------------

These instructions will get you a copy of the project up and running
on your local machine for development and testing purposes.

1.1 Prerequisites
~~~~~~~~~~~~~~~~~

PyMonad requires Python 3.7+. If installing via ``pip`` then you
will also need `Pip <https://pypi.org/project/pip/>`_ and `Wheel <https://pypi.org/project/wheel/>`_ installed. See those projects for
more information on installing them if necessary.

Potential contributors should additionally install `pylint <https://pypi.org/project/pylint/>`_ and
`pytype <https://pypi.org/project/pytype/>`_ to ensure their code adheres to common style conventions.

1.2 Installing
~~~~~~~~~~~~~~

1.2.1 From the Python Package Index (PyPI) with pip
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

From a command line run:

.. code:: bash

    pip install PyMonad

1.2.2 Manual Build from PyPI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Download the project files from
`https://pypi.org/project/PyMonad/#files <https://pypi.org/project/PyMonad/#files>`_ and from the project
directory run:

.. code:: bash

    python setup.py install

If that doesn't work you may need to run the following instead.

.. code:: bash

    python3 setup.py install

1.2.3 From github
^^^^^^^^^^^^^^^^^

Clone the project repository:

.. code:: bash

    git clone https://github.com/jasondelaat/pymonad.git

Then from the project directory run ``setup.py`` as for the manual
build instructions above.

1.2.4 Example Usage
^^^^^^^^^^^^^^^^^^^

The following example imports the ``tools`` module and uses the
``curry`` function to define a curried addition function.

.. code:: python

    import pymonad.tools

    @pymonad.tools.curry(2) # Pass the expected number of arguments to the curry function.
    def add(x, y):
        return x + y

    # We can call add with all of it's arguments...
    print(add(2, 3)) # Prints '5'

    # ...or only some of them.
    add2 = add(2)  # Creates a new function expecting a single arguments
    print(add2(3)) # Also prints '5'

1.2.5 Next Steps
^^^^^^^^^^^^^^^^

The PyMonad documentation is a work in progress. For tutorials,
how-to, and more head over to the `PyMonad Documentation Project <https://jasondelaat.github.io/pymonad_docs/>`_.
If you'd like to contribute visit the documentation repository
`here <https://github.com/jasondelaat/pymonad_docs>`_.

1.3 Upgrading from PyMonad 1.3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you've used the 1.x versions of PyMonad you'll notice that
there are a few differences:

1.3.1 Curried functions
^^^^^^^^^^^^^^^^^^^^^^^

Currying functions in PyMonad version 1.x wrapped a function in
an instance of the Reader monad. This is no longer the case and
currying simply produces a new function as one might expect. 

The signature of ``curry`` has changed slightly. The new ``curry``
takes two arguments: the number of arguments which need to be
curried and the function.

.. code:: python

    from pymonad.tools import curry

    def add(x, y):
        return x + y

    curried_add = curry(2, add)
    # add = curry(2, add) # If you don't need access to the uncurried version.

``curry`` is itself a curried function so it can be used more
concisely as a decorator.

.. code:: python

    from pymonad.tools import curry

    @curry(2)
    def add(x, y):
        return x + y

1.3.2 Operators
^^^^^^^^^^^^^^^

Version 2 of PyMonad discourages the use of operators (>>, \\\*, and
&) used in version 1 so old code which uses them will
break. Operators have been removed from the default monad
implementation but are still available for users that still wish
to use them in the ``operators`` package. To use operators:

.. code:: python

    # Instead of this:
    # import pymonad.maybe

    # Do this:
    import pymonad.operators.maybe

While it's unlikely operators will be removed entirely, it is
strongly suggested that users write code that doesn't require
them.

1.3.3 Renamed Methods
^^^^^^^^^^^^^^^^^^^^^

The ``fmap`` method has been renamed to simply ``map`` and ``unit`` is now called ``insert``.

.. code:: python

    from pymonad.maybe import Maybe

    def add2(x):
        return x + 2

    m = (Maybe.insert(1)
         .map(add2)
    )

    print(m) # Just 3

1.3.4 Applicative Syntax
^^^^^^^^^^^^^^^^^^^^^^^^

Previously applicative syntax used the ``&`` operator or the ``amap``
method. ``amap`` still exists but there's now another way to use
applicatives: ``apply().to_arguments()``

.. code:: python

    from pymonad.tools import curry
    from pymonad.maybe import Maybe, Just

    @curry(2)
    def add(x, y):
        return x + y

    a = Just(1)
    b = Just(2)

    c  = Maybe.apply(add).to_arguments(a, b)
    print(c) # Just 3

If the function passed to ``apply`` accepts multiple arguments then
it *must* be a curried function.

1.3.5 New ``then`` method
^^^^^^^^^^^^^^^^^^^^^^^^^

The ``then`` method combines the functionality of both ``map`` and
``bind``. It first tries to ``bind`` the function passed to it and,
if that doesn't work, tries ``map`` instead. It will be slightly
less efficient than using ``map`` and ``bind`` directly but frees
users from having to worry about specifically which functions are
being used where.

.. code:: python

    from pymonad.tools import curry
    from pymonad.maybe import Maybe, Just, Nothing

    @curry(2)
    def add(x, y):
        return x + y

    @curry(2)
    def div(y, x):
        if y == 0:
    	return Nothing
        else:
    	return Just(x / y)

    m = (Maybe.insert(2)
         .then(add(2)) # Uses map
         .then(div(4)) # Uses bind
    )

    print(m) # Just 1.0

1.3.6 Getting values out of ``Maybe`` and ``Either``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Previously, if you need to get a value out of a ``Maybe`` or an
``Either`` after a series of calculations you would have to access
the ``.value`` property directly. By the very nature of these two
monads, ``.value`` may not contain valid data and checking whether
the data is valid or not is the problem these monads are supposed
to solve. As of PyMonad 2.3.0 there are methods -- ``maybe`` and
``either`` -- for properly extracting values from these
monads.

Given a ``Maybe`` value ``m``, the ``maybe`` method takes a default
value, which will be returned if ``m`` is ``Nothing``, and a function
which will be applied to the value inside of a ``Just``.

.. code:: python

    from pymonad.maybe import Just, Nothing

    a = Just(2)
    b = Nothing

    print(a.maybe(0, lambda x: x)) # 2
    print(b.maybe(0, lambda x: x)) # 0

The ``either`` method works essentially the same way but takes two
functions as arguments. The first is applied if the value is a
``Left`` value and the second if it's a ``Right``.

.. code:: python

    from pymonad.either import Left, Right

    a = Right(2)
    b = Left('Invalid')

    print(a.either(lambda x: f'Sorry, {x}', lambda x: x)) # 2
    print(b.either(lambda x: f'Sorry, {x}', lambda x: x)) # Sorry, Invalid

1.4 Note on efficiency in versions <2.3.5
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In pymonad versions 2.3.4 and earlier, an error in the
implementation of ``then`` meant that some monad
types executed ``then`` with exponential complexity. As of version
2.3.5 this has been corrected. All monad types now execute ``then``
in linear time. A similar problem occured with the ``map`` and
``bind`` methods for the State monad which have also been fixed in
2.3.5

If you're using an earlier version of pymonad upgrading to 2.3.5
is highly recommended.

2 Running the tests
-------------------

2.1 Unit Tests
~~~~~~~~~~~~~~

These tests primarily ensure that the defined monads and monoids
obey the required mathematical laws.

On most \*nix systems you should be able to run the automated tests
by typing the following at the command line.

.. code:: bash

    ./run_tests.sh

However, ``run_tests.sh`` is just a convenience. If the above doesn't
work the following should:

.. code:: bash

    python3 -m unittest discover test/

2.2 Style Tests
~~~~~~~~~~~~~~~

Contributors only need to run ``pylint`` and ``pytype`` over their
code and ensure that there are no glaring style or type
errors. PyMonad (mostly) attempts to adhere to the `Google Python Style Guide <https://google.github.io/styleguide/pyguide.html>`_ 
and includes type hinting according to `PEP 484 <https://www.python.org/dev/peps/pep-0484/>`_.

In general, don't disable ``pylint`` or ``pytype`` errors for the
whole project, instead disable them via comments in the code. See
the existing code for examples of errors which can be disabled.

3 Authors
---------

**Jason DeLaat** - *Primary Author/Maintainer* - `https://github.com/jasondelaat/pymonad <https://github.com/jasondelaat/pymonad>`_

4 License
---------

This project is licensed under the 3-Clause BSD License. See
`LICENSE.rst <./LICENSE.rst>`_ for details.



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/jasondelaat/pymonad",
    "name": "PyMonad",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "",
    "author": "Jason DeLaat",
    "author_email": "jason.develops@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/3b/4e/68803065743007b7dc000c543f45f6e407f8a1cbc673fdab016df3ca4b44/PyMonad-2.4.0.tar.gz",
    "platform": "",
    "description": "\n.. contents::\n\nPyMonad implements data structures typically available in pure\nfunctional or functional first programming languages like Haskell and\nF#. Included are Monad and Monoid data types with several common\nmonads included - such as Maybe and State - as well as some useful\ntools such as the @curry decorator for defining curried\nfunctions. PyMonad 2.0.x represents and almost complete re-write of\nthe library with a simpler, more consistent interface as well as type\nannotations to help ensure correct usage.\n\n1 Getting Started\n-----------------\n\nThese instructions will get you a copy of the project up and running\non your local machine for development and testing purposes.\n\n1.1 Prerequisites\n~~~~~~~~~~~~~~~~~\n\nPyMonad requires Python 3.7+. If installing via ``pip`` then you\nwill also need `Pip <https://pypi.org/project/pip/>`_ and `Wheel <https://pypi.org/project/wheel/>`_ installed. See those projects for\nmore information on installing them if necessary.\n\nPotential contributors should additionally install `pylint <https://pypi.org/project/pylint/>`_ and\n`pytype <https://pypi.org/project/pytype/>`_ to ensure their code adheres to common style conventions.\n\n1.2 Installing\n~~~~~~~~~~~~~~\n\n1.2.1 From the Python Package Index (PyPI) with pip\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFrom a command line run:\n\n.. code:: bash\n\n    pip install PyMonad\n\n1.2.2 Manual Build from PyPI\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nDownload the project files from\n`https://pypi.org/project/PyMonad/#files <https://pypi.org/project/PyMonad/#files>`_ and from the project\ndirectory run:\n\n.. code:: bash\n\n    python setup.py install\n\nIf that doesn't work you may need to run the following instead.\n\n.. code:: bash\n\n    python3 setup.py install\n\n1.2.3 From github\n^^^^^^^^^^^^^^^^^\n\nClone the project repository:\n\n.. code:: bash\n\n    git clone https://github.com/jasondelaat/pymonad.git\n\nThen from the project directory run ``setup.py`` as for the manual\nbuild instructions above.\n\n1.2.4 Example Usage\n^^^^^^^^^^^^^^^^^^^\n\nThe following example imports the ``tools`` module and uses the\n``curry`` function to define a curried addition function.\n\n.. code:: python\n\n    import pymonad.tools\n\n    @pymonad.tools.curry(2) # Pass the expected number of arguments to the curry function.\n    def add(x, y):\n        return x + y\n\n    # We can call add with all of it's arguments...\n    print(add(2, 3)) # Prints '5'\n\n    # ...or only some of them.\n    add2 = add(2)  # Creates a new function expecting a single arguments\n    print(add2(3)) # Also prints '5'\n\n1.2.5 Next Steps\n^^^^^^^^^^^^^^^^\n\nThe PyMonad documentation is a work in progress. For tutorials,\nhow-to, and more head over to the `PyMonad Documentation Project <https://jasondelaat.github.io/pymonad_docs/>`_.\nIf you'd like to contribute visit the documentation repository\n`here <https://github.com/jasondelaat/pymonad_docs>`_.\n\n1.3 Upgrading from PyMonad 1.3\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you've used the 1.x versions of PyMonad you'll notice that\nthere are a few differences:\n\n1.3.1 Curried functions\n^^^^^^^^^^^^^^^^^^^^^^^\n\nCurrying functions in PyMonad version 1.x wrapped a function in\nan instance of the Reader monad. This is no longer the case and\ncurrying simply produces a new function as one might expect. \n\nThe signature of ``curry`` has changed slightly. The new ``curry``\ntakes two arguments: the number of arguments which need to be\ncurried and the function.\n\n.. code:: python\n\n    from pymonad.tools import curry\n\n    def add(x, y):\n        return x + y\n\n    curried_add = curry(2, add)\n    # add = curry(2, add) # If you don't need access to the uncurried version.\n\n``curry`` is itself a curried function so it can be used more\nconcisely as a decorator.\n\n.. code:: python\n\n    from pymonad.tools import curry\n\n    @curry(2)\n    def add(x, y):\n        return x + y\n\n1.3.2 Operators\n^^^^^^^^^^^^^^^\n\nVersion 2 of PyMonad discourages the use of operators (>>, \\\\\\*, and\n&) used in version 1 so old code which uses them will\nbreak. Operators have been removed from the default monad\nimplementation but are still available for users that still wish\nto use them in the ``operators`` package. To use operators:\n\n.. code:: python\n\n    # Instead of this:\n    # import pymonad.maybe\n\n    # Do this:\n    import pymonad.operators.maybe\n\nWhile it's unlikely operators will be removed entirely, it is\nstrongly suggested that users write code that doesn't require\nthem.\n\n1.3.3 Renamed Methods\n^^^^^^^^^^^^^^^^^^^^^\n\nThe ``fmap`` method has been renamed to simply ``map`` and ``unit`` is now called ``insert``.\n\n.. code:: python\n\n    from pymonad.maybe import Maybe\n\n    def add2(x):\n        return x + 2\n\n    m = (Maybe.insert(1)\n         .map(add2)\n    )\n\n    print(m) # Just 3\n\n1.3.4 Applicative Syntax\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nPreviously applicative syntax used the ``&`` operator or the ``amap``\nmethod. ``amap`` still exists but there's now another way to use\napplicatives: ``apply().to_arguments()``\n\n.. code:: python\n\n    from pymonad.tools import curry\n    from pymonad.maybe import Maybe, Just\n\n    @curry(2)\n    def add(x, y):\n        return x + y\n\n    a = Just(1)\n    b = Just(2)\n\n    c  = Maybe.apply(add).to_arguments(a, b)\n    print(c) # Just 3\n\nIf the function passed to ``apply`` accepts multiple arguments then\nit *must* be a curried function.\n\n1.3.5 New ``then`` method\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``then`` method combines the functionality of both ``map`` and\n``bind``. It first tries to ``bind`` the function passed to it and,\nif that doesn't work, tries ``map`` instead. It will be slightly\nless efficient than using ``map`` and ``bind`` directly but frees\nusers from having to worry about specifically which functions are\nbeing used where.\n\n.. code:: python\n\n    from pymonad.tools import curry\n    from pymonad.maybe import Maybe, Just, Nothing\n\n    @curry(2)\n    def add(x, y):\n        return x + y\n\n    @curry(2)\n    def div(y, x):\n        if y == 0:\n    \treturn Nothing\n        else:\n    \treturn Just(x / y)\n\n    m = (Maybe.insert(2)\n         .then(add(2)) # Uses map\n         .then(div(4)) # Uses bind\n    )\n\n    print(m) # Just 1.0\n\n1.3.6 Getting values out of ``Maybe`` and ``Either``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nPreviously, if you need to get a value out of a ``Maybe`` or an\n``Either`` after a series of calculations you would have to access\nthe ``.value`` property directly. By the very nature of these two\nmonads, ``.value`` may not contain valid data and checking whether\nthe data is valid or not is the problem these monads are supposed\nto solve. As of PyMonad 2.3.0 there are methods -- ``maybe`` and\n``either`` -- for properly extracting values from these\nmonads.\n\nGiven a ``Maybe`` value ``m``, the ``maybe`` method takes a default\nvalue, which will be returned if ``m`` is ``Nothing``, and a function\nwhich will be applied to the value inside of a ``Just``.\n\n.. code:: python\n\n    from pymonad.maybe import Just, Nothing\n\n    a = Just(2)\n    b = Nothing\n\n    print(a.maybe(0, lambda x: x)) # 2\n    print(b.maybe(0, lambda x: x)) # 0\n\nThe ``either`` method works essentially the same way but takes two\nfunctions as arguments. The first is applied if the value is a\n``Left`` value and the second if it's a ``Right``.\n\n.. code:: python\n\n    from pymonad.either import Left, Right\n\n    a = Right(2)\n    b = Left('Invalid')\n\n    print(a.either(lambda x: f'Sorry, {x}', lambda x: x)) # 2\n    print(b.either(lambda x: f'Sorry, {x}', lambda x: x)) # Sorry, Invalid\n\n1.4 Note on efficiency in versions <2.3.5\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn pymonad versions 2.3.4 and earlier, an error in the\nimplementation of ``then`` meant that some monad\ntypes executed ``then`` with exponential complexity. As of version\n2.3.5 this has been corrected. All monad types now execute ``then``\nin linear time. A similar problem occured with the ``map`` and\n``bind`` methods for the State monad which have also been fixed in\n2.3.5\n\nIf you're using an earlier version of pymonad upgrading to 2.3.5\nis highly recommended.\n\n2 Running the tests\n-------------------\n\n2.1 Unit Tests\n~~~~~~~~~~~~~~\n\nThese tests primarily ensure that the defined monads and monoids\nobey the required mathematical laws.\n\nOn most \\*nix systems you should be able to run the automated tests\nby typing the following at the command line.\n\n.. code:: bash\n\n    ./run_tests.sh\n\nHowever, ``run_tests.sh`` is just a convenience. If the above doesn't\nwork the following should:\n\n.. code:: bash\n\n    python3 -m unittest discover test/\n\n2.2 Style Tests\n~~~~~~~~~~~~~~~\n\nContributors only need to run ``pylint`` and ``pytype`` over their\ncode and ensure that there are no glaring style or type\nerrors. PyMonad (mostly) attempts to adhere to the `Google Python Style Guide <https://google.github.io/styleguide/pyguide.html>`_ \nand includes type hinting according to `PEP 484 <https://www.python.org/dev/peps/pep-0484/>`_.\n\nIn general, don't disable ``pylint`` or ``pytype`` errors for the\nwhole project, instead disable them via comments in the code. See\nthe existing code for examples of errors which can be disabled.\n\n3 Authors\n---------\n\n**Jason DeLaat** - *Primary Author/Maintainer* - `https://github.com/jasondelaat/pymonad <https://github.com/jasondelaat/pymonad>`_\n\n4 License\n---------\n\nThis project is licensed under the 3-Clause BSD License. See\n`LICENSE.rst <./LICENSE.rst>`_ for details.\n\n\n",
    "bugtrack_url": null,
    "license": "BSD-3-Clause",
    "summary": "Data structures and utilities for monadic style functional programming.",
    "version": "2.4.0",
    "project_urls": {
        "Homepage": "https://github.com/jasondelaat/pymonad"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8d42461af4ed7ea252b46c53b59f965d5f1eda971eef4d0c12f8320286578bf3",
                "md5": "61a6aa18cc022456f9d083b00838e4c8",
                "sha256": "e78d9bf3b0712b165d4f43c29b0c1b23ed3ff11dbae1a6d61402cda7f0827d1a"
            },
            "downloads": -1,
            "filename": "PyMonad-2.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "61a6aa18cc022456f9d083b00838e4c8",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 29141,
            "upload_time": "2021-05-14T16:44:13",
            "upload_time_iso_8601": "2021-05-14T16:44:13.269639Z",
            "url": "https://files.pythonhosted.org/packages/8d/42/461af4ed7ea252b46c53b59f965d5f1eda971eef4d0c12f8320286578bf3/PyMonad-2.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3b4e68803065743007b7dc000c543f45f6e407f8a1cbc673fdab016df3ca4b44",
                "md5": "7da98cd9d410bc10c52f113ea090d01a",
                "sha256": "6b9d98e40bf2e0f1cc1900309365f9ba4997f3432a1d6e71f80f41ace2890204"
            },
            "downloads": -1,
            "filename": "PyMonad-2.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "7da98cd9d410bc10c52f113ea090d01a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 22870,
            "upload_time": "2021-05-14T16:44:14",
            "upload_time_iso_8601": "2021-05-14T16:44:14.838642Z",
            "url": "https://files.pythonhosted.org/packages/3b/4e/68803065743007b7dc000c543f45f6e407f8a1cbc673fdab016df3ca4b44/PyMonad-2.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-05-14 16:44:14",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jasondelaat",
    "github_project": "pymonad",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "pymonad"
}
        
Elapsed time: 0.50005s