AndreaCensi / contracts

PyContracts is a Python package that allows to declare constraints on function parameters and return values. Contracts can be specified using Python3 annotations, or inside a docstring. PyContracts supports a basic type system, variables binding, arithmetic constraints, and has several specialized contracts and an extension API.
http://andreacensi.github.io/contracts/
Other
398 stars 62 forks source link

Built in support for higher order functions contracts? #10

Open piotrklibert opened 11 years ago

piotrklibert commented 11 years ago

Hi Andrea!

I only quickly skimmed through the docs, so I might missed something, but it seems that it's not possible to (out of the box) declare a contract on the function that takes function as an argument and returns another function. I'm dealing with a lot of functional-style code lately and I thought that this could be very handy in certain situations. I'm guessing it's possible to add support for this via custom contracts easily (haven't tried yet, but I'm going to) and what I'd like to know is if has a chance of becoming built in if implemented. (For example of this feature check here: http://docs.racket-lang.org/guide/contract-func.html#(part._.Contracts_on_.Higher-order_.Functions) )

Also, in some contracts library for CoffeeScript (which wasn't actually working IIRC :)) I saw an idea of embedding object's invariants into its methods' contracts. This should be easy enough, given the fact that self is explicitly passed in Python, but it would require having a syntax for making contracts for objects' attributes, which I didn't see built in either? I think I'll be exploring some possibilities in the course of next few weeks, I just wanted to know what do you, as an author and maintainer, think about these ideas.

Best regards, Piotr Klibert

AndreaCensi commented 11 years ago

Hi Piotr,

In general I welcome any improvements that make the core code still maintainable --- as you can imagine wrapping an arbitrary function and checking its arguments is kind of delicate, because there are many corner cases...

1) Regarding "declare a contract on the function that takes function as an argument and returns another function": Just to see if we are on the same page, could you write some use-case code for what you are suggesting?

Clearly PyContracts has the limitation that the checks are executed at call time, so it's not possible to do static analysis with higher-order types (if I understand what you are suggesting).

For example, one thing that I wanted to do was to add checks on values returned by iterators

@contract(it='iterator(int)')
def f(it):
   for ob in it:
      print ob + 1

but to do this, one would need to wrap around the iterator, which means that PyContracts' action would not be limited to the beginning and end of the function, but would be mixed with the user's code.

2) Regarding invariants: You are right that the second feature you mention needs an extension of PyContracts' syntax; currently, all contracts are attached either to arguments or return values; so they don't have the semantics of invariants.

Actually establishing invariants seems to be a feature which is really orthogonal to PyContracts' core, so I wonder if it should be implemented in a separate library, or at least with a separate decorator, different from @contract.

I'm open to integrate this feature in PyContracts, just to make it a one-stop destination for all checking needs.

Also here example use-case code would be useful to see what you imagine...

AndreaCensi commented 11 years ago

Answering here regarding invariants: I still think that it would go beyond PyContracts' scope, which I'm trying to keep manageable. It is also true that I'm not really used to using invariants in my code, so I wouldn't be confident in getting the design right. Contributions accepted, but perhaps we need a PyInvariants built alongside (or on top) of PyContracts.

AndreaCensi commented 9 years ago

After thinking about it for a while, I still think that conditions on higher-order functions are (1) slightly out of scope of the library, and (2) they could be implemented, but require a major effort. I'm leaving this open for the future (when somebody starts to think of PyContracts2).