Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
43fee2a
reshape refactor
Dec 15, 2020
5436b79
no need of ssh
Dec 15, 2020
1a57dde
fix deps
Dec 15, 2020
e58d3a9
deps
Dec 15, 2020
d1cb471
project mgmnt
Dec 15, 2020
38ebeef
fixed type and formats
Dec 15, 2020
7e702c0
Bright future
Dec 15, 2020
ebf480b
black fmt
Dec 15, 2020
07cd9a1
maybe it is ok
Dec 15, 2020
de5da7d
End of refactoring
Dec 16, 2020
1599885
adde utility methods
Dec 16, 2020
e35c991
liest tests
Dec 16, 2020
43d46bc
docs
Dec 16, 2020
c61baf5
set with test
Dec 16, 2020
96efb0c
fixed set
Dec 16, 2020
180bb16
make a release
Dec 16, 2020
6d7660c
rel test
Dec 16, 2020
e6dacc5
scritp ok
Dec 16, 2020
8b01292
refs heads
Dec 16, 2020
43cf78a
set version
Dec 16, 2020
80efc66
black
Dec 16, 2020
04d8877
pipe script
Dec 16, 2020
0cafd59
push back
Dec 17, 2020
d88284d
Automated publish: Thu Dec 17 08:00:47 UTC 2020 0cafd59066a079e3c3a64…
Dec 17, 2020
8ce1658
fix version
Dec 17, 2020
d202550
Merge branch 'master' into implement_set
Dec 17, 2020
72fb284
black setup.py
Dec 17, 2020
ee5b630
black monads/__init__.py
Dec 17, 2020
9d86f80
formatting
Dec 18, 2020
41aaf8e
flatten support
Dec 18, 2020
2238c73
setup python
Dec 18, 2020
bffef50
Automated publish: Fri Dec 18 10:49:44 UTC 2020 2238c7381c18b916340cc…
Dec 18, 2020
01529c8
reformat
Dec 18, 2020
8d7e9d4
Merge branch 'master' into implement_set
Dec 18, 2020
8f94d33
Added Sized type
Dec 18, 2020
b506b9a
better coverage and oop
Dec 18, 2020
625b4cc
Added tests
Dec 18, 2020
0c5708b
Merge pull request #1 from sammyrulez/implement_set
sammyrulez Dec 18, 2020
7ffef8d
Merge pull request #2 from sammyrulez/comprensions
sammyrulez Dec 18, 2020
801df9d
Automated publish: Fri Dec 18 11:43:28 UTC 2020 0c5708b5a8bef1bc62ac2…
Dec 18, 2020
6fab780
added note
Dec 18, 2020
dfd42e1
doc for curry
Dec 21, 2020
3da210c
Merge branch 'implement_set'
Dec 21, 2020
054ee4d
Automated publish: Mon Dec 21 08:20:08 UTC 2020 3da210c062e75af4ab749…
Dec 21, 2020
46f7dc7
more docs
Dec 25, 2020
f1a2633
Set docs
Dec 25, 2020
abbe2d1
Automated publish: Fri Dec 25 10:15:09 UTC 2020 46f7dc72498b98f0a6730…
Dec 25, 2020
bf4f76b
hotfix
Dec 25, 2020
5ecb5dd
Automated publish: Fri Dec 25 16:09:51 UTC 2020 bf4f76bfb04f4cd530af4…
Dec 25, 2020
6dd1d36
add some immutability safety
Dec 31, 2020
9869a5c
improved coverage
Dec 31, 2020
52819c3
Docs and tests
Dec 31, 2020
299407e
Merge pull request #7 from sammyrulez/Imutable
sammyrulez Dec 31, 2020
bb9d0ff
Automated publish: Thu Dec 31 15:35:22 UTC 2020 299407e375d19c8c17a6c…
Dec 31, 2020
d046141
fixed derived docs
Jan 2, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
on:
push:
branches:
- master
pull_request:
branches:
- master

name: Build

jobs:
checking:
name: Lint & Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: Install Dev dependencies
run: |
echo ${{github.ref}}
pip install pytest black mypy pytest-asyncio pytest-cov pytest-black pytest-mypy
pip install -e .

- name: Run Tests
run: python setup.py test
- uses: codecov/codecov-action@v1
with:
file: ./reports/coverage.xml
flags: unittests
fail_ci_if_error: true
verbose: true

