Skip to content

Commit

Permalink
New interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn committed Jun 19, 2019
1 parent c030d82 commit d73a043
Show file tree
Hide file tree
Showing 20 changed files with 279 additions and 195 deletions.
26 changes: 17 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ We follow Semantic Versions since the `0.1.0` release.

## WIP

### Features

- Provides a bunch of primitive interfaces to write your own containers

### Bugfixes

- Fixes type of `Maybe.fix` and `Maybe.rescue` to work with both `lambda: 1` and `lambda _: 1`

### Misc

- Improves `README`
Expand All @@ -14,7 +22,7 @@ We follow Semantic Versions since the `0.1.0` release.

### Features

- Reintroduces the `Maybe` monad, typed!
- Reintroduces the `Maybe` container, typed!
- Introduces converters from one type to another
- Adds `mypy` plugin to type decorators
- Complete rewrite of `Result` types
Expand Down Expand Up @@ -80,7 +88,7 @@ We follow Semantic Versions since the `0.1.0` release.
- Fixes copyright notice in the docs


## Version 0.4.0 aka Goodbye, Monads!
## Version 0.4.0 aka Goodbye, containers!

### Features

Expand All @@ -91,8 +99,8 @@ We follow Semantic Versions since the `0.1.0` release.
- Renames `do_notation` to `pipeline`, moves it to `functions.py`
- Renames `ebind` to `rescue`
- Renames `efmap` to `fix`
- Renames `Monad` to `Container`
- Removes `Maybe` monad, since typing does not have `NonNullable` type
- Renames `container` to `Container`
- Removes `Maybe` container, since typing does not have `NonNullable` type


## Version 0.3.1
Expand All @@ -116,16 +124,16 @@ The project is renamed to `returns` and moved to `dry-python` org.

### Features

- Adds `Maybe` monad
- Adds immutability and `__slots__` to all monads
- Adds `Maybe` container
- Adds immutability and `__slots__` to all containers
- Adds methods to work with failures
- Adds `safe` decorator to convert exceptions to `Result` monad
- Adds `safe` decorator to convert exceptions to `Result` container
- Adds `is_successful()` function to detect if your result is a success
- Adds `failure()` method to unwrap values from failed monads
- Adds `failure()` method to unwrap values from failed containers

### Bugfixes

- Changes the type of `.bind` method for `Success` monad
- Changes the type of `.bind` method for `Success` container
- Changes how equality works, so now `Failure(1) != Success(1)`
- Changes how new instances created on unused methods

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ pip install returns
```

You might also want to [configure](https://returns.readthedocs.io/en/latest/pages/container.html#type-safety)
`mypy` correctly and install our plugin:
`mypy` correctly and install our plugin
to fix [this existing issue](https://github.com/python/mypy/issues/3157):

```ini
# In setup.cfg or mypy.ini:
Expand Down
27 changes: 16 additions & 11 deletions docs/pages/container.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ List of supported containers:

- :class:`IO <returns.io.IO>` to mark explicit ``IO`` actions
- :class:`Result <returns.result.Result>` to handle possible exceptions
- :class:`Maybe <returns.maybe.Maybe>` to handle ``None`` cases

We will show you container's simple API of one attribute
and several simple methods.
Expand Down Expand Up @@ -49,7 +50,7 @@ The difference is simple:
- ``map`` works with functions that return regular value
- ``bind`` works with functions that return new container of the same type

:func:`.bind <returns.primitives.container.Container.bind>`
:func:`.bind <returns.primitives.container.Bindable.bind>`
is used to literally bind two different containers together.

.. code:: python
Expand All @@ -62,7 +63,7 @@ is used to literally bind two different containers together.
# Can be assumed as either Success[int] or Failure[str]:
result: Result[int, str] = Success(1).bind(may_fail)
And we have :func:`.map <returns.primitives.container.Container.map>`
And we have :func:`.map <returns.primitives.container.Mappable.map>`
to use containers with regular functions.

.. code:: python
Expand Down Expand Up @@ -139,10 +140,10 @@ Returning execution to the right track
We also support two special methods to work with "failed"
types like ``Failure``:

- :func:`.fix <returns.primitives.container.FixableContainer.fix>`
- :func:`.fix <returns.primitives.container.Fixable.fix>`
is the opposite of ``map`` method
that works only when container is in failed state
- :func:`.rescue <returns.primitives.container.FixableContainer.rescue>`
- :func:`.rescue <returns.primitives.container.Rescueable.rescue>`
is the opposite of ``bind`` method
that works only when container is in failed state

Expand Down Expand Up @@ -193,19 +194,20 @@ Unwrapping values
And we have two more functions to unwrap
inner state of containers into a regular types:

