Git Product home page Git Product logo

returns's People

Contributors

0xflotus avatar ariebovenberg avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar ducdetronquito avatar github-actions[bot] avatar hiyorimi avatar homeworkprod avatar johnthagen avatar kenjihiraoka avatar maybe-hello-world avatar novikovfred avatar nsidnev avatar orsinium avatar paveldroo avatar q0w avatar qdegraaf avatar ryangrose avatar sabinu avatar sam-persoon avatar seancrwhite avatar sobolevn avatar thedrow avatar thepabloaguilar avatar timgates42 avatar timqsh avatar topiaruss avatar vchernetskyi993 avatar williamjamir 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

returns's Issues

current init breaks `import returns` usecase

(crypy-PletnYfd)alexv@pop-os:~/Projects/crypy$ python3
Python 3.7.2 (default, Jan  8 2019, 16:12:09) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import returns
>>> returns.result
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'returns' has no attribute 'result'
>>> returns.__file__
'/home/alexv/.local/share/virtualenvs/crypy-PletnYfd/lib/python3.7/site-packages/returns/__init__.py'
>>> returns.__version__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'returns' has no attribute '__version__'

I believe __init__.py should contain the modules or not be there at all, for both usecases to work.
The usage given in example from returns.result import Result works because python import finds result.py as part of the package, but the module is not loaded as part of the super/parent package.

Dependabot can't evaluate your Python dependency files

Dependabot can't evaluate your Python dependency files.

As a result, Dependabot couldn't check whether any of your dependencies are out-of-date.

The error Dependabot encountered was:

Illformed requirement ["==3.7.2."]

You can mention @dependabot in the comments below to contact the Dependabot team.

is_successful typecheck fails in 0.7.0

Hi,
I faced the following issue in 0.7.0: mypy gives an error when is_successful is given a Result type. Given the following code (adapted from the docs) in main.py using 0.7.0

from returns.result import safe, is_successful

@safe
def divide(number: int) -> float:
    return number / number

is_successful(divide(1))

mypy main.py (mypy 0.701) fails with

main.py:7: error: No overload variant of "is_successful" matches argument type "Result[float, Exception]"
main.py:7: note: Possible overload variants:
main.py:7: note:     def is_successful(container: Success[Any]) -> Literal[True]
main.py:7: note:     def is_successful(container: Failure[Any]) -> Literal[False]

The typecheck passes with 0.6.0 (importing the functions from returns.functions rather than returns.result) and with the is_successful examples in the docs where it's given a known Success or Failure. The type error aside, the function works.
Thanks for your work on the project.

Rename `.bind()` to `.then()`

bind causes too many associations with monads. Which is not a good thing.
But, bind is also not so understandable. then is just like in Promise. So, it is way more readable.

Add docs about errors in enum

It is a common practice to put possible errors in enum (or Sum Type).

So, we can later use them like so:

from enum import Enum, unique

@unique
class APIExceptions(Enum):
      _value: Exception 
      ServerTimeoutError = exceptions.ServerTimeoutError
      OtherError = exceptions.OtherError

def call_api() -> Result[int, APIExceptions]: ... 

We can also try Union and not enum.

Many types are incorrect

After a review done by @astynax it was revealed that many types are incorrect.

We have privately discussed all the problems and solutions.
Here's the short version:

  1. We should only deal with Result itself, Success and Failure should return Result instances
  2. We should not work with Success and Failure directly, since there's no such thing, only Result
  3. Result functions should always return Result changing only the type signature

Btw, @astynax is now a core-dev of returns library. Congrats ๐ŸŽ‰

Consider adding IO monad

It might be a good idea to promote https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell idea in IO[] type.

Let's see an example:

from returns.io import IO, impure

@impure
def get_username() -> str:
     return input('Hi, what is your name?')

reveal_type(get_username())  # => IO[str]
get_username().split('@')  # nope! type error!
get_username().map(lambda name: name.split('@'))  # => IO[List[str]]

It is important to restrict any access to the internal state of this object.

Consider adding execution flow tracking mechanism for debugging

We can add a special value that will track the execution of the following snippet:

tracked: Tracked[int]

Success(tracked)
   .map(lambda x: x * 2)
   .fix(lambda x: -x)
   .bind(lambda x: Success(x) if x > 10 else Failure(x))
   .fix(lambda x: abs(x))

It is hard to tell from the start. But, we can use Writer monad for that. And it will allow us to build nice tracebacks.

We can sync the format with the one stories output. I would say that json is the best option for the output.

Try to move all the typing to `.pyi` files only

Current implementation causes a lot of confusion.
We have two identical type declaration in both source files and .pyi.
I will try to reduce it to just .pyi removing all types from the source code.

Typing should still be working for both local development and end users.

Rename classes and methods

Left -> Failure
Right -> Success
Either -> Result

fmap -> map
ebind -> rescue
efmap -> ? fix, repair

do_notation -> pipeline

Create "compose" function

We should provide a utility way to compose any amount of functions that works with single input and single output.

Make sure that we do not lose traceback with .unwrap()

When we do something like this:

class FetchUserProfile(object):
    """Single responsibility callable object that fetches user profile."""

    #: You can later use dependency injection to replace `requests`
    #: with any other http library (or even a custom service).
    _http = requests

    @pipeline
    def __call__(self, user_id: int) -> Result['UserProfile', Exception]:
        """Fetches UserProfile dict from foreign API."""
        response = self._make_request(user_id).unwrap()
        return self._parse_json(response)

    @safe
    def _make_request(self, user_id: int) -> requests.Response:
        response = self._http.get('/api/users/{0}'.format(user_id))
        response.raise_for_status()
        return response

    @safe
    def _parse_json(self, response: requests.Response) -> 'UserProfile':
        return response.json()

In this example self._make_request(user_id) may return Failure[Exception] and calling unwrap will create new exception, so we might lose the traceback from the original exception.

We need to be sure that it does not happen at all.

Give an example with exceptions

Rewrite the same code with exceptions and nulls:

class CreateAccountAndUser(object):
    """Creates new Account-User pair."""

    @do_notation
    def __call__(self, username: str, email: str) -> Result['User', str]:
        """Can return a Success(user) or Failure(str_reason)."""
        user_schema = self._validate_user(username, email).unwrap()
        account = self._create_account(user_schema).unwrap()
        return self._create_user(account)

We have a problem with @pipeline and unwrap()

from typing import TYPE_CHECKING
from returns import Failure, pipeline, Result

@pipeline
def test(x: int) -> Result[int, str]:
    res = Failure(bool).unwrap()
    return Failure('a')

if TYPE_CHECKING:
    reveal_type(test(1))
    # => Revealed type is 'returns.result.Result[builtins.int, builtins.str]'

print(test(1))
# => <Failure: <class 'bool'>>

I am pretty sure, that we will have to change how @pipeline works.
This is also related to #89

Think about `.contrib` package

We possibly can create a .contrib package with some popular use-case like:

  1. Result -> django request converter
  2. Result -> transaction rollback
  3. etc

Or maybe documentation will be just fine.

I invite @proofit404 to give some ideas.

Implement Maybe monad and custom mypy plugin

Currently it is impossible to use this monad due to this bug in typing:

x: Optional[int]
Monad.new(x)  # ?

Type will be: Some[Union[int, None]]
, not Union[Some[int], Nothing]
Related:

_ContainerType = TypeVar('_ContainerType', bound=Container)
_ValueType = TypeVar('_ValueType')
_NewValueType = TypeVar('_NewValueType')
class Maybe(GenericContainerOneSlot[_ValueType], metaclass=ABCMeta):
@overload
@classmethod
def new(cls, inner_value: Literal[None]) -> 'Nothing': # type: ignore
...
@overload # noqa: F811
@classmethod
def new(cls, inner_value: _ValueType) -> 'Some[_ValueType]':
...

Add new badge

[![Python Version](https://img.shields.io/pypi/pyversions/wemake-python-styleguide.svg)](https://pypi.org/project/wemake-python-styleguide/)
[![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/wemake-services/wemake-python-styleguide/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot)
[![wemake-python-styleguide](https://img.shields.io/badge/style-wemake-000000.svg)](https://github.com/wemake-services/wemake-python-styleguide)

Can be taken from here.

Unwrapping failures erases exception type information

Let's say I unwrap a failure and want to catch it using try-except.

The only way to do so currently is:

from returns.result import Result
from returns.functions import safe
from returns.primitives.exceptions import UnwrapFailedError

@safe
def foo(i) -> Result[int, Exception]:
  if i == 0:
    raise ValueError("problem")
  return i + 5

try:
  result = foo('a') # Will raise TypeError
  # do stuff
  result.unwrap()
except UnwrapFailedError as e:
  try:
    raise e.halted_container.failure() from e
  except ValueError:
    print("Don't use zero")
  except TypeError:
    print("Dont use strings")

This makes exception handling code more complex than it should be.

Naturally you could have also used:

@safe
def handle_err(e) -> Result[None, Exception]:
  if isinstance(e, ValueError):
    print("Don't use zero")
  elif isinstance(e, TypeError):
    print("Dont use strings")
  else:
    raise e from e

try:
  foo('a').rescue(handle_err).unwrap()
except UnwrapFailedError:
  print("Unhandled exception!!!")

But the error handling code above simply feels unpythonic and awkward.

Rescuing from an error fits the usecase where you want to log the error and proceed or any other generic error handling situation.

You could make rescue() accept a mapping between types to callables and call the appropriate error handler when an exception occured but I don't see why you'd have to do so.

It seems to me that the UnwrapFailedError exception is not necessary and hinders code readability.
The try-except clause was created for such situations. We should use it.

Dependabot can't evaluate your Python dependency files

Dependabot can't evaluate your Python dependency files.

As a result, Dependabot couldn't check whether any of your dependencies are out-of-date.

The error Dependabot encountered was:

Illformed requirement ["==3.7.2."]

You can mention @dependabot in the comments below to contact the Dependabot team.

Real life examples

Hello!

This library looks interesting as a "ROP" approach to exception handling. Can you provide more real-life examples with usage of rescue for example?

As i understand, currently rescue function is only usable for transforming exception message into some response, because when exception happens, Failure holds no context but exception instance.

My case:

task = get_next_task_from_db()

try:
    result = process_task(task)  # result is a dataclass with success: bool, payload: dict
except Exception as e:
    task.set_exception(e)
else:
    if result.success:
        task.set_complete(result.payload)
    else:
        task.set_failed(result.payload)

put_task_into_db(task)

I'm not sure of how to rewrite this code in a functional manner.

Implement `map_failure` method

We need to map failures to new failures without fixing them.
That's where map_failure() comes into play.

Scope:

  • Method itself (should be a part of FixableContainer definition
  • Types (in both primitives/container.pyi and result.pyi)
  • Docs (in both containers.rst and result.rst)

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.