returns icon indicating copy to clipboard operation
returns copied to clipboard

WIP: Adds validate function

Open sobolevn opened this issue 5 years ago • 3 comments

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))

sobolevn avatar Sep 06 '20 10:09 sobolevn

Related https://github.com/zio/zio-prelude/pull/233

sobolevn avatar Sep 13 '20 11:09 sobolevn

Related https://blog.drewolson.org/declarative-validation

sobolevn avatar Apr 28 '21 16:04 sobolevn

Related #258

sobolevn avatar Apr 28 '21 16:04 sobolevn