releasing:
name: Build Release
runs-on: ubuntu-latest
needs: checking
if: github.ref == 'refs/heads/master'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: Install Dev dependencies
run: |
pip install black
- name: Release
id: release
uses: rymndhng/release-on-push-action@v0.14.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
bump_version_scheme: patch

- name: Check Output Parameters
run: echo "Got version ${{ steps.release.outputs.tag_name }}"
- name: Print Version
run: |
sed -i '$ d' monads/__init__.py
echo "version='${{ steps.release.outputs.tag_name }}'" >> monads/__init__.py
sed -i '9s/.*/ version="${{ steps.release.outputs.tag_name }}",/' setup.py
black setup.py
black monads/__init__.py
- uses: mikeal/publish-to-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}




22 changes: 22 additions & 0 deletions .github/workflows/todo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
on:
push:
branches:
- master

name: "TODO to Issue Check"

jobs:
build:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@master"
- name: "TODO to Issue"
uses: "alstr/todo-to-issue-action@v1.2-beta"
with:
REPO: ${{ github.repository }}
BEFORE: ${{ github.event.before }}
SHA: ${{ github.sha }}
TOKEN: ${{ secrets.GITHUB_TOKEN }}
LABEL: "# TODO"
COMMENT_MARKER: "#"
id: "todo"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ venv.bak/
.mypy_cache/
.dmypy.json
dmypy.json
.vscode
21 changes: 0 additions & 21 deletions .travis.yml

This file was deleted.

64 changes: 62 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Type-safe Monads

