I really really like the generic guard concept... and the proposed syntax (no matter for the moment if we use a colon an arrow or any other symbol to do this). I think it's a very elegant way to formalise adaptation and static restrictions that we want for a function.
Some problems still keep in my mind:
1) how could we express a very simple post condition like this one: "result of function foo must be positive"?
Here is the solution with decorator syntax of precondition (any other postcondition syntax would fit).
@postcondition(lambda result : result > 0)
def foo(arg1, arg2):
#foo code here
With the guard syntax we have a problem: the result of the function as no name and can not be tested except if we use a new reserved token "result":
def foo(arg1, arg2) -> result > 0:
#foo code here
2) How should we express a predicate that do not apply to one peticular parameter but to many of them or to data that are not parameters (attribute of the object itself for exemple). It's difficult to do this and keep the declaration static....
#is it good to do so ?
def foo1(arg1: arg1 > arg2, arg2):
#foo code here
#...or so maybe ?
def foo2(arg1, arg2: arg1 > arg2):
#foo code here
#...and so ?
def foo3(arg1, arg2: self.amout > 0):
#foo code here
3) If we use many predicate for one argument the syntax could be quite heavy... but it is still possible to express it in a multiline way or put many predicates in one external function.
#raw syntax with many predicates
def foo(arg1: numeric: arg1 > 0, arg2: numeric: arg2 < 0):
#foo code here
#already defined predicates
def predicate1(arg):
arg = adapt(arg, numeric)
arg = adapt(arg, arg > 0)
return arg
def predicate2(arg):
arg = adapt(arg, numeric)
arg = adapt(arg, arg < 0)
return arg
def foo(arg1: predicate1, arg2: predicate2):
#foo code here
4) I think we should not allow to have a type as second param to the adapt() function so that we do not allow static typing (what for me is not a good thing for python). But interface should be allowed (once again interfaces are not types but only behaviour definitions !!!!!).
But do not think I do not like your idea... I found it really elegant. If one can find solution for the 2 first problems (the 3rd is more a personnal problem about too long lines ^_^ ).
But once again here what I think could be a good road map to that feature because I do not like the idea to directly add a new syntax element with no wide usage/testing before:
1) Add a good adapt() function/mecanism to the library (it should support the "adapt(a, True)" syntax).
def foo(arg1, arg2):
arg1 = adapts(arg1, numeric)
arg1 = adapts(arg1, arg1 > 0)
arg2 = adapts(arg2, numeric)
arg2 = adapts(arg2, arg2 < 0)
#foo code here
2) Add a standard decorator to enable the use of guards for function using adaptation.
@adapts(arg1, numeric)
@adapts(arg1, lambda arg1: arg1 > 0)
@adapts(arg2, numeric)
@adapts(arg2, lambda arg2: arg2 < 0)
def foo(arg1, arg2):
#foo code here
3) Add a new syntax to have an heavyweight way to express such guards using a new operator(I choose the arrow syntax to prevent confusion with) :
def foo(arg1 -> numeric -> arg1 > 0, \
arg2 -> numeric -> arg2 < 0):
#foo code here
4) (maybe it could happens in the same time as point 3) Extends the new syntax so that the operator could be use anywhere in the code.
def foo(arg1 -> numeric -> arg1 > 0, \
arg2 -> numeric -> arg2 < 0):
#some code
for item in items:
shape = item -> Shape #equivalent to shape = adapts(item, Shape)
#some other code