Giter VIP home page Giter VIP logo

oslash's Introduction

Functors, Applicatives, And Monads in Python

Python package

OSlash (Ø) is a library for playing with functional programming in Python 3.8+. It's an attempt to re-implement some of the code from Learn You a Haskell for Great Good! in Python 3.8. OSlash unifies functional and object oriented paradigms by grouping related functions within classes. Objects are however never used for storing values or mutable data, and data only lives within function closures.

OSlash is intended to be a tutorial. For practical functional programming in Python in production environments you should use Expression instead.

Install

> pip3 install oslash

The project currently contains implementations for:

Abstract Base Classes

  • Functor, for stuff that can be mapped
  • Applicative, for callable stuff
  • Monoid, for associative stuff
  • Monad, for monadic stuff

And Some Monads

  • Identity, boxed stuff in its simplest form
  • Maybe (Just | Nothing), for optional stuff
  • Either (Right | Left), for possible failures
  • List, purely functional list of stuff
  • IO Action, for impure stuff
  • Writer, for logging stuff
  • Reader, for callable stuff
  • State, for stateful computations of stuff
  • Cont, for continuation of stuff

Monadic functions

  • >>, for sequencing monadic actions
  • lift, for mapping a function over monadic values
  • join, for removing one level of monadic structure
  • compose, for composing monadic functions

Utility functions

  • compose, for composing 0 to n functions

But why?

Yes, I know there are other projects out there like PyMonad, fn.py. I'm simply doing this in order to better understand the book. It's so much easier to learn when you implement things yourself. The code may be similar to PyMonad in structure, but is quite different in implementation.

Why is the project called OSlash? OSlash is the Norwegian character called Oslash. Initially I wanted to create a project that used Ø and ø (unicode) for the project name and modules. It didn't work out well, so I renamed it to OSlash.

Examples

Haskell:

> fmap (+3) (Just 2)
Just 5

> (+3) <$> (Just 2)
Just 5

Python:

>>> Just(2).map(lambda x: x+3)
Just 5

>>> (lambda x: x+3) % Just(2)
Just 5

IO Actions:

from oslash import put_line, get_line

main = put_line("What is your name?") | (lambda _:
    get_line() | (lambda name:
    put_line("What is your age?") | (lambda _:
    get_line() | (lambda age:
    put_line("Hello " + name + "!") | (lambda _:
    put_line("You are " + age + " years old"))))))

if __name__ == "__main__":
    main()

Tutorials

oslash's People

Contributors

cbenz avatar dbrattli avatar duythinht avatar sobolevn avatar tanayseven avatar technologicat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oslash's Issues

publish py3 wheels

can you do:

git checkout 89075d281b9cd132c85d9de4596b85ee97b29b43
git tag 0.5.0
git push --tags
python3 setup.py bdist_wheel upload

Mutability Issue

I was experimenting a bit with how OSlash operates with Python's inherent mutability.

While you can't assign within a map, you can still mutate variables outside of the context, for instance a dict:


In [77]: y = Just(1)

In [78]: y.map(lambda x: d.pop('hoge'))
Out[78]: Just 10

In [79]: d
Out[79]: {}

This is troublesome in a context in which you would want to map with a function that relies on the mutability of an object - for instance splitting one dict in to two via popping the appropriate variables in to a second dict, while maintaining the structure of the initial dict.