[![Build Status](https://travis-ci.com/correl/typesafe-monads.svg?branch=master)](https://travis-ci.com/correl/typesafe-monads)
![Build](https://github.com/correl/typesafe-monads/workflows/Build/badge.svg)
[![codecov](https://codecov.io/gh/correl/typesafe-monads/branch/master/graph/badge.svg)](https://codecov.io/gh/correl/typesafe-monads)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)

Expand All @@ -18,6 +18,31 @@ lack of type constraints preventing incorrect usage. I could've
attempted to add type annotations to one of those libraries, but
building my own is more fun.

This is a fork of the original work by [Correl Roush](http://correl.phoenixinquis.net/)

I added some utility methods to make it easier to use in my day to day code and better interate with the pythonic style ( ie _List Comprehension_ )

## Installation

There is no *pipy* release ( yet)

```bash
$ pip install typesafe-monads
```

## Curring

Mixing Higher order functions ( functions that return a function ) with moand is a very common programming style other functional programming languages.
With _curry_ decorator you can transform a function in a _curried_ function: just apss some positional parameters and get back a function with the remaining ones.

```python
@curry
def power(exp: int, base: int ) -> int:
return math.pow(base, exp)

square_fn = power(2) # a function that returns the square of the parameter

```

## Base Classes

Expand Down Expand Up @@ -122,6 +147,9 @@ value if the list is empty.

## Monads

Wrapped values should be immutable: they are _protected_ from accidental direct writing with *Final* type and the pythonic naming convention.


### Maybe[T]

Represents optional data. A `Maybe` instance of a certain type `T` will
Expand All @@ -146,7 +174,13 @@ failure type `E`.

### List[T]

Represents a sequence of items.
Represents a ordered sequence of items.

- Also implements `Monoid`.

### Set[T]

Represents a unordered sequence of unique items.

- Also implements `Monoid`.

Expand All @@ -159,3 +193,29 @@ Represents an asynchronous action.
### Reader[T]

Represents the application of a function to it's argument.

## Monads as iterable

It is handy to iterate over some monad contents. *List* is obliviously the first candidate:
```python

m_list: List[int] = List([1, 2, 4, 9])
for i in m_list:
...

#Or filter with a generator

evens: List[int] = [k for k in m_list if k % 2 == 0 ]

```

If you want to something to happen just if a *Maybe* monad is defined
```python

for n in Just("one"):
...

```

The same apply for *Results*

3 changes: 3 additions & 0 deletions monads/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
from .applicative import Applicative
from .monad import Monad
from .list import List
from .set import Set
from .maybe import Maybe, Just, Nothing
from .result import Result, Ok, Err
from .future import Future
from .reader import Reader

version = "v0.0.15"
3 changes: 2 additions & 1 deletion monads/applicative.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ def pure(cls, value: T) -> Applicative[T]: # pragma: no cover
def apply(self, functor: Any) -> Functor[S]: # pragma: no cover
raise NotImplementedError

__and__ = lambda other, self: Applicative.apply(self, other)
def __and__(self, other: Any) -> Functor[S]: # pragma: no cover
return Applicative.apply(self, other)
7 changes: 5 additions & 2 deletions monads/future.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ def mcons(acc: Future[List[T]], x: Awaitable[T]) -> Future[List[T]]:
future: Future[T] = x if isinstance(x, Future) else Future(x)
return acc.bind(lambda acc_: future.map(lambda x_: acc_ + [x_]))

empty: Future[List[T]] = cls.pure([])
empty_list: List[T] = []
empty: Future[List[T]] = Future.pure(empty_list)
return functools.reduce(mcons, xs, empty)

def __await__(self):
return self.awaitable.__await__()

__rshift__ = bind
__and__ = lambda other, self: Future.apply(self, other)

__and__ = lambda other, self: Future.apply(self, other) # type: ignore

__mul__ = __rmul__ = map
67 changes: 57 additions & 10 deletions monads/list.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
from __future__ import annotations
from functools import reduce
from itertools import chain
from typing import Callable, Iterable, List as _List, TypeVar
from monads import functor
from typing import (
Callable,
Iterable,
Iterator,
List as _List,
Optional,
TypeVar,
Union,
cast,
)

from .monad import Monad
from .monoid import Monoidal
from .currying import CurriedBinary, uncurry

T = TypeVar("T")
S = TypeVar("S")
Expand All @@ -16,34 +27,70 @@ def pure(cls, value: T) -> List[T]:
return List([value])

def bind(self, function: Callable[[T], List[S]]) -> List[S]:
return reduce(List.mappend, map(function, self.value), List.mzero())
return reduce(List.mappend, map(function, self._value), List.mzero())

def map(self, function: Callable[[T], S]) -> List[S]:
return List(list(map(function, self.value)))
return List(list(map(function, self._value)))

def apply(self, functor: List[Callable[[T], S]]) -> List[S]:

return List(
list(chain.from_iterable([map(f, self.value) for f in functor.value]))
list(chain.from_iterable([map(f, self._value) for f in functor._value]))
)

@classmethod
def mzero(cls) -> List[T]:
return cls(list())

@classmethod
def sequence(cls, xs: Iterable[List[T]]) -> List[_List[T]]:
"""Evaluate monadic actions in sequence, collecting results."""

def mcons(acc: List[_List[T]], x: List[T]) -> List[_List[T]]:
return acc.bind(lambda acc_: x.map(lambda x_: acc_ + [x_]))

empty: List[_List[T]] = cls.pure([])
empty: List[_List[T]] = List.pure([])
return reduce(mcons, xs, empty)

@classmethod
def mzero(cls) -> List[T]:
return cls(list())
def flatten(self) -> List[T]:
def flat(acc: List[T], element: T) -> List[T]:
if element and isinstance(element, Iterable):
for k in element:
acc = acc.mappend(List([k]))
elif element:
acc = acc.mappend(List([element]))
return acc

return List(reduce(flat, self, List.mzero())) # type: ignore

def sort(self, key: Optional[str] = None, reverse: bool = False) -> List[T]:
lst_copy = self._value.copy()
lst_copy.sort(key=key, reverse=reverse) # type: ignore
return List(lst_copy)

def fold(
self, func: Union[Callable[[S, T], S], CurriedBinary[S, T, S]], base_val: S
) -> S:
if isinstance(func, CurriedBinary):
functor = uncurry(cast(CurriedBinary, func))
else:
functor = func
return reduce(functor, self._value, base_val) # type: ignore

__and__ = lambda other, self: List.apply(self, other) # type: ignore

def mappend(self, other: List[T]) -> List[T]:
return List(self.value + other.value)
return List(self._value + other._value)

__add__ = mappend
__and__ = lambda other, self: List.apply(self, other)
__mul__ = __rmul__ = map
__rshift__ = bind

def __sizeof__(self) -> int:
return self._value.__sizeof__()

def __len__(self) -> int:
return len(list(self._value))

def __iter__(self) -> Iterator[T]:
return iter(self._value)
Loading