- :func:`.value_or <returns.primitives.container.ValueUnwrapContainer.value_or>`
- :func:`.value_or <returns.primitives.container.Unwrapable.value_or>`
returns a value if it is possible, returns ``default_value`` otherwise
- :func:`.unwrap <returns.primitives.container.ValueUnwrapContainer.unwrap>`
- :func:`.unwrap <returns.primitives.container.Unwrapable.unwrap>`
returns a value if it is possible, raises ``UnwrapFailedError`` otherwise

.. code:: python
from returns.result import Failure, Success
from returns.maybe import Some, Nothing
Success(1).value_or(None)
# => 1
Success(0).unwrap()
Some(0).unwrap()
# => 0
Failure(1).value_or(default_value=100)
Expand All @@ -214,11 +216,14 @@ inner state of containers into a regular types:
Failure(1).unwrap()
# => Traceback (most recent call last): UnwrapFailedError
The most user-friendly way to use ``unwrap`` method is with :ref:`pipeline`.
Nothing.unwrap()
# => Traceback (most recent call last): UnwrapFailedError
The most user-friendly way to use ``.unwrap()`` method is with :ref:`pipeline`.
We even discourage using ``.unwrap()`` without a ``@pipeline``.

For failing containers you can
use :func:`.failure <returns.primitives.container.FixableContainer.failure>`
use :func:`.failure <returns.primitives.container.Unwrapable.failure>`
to unwrap the failed state:

.. code:: python
Expand All @@ -230,7 +235,7 @@ to unwrap the failed state:
# => Traceback (most recent call last): UnwrapFailedError
Be careful, since this method will raise an exception
when you try to ``failure`` a successful container.
when you try to ``.failure()`` a successful container.

Note::

Expand Down Expand Up @@ -307,7 +312,7 @@ You can use :ref:`converters` to convert ``Maybe`` and ``Result`` containers.
So, you don't have to compose them.


.. converters_:
.. _converters:

Converters
----------
Expand Down
9 changes: 5 additions & 4 deletions docs/pages/maybe.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ It might be very useful for complex operations like the following one:
order: Order # some existing Order instance
street: Maybe[str] = Maybe.new(order.user).map(
lambda user: user.address,
lambda user: user.address,
).map(
lambda address: address.street,
lambda address: address.street,
)
# => `Some('address street info')` if all fields are not None
# => `Nothing` if at least one field is `None`
Expand Down Expand Up @@ -81,7 +81,8 @@ when new logic will be introduced.
Sometimes we have to deal with functions
that dears to return ``Optional`` values!

We have to work with the carefully and write ``if x is not None:`` everywhere.
We have to work with it the carefully
and write ``if x is not None:`` everywhere.
Luckily, we have your back! ``maybe`` function decorates
any other function that returns ``Optional``
and converts it to return ``Maybe`` instead:
Expand All @@ -98,7 +99,7 @@ and converts it to return ``Maybe`` instead:
return None
result: Maybe[int] = number(1)
# => 1
# => Some(1)
API Reference
Expand Down
20 changes: 10 additions & 10 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions returns/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
See: https://github.com/dry-python/returns/issues/73
"""

from returns.converters import maybe_to_result, result_to_maybe
from returns.functions import compose, raise_exception
from returns.io import IO, impure
from returns.maybe import Maybe, Nothing, Some, maybe
Expand Down Expand Up @@ -45,4 +46,8 @@
# pipeline:
'is_successful',
'pipeline',

# Converters:
'result_to_maybe',
'maybe_to_result',
)
8 changes: 4 additions & 4 deletions returns/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

from functools import wraps
from inspect import iscoroutinefunction
from typing import Callable, Coroutine, TypeVar, overload
from typing import Callable, Coroutine, Generic, TypeVar, overload

from typing_extensions import final

from returns.primitives.container import Container, GenericContainerOneSlot
from returns.primitives.container import BaseContainer

_ValueType = TypeVar('_ValueType')
_NewValueType = TypeVar('_NewValueType')
Expand All @@ -17,7 +17,7 @@


@final
class IO(GenericContainerOneSlot[_ValueType]):
class IO(Generic[_ValueType], BaseContainer):
"""
Explicit marker for impure function results.
Expand All @@ -33,7 +33,7 @@ class IO(GenericContainerOneSlot[_ValueType]):

def __init__(self, inner_value: _ValueType) -> None:
"""Required for typing."""
Container.__init__(self, inner_value) # type: ignore # noqa: Z462
BaseContainer.__init__(self, inner_value) # type: ignore # noqa: Z462

def map( # noqa: A003
self,
Expand Down
Loading

0 comments on commit d73a043

Please sign in to comment.