Obviously this can be avoided by copying the dict, either outside the context or within the function. I would however expect that applying something in a functional context would absolutely not mutate anything. I don't know if this is avoidable in a way that isn't wasteful (simply implementing a copy within the library wouldn't be ideal) but it caught my interest.

How to convert a plain function into a function that returns a monad with inflix operators

An example is worth 1000 words:

import oslash

def operation_that_succeeds(input):
    return oslash.Right(input + 1)

def operation_that_fails(input):
    return oslash.Left("Failure")

def lift(m):
    def w(f):
        return lambda x: m.unit(f(x))
    return w

@lift(oslash.Right)
def pure_function(input):
    return input + 1

if __name__ == "__main__":
    print(operation_that_succeeds(45) | operation_that_succeeds | pure_function | operation_that_succeeds)

How can I rewrite this without needing to implement the lift decorator?

TypeAlias with Generic ABCs

type checking the follow program with mypy:

from oslash import List

IntList = List[int]

def f() -> IntList:
     return List.unit(0)

Yields error: Invalid type "StrList".

Is OSlash usable in real projects?

OSlash is primarily meant to be a Monad tutorial for Python. If you want to use types like Maybe and Either in your Python code, you should also take a look at FSlash which uses the same concepts named as Option and Result, and aim to be a solid library for doing practical functional programming in Python.

Note that FSlash has now been renamed to Expression.

Python 3.7 support

Hello @dbrattli, I tried to install oslash on python 3.7 and has some problems in runtime. (see full stacktrace).

I would like to fix that if you want.

These error is related List.__str__.

In [2]: oslash.List([1,2,3])                                                                   
Out[2]: ---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
~/.local/share/virtualenvs/tests-mSusHyBX/lib/python3.7/site-packages/oslash/list.py in __iter__(self)
     98             if xs.null():
---> 99                 raise StopIteration
    100 

StopIteration: 

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
~/.pyenv/versions/3.7.3/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

~/.pyenv/versions/3.7.3/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
    400                         if cls is not object \
    401                                 and callable(cls.__dict__.get('__repr__')):
--> 402                             return _repr_pprint(obj, self, cycle)
    403 
    404             return _default_pprint(obj, self, cycle)

~/.pyenv/versions/3.7.3/lib/python3.7/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
    695     """A pprint that just redirects to the normal repr function."""
    696     # Find newlines and replace them with p.break_()
--> 697     output = repr(obj)
    698     for idx,output_line in enumerate(output.splitlines()):
    699         if idx:

~/.local/share/virtualenvs/tests-mSusHyBX/lib/python3.7/site-packages/oslash/list.py in __repr__(self)
    115         """Return string representation of List.
    116         """
--> 117         return str(self)
    118 
    119     def __eq__(self, other: 'List') -> bool:

~/.local/share/virtualenvs/tests-mSusHyBX/lib/python3.7/site-packages/oslash/list.py in __str__(self)
    110         """Return string representation of List.
    111         """
--> 112         return "[%s]" % ", ".join([str(x) for x in self])
    113 
    114     def __repr__(self) -> str:

~/.local/share/virtualenvs/tests-mSusHyBX/lib/python3.7/site-packages/oslash/list.py in <listcomp>(.0)
    110         """Return string representation of List.
    111         """
--> 112         return "[%s]" % ", ".join([str(x) for x in self])
    113 
    114     def __repr__(self) -> str:

RuntimeError: generator raised StopIteration

Related #4

TypeError: Cannot create a consistent method resolution order (MRO) for bases Generic, Monad, Applicative, Functor

When trying to run hello.py under folder examples, something goes wrong with following error message:

» python3 ./hello.py                                                                                                                      feng@DDM05959
Traceback (most recent call last):
  File "./hello.py", line 1, in <module>
    from oslash import put_line, get_line
  File "/usr/lib/python3.7/site-packages/oslash/__init__.py", line 10, in <module>
    from .identity import Identity
  File "/usr/lib/python3.7/site-packages/oslash/identity.py", line 13, in <module>
    class Identity(Generic[A], Monad, Applicative, Functor):
  File "/usr/lib/python3.7/abc.py", line 126, in __new__
    cls = super().__new__(mcls, name, bases, namespace, **kwargs)
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Generic, Monad, Applicative, Functor

Python version

 » python3                                                                                                                                 feng@DDM05959
Python 3.7.0 (default, Jul 15 2018, 10:44:58)
[GCC 8.1.1 20180531] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.version
'3.7.0 (default, Jul 15 2018, 10:44:58) \n[GCC 8.1.1 20180531]'
>>>

(lambda x: x+3) % Just(2)

(lambda x: x+3) % Just(2)

Traceback (most recent call last):
File "", line 1, in
TypeError: unsupported operand type(s) for %: 'function' and 'Just'

Thanks

List Monad/Functor takes no arguments.

Hello, I am going through a book by Martin McBride about functional programming in Python.
I know this is not production-class library, but I am trying to instantiate a List functor (from oslash.list) as below and I end up raising an Exception...

from oslash import List as ListFunctor
a = ListFunctor([1,2,3])

Ends up with:

---------------------------------------------------------------------------  
TypeError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 ListFunctor([1,2,3])

TypeError: List() takes no arguments

I looked into the code of the library and it seems that we're supposed to use the class method/factory from_iterable right now? Is this true?
While I understand this is not a repository about this book, but it also mentioned possibility of mapping functions using % operator on Functors, which seemed to be working for Just and Nothing Functors, but does not work for this List Functor. I have to use List.from_iterable([1,2,3]).map(operator.neg), the "old way" (if ever supported) neg % List.from_iterable(1,2,3) ends up raising an Exception

from oslash import List as ListFunctor


a = ListFunctor.from_iterable([1,2,3])

neg % a
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 neg % a

TypeError: unsupported operand type(s) for %: 'builtin_function_or_method' and 'Cons'

Operand Type | error

q = Just(5) | (lambda x: Just(x + 1) | (lambda x: Just(x * 2) | (lambda x: Just(x - 4))))
TypeError: unsupported operand type(s) for |: 'Just' and 'function'

I have python,
Python 3.10.7 (v3.10.7:6cc6b13308, Sep 5 2022, 14:02:52) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

3.12.2 python problem with Functor subclass check

Hi, the next error appears on python3.12.2:

 File "/venv/lib/python3.12/site-packages/oslash/_init_.py", line 11, in <module>
    from .state import State
  File "/venv/lib/python3.12/site-packages/oslash/state.py", line 78, in <module>
    assert issubclass(State, Functor)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/typing.py", line 1841, in _subclasscheck_
    cls._non_callable_proto_members_
AttributeError: type object 'Functor' has no attribute '_non_callable_proto_members_'

with 3.11 everything is fine

oslash-0.6.3 (latest)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.