Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Adds validate function #584

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

WIP: Adds validate function #584

wants to merge 1 commit into from

Conversation

sobolevn
Copy link
Member

@sobolevn sobolevn commented Sep 6, 2020

I am not going to merge this PR any time soon. This is just a place for discussion.

TODO:

  • Decide how we can change the final type of an object: Person -> Validated[Person] or Person -> ValidatedPerson
  • Decide what to do with container_type, because I don't like to specify it by hands. Maybe we can enforce this function to be def validate(steps: NonEmpty[Sequence[...]]) -> ...?
  • Decide how to join different validation steps together. There might be different strategies for it: run all, fail fast, etc
  • Decide should it be inside the main repo? Or a separate one?

Code to ry this out:

from typing import TYPE_CHECKING, Callable, Iterable, Sequence, TypeVar, cast

import attr

from returns._internal.pipeline.validation import validate
from returns.context import ReaderResult
from returns.io import IOResult
from returns.result import Result

if not TYPE_CHECKING:
    reveal_type = print


@attr.dataclass
class Person(object):
    fullname: str
    age: int
    passport: str


class ValidatedPerson(Person):
    ...


def validate_fullname(person: Person) -> Result[Person, str]:
    if not person.fullname:
        return Result.from_failure('No fullname specified')
    return Result.from_value(person)

def validate_age(person: Person) -> Result[Person, str]:
    if person.age < 0:
        return Result.from_failure('Negative age')
    return Result.from_value(person)

def validate_passport(person: Person) -> IOResult[Person, str]:
    """Impures, calls 3rd party API."""
    if not person.passport:  # this is not an IO action, just an example
        return IOResult.from_failure('Missing passort')
    return IOResult.from_value(person)

def validate_with_context(person: Person) -> ReaderResult[Person, str, int]:
    """Requires ``int`` context to actually validate anything."""
    def factory(deps: int) -> Result[Person, str]:
        if person.age < deps:
            return Result.from_failure('Less than minimal {0} age'.format(deps))
        return Result.from_value(person)
    return ReaderResult(factory)

person = Person('N', 28, '')

simple = validate(Result, [
    validate_fullname,
    validate_age,
])(person)

hard = validate(IOResult, [
    validate_passport,
])(person)

context = validate(ReaderResult, [
    validate_with_context
])(person)

reveal_type(simple)
reveal_type(hard)
reveal_type(context(35))

@thepabloaguilar thepabloaguilar linked an issue Sep 10, 2020 that may be closed by this pull request
@sobolevn
Copy link
Member Author

Related zio/zio-prelude#233

@sobolevn
Copy link
Member Author

@sobolevn
Copy link
Member Author

Related #258

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Consider adding validators package
1 participant