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

st.functions() returning consistent (or pure) functions #2538

Closed
sobolevn opened this issue Aug 9, 2020 · 4 comments · Fixed by #2541
Closed

st.functions() returning consistent (or pure) functions #2538

sobolevn opened this issue Aug 9, 2020 · 4 comments · Fixed by #2541
Labels
enhancement it's not broken, but we want it to be better

Comments

@sobolevn
Copy link
Member

sobolevn commented Aug 9, 2020

Currently, st.functions() creates functions that are not-pure (in a sence that they don't return the same value for the same arguments).

My use-case is that I am testing monad laws (see dry-python/returns#533 and dry-python/returns#543 for more information). Here's one of the definitions:

def associativity_law(
    mappable: MappableN[_FirstType, _SecondType, _ThirdType],
    first: Callable[[_FirstType], _NewType1],
    second: Callable[[_NewType1], _NewType2],
) -> None:
    assert mappable.map(first).map(second) == mappable.map(
        compose(first, second),
    )

And it never passes! Because each time first(x) and second(y) returns a different result.

My work-around for now is this custom strategy:

_func_template = """
lambda {0}: ...
"""

def functions(data: st.DataObject):
        def factory(typ):
            *arg_types, return_type = typ.__args__
            return_value = data.draw(st.from_type(return_type))
            args = []
            for i, arg in enumerate(arg_types):
                args.append('arg{0}'.format(i + 1))

            func = eval(_func_template.format(
                ', '.join(args),
                # return_type.__name__,
            ))
            
            return st.functions(
                like=func,
                returns=st.just(return_value),
            )
        return factory

And then I register this callback to handle Callable, like so: st.register_type_strategy(Callable, custom_strategies.functions(data))

So, is there any chance to see pure functions in the core of st.functions()?

@sobolevn sobolevn changed the title st.functions() returning consistent functions st.functions() returning consistent (or pure) functions Aug 9, 2020
@Zac-HD
Copy link
Member

Zac-HD commented Aug 10, 2020

Generating pure functions is a little tricky - I think in general we could only support it based on hashing the arguments, and mutable return values wouldn't be regenerated. If you think it's useful though, there's a demo at master...Zac-HD:pure-functions and it seems to work. Needs much more testing before it could be merged though 😅

@Zac-HD Zac-HD added the enhancement it's not broken, but we want it to be better label Aug 10, 2020
@sobolevn
Copy link
Member Author

sobolevn commented Aug 10, 2020

@Zac-HD is there anything I can help you with?
I guess returns is a great place to test pure function generation!

P.S. does my solution look viable for now?

@Zac-HD
Copy link
Member

Zac-HD commented Aug 10, 2020

Actually, yes - if you want to copy that branch and add tests + release notes, I'd be happy to merge it. All the tests can go in hypothesis-python/tests/cover/test_functions.py:

  • raises InvalidArgument if pure is not a bool
  • with pure=True, identical arguments -> same returned value
  • can generate non-identical return value for non-identical arguments
  • different generated functions like the same base function can generate separate return values for same args
  • passing kwargs in different orders, or an arg positionally vs by keyword, does not affect the caching (unlike lru_cache())

Your workaround looks OK to me, with the caveats that I think it fails for Callable or Callable[..., X] on some Python versions (maybe only 3.5?); and that it only generates a single example ever rather than maybe varying if arguments vary.

@sobolevn
Copy link
Member Author

I am on it! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement it's not broken, but we want it to be better